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));