From 8504ac36fe0f3540cf0804c4b5a7581872613ab1 Mon Sep 17 00:00:00 2001 From: Richard North Date: Thu, 6 May 2021 16:31:58 +0100 Subject: [PATCH 1/5] Use up-to-date MySQL docker images Fixes #4058 Latest versions of JDKs (including `adopt@1.11.0-11`) have disabled TLS1.0 and 1.1, which means that these JDKs can no longer use particularly old versions of the MySQL docker images. This PR makes Testcontainers' own tests use the most up-to-date images available (for each version of MySQL in use). The PR also updates the 'default' MySQL tag, since the previous version can no longer be used reliably. --- .../DockerClientProviderStrategy.java | 2 +- .../images/RemoteDockerImage.java | 30 +++++++++++++++---- 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 | 2 +- .../jdbc/ConnectionUrlDriversTests.java | 2 +- .../jdbc/ConnectionUrlTest.java | 10 +++---- .../jdbc/MissingJdbcDriverTest.java | 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 +- 23 files changed, 96 insertions(+), 75 deletions(-) diff --git a/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java b/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java index 35e3b6eabb8..a54adb4e59b 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_40); } 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..90594a87fd9 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.github.dockerjava.api.model.Info; import com.google.common.util.concurrent.Futures; import lombok.AccessLevel; import lombok.AllArgsConstructor; @@ -10,6 +11,7 @@ import lombok.SneakyThrows; import lombok.ToString; import lombok.experimental.Wither; +import org.apache.commons.lang.SystemUtils; import org.slf4j.Logger; import org.testcontainers.DockerClientFactory; import org.testcontainers.containers.ContainerFetchException; @@ -75,11 +77,7 @@ protected final String resolve() { while (Instant.now().isBefore(lastRetryAllowed)) { try { - dockerClient - .pullImageCmd(imageName.getUnversionedPart()) - .withTag(imageName.getVersionPart()) - .exec(new TimeLimitedLoggedPullImageResultCallback(logger)) - .awaitCompletion(); + pullImageWithEmulatedFallback(imageName, logger); LocalImagesCache.INSTANCE.refreshCache(imageName); @@ -100,6 +98,28 @@ protected final String resolve() { } } + private void pullImageWithEmulatedFallback(DockerImageName imageName, Logger logger) throws InterruptedException { + try { + dockerClient + .pullImageCmd(imageName.getUnversionedPart()) + .withTag(imageName.getVersionPart()) + .exec(new TimeLimitedLoggedPullImageResultCallback(logger)) + .awaitCompletion(); + } catch (DockerClientException e) { + Info info = dockerClient.infoCmd().exec(); + if ("aarch64".equals(info.getArchitecture())) { + dockerClient + .pullImageCmd(imageName.getUnversionedPart()) + .withTag(imageName.getVersionPart()) + .withPlatform("linux/amd64") + .exec(new TimeLimitedLoggedPullImageResultCallback(logger)) + .awaitCompletion(); + } else { + throw e; + } + } + } + private DockerImageName getImageName() throws InterruptedException, ExecutionException { final DockerImageName specifiedImageName = imageNameFuture.get(); 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..4a30c3f315f 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.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: 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..2edc7d18562 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,7 @@ protected DataSource getDataSource(JdbcDatabaseContainer container) { hikariConfig.setUsername(container.getUsername()); hikariConfig.setPassword(container.getPassword()); hikariConfig.setDriverClassName(container.getDriverClassName()); - + hikariConfig.addDataSourceProperty("enabledTLSProtocols", "TLSv1,TLSv1.1,TLSv1.2"); 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/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..e42f01b8a0c 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_57_IMAGE.getVersionPart(); } @Override protected MySQLContainer createContainer() { - return new MySQLContainer<>(MySQLTestImages.MYSQL_IMAGE); + return new MySQLContainer<>(MySQLTestImages.MYSQL_57_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 0872fd7e10bbbf717aa510d7b2b0623da9584d17 Mon Sep 17 00:00:00 2001 From: Richard North Date: Thu, 6 May 2021 16:34:29 +0100 Subject: [PATCH 2/5] Revert accidentally pushed changes --- .../DockerClientProviderStrategy.java | 2 +- .../images/RemoteDockerImage.java | 30 ++++--------------- 2 files changed, 6 insertions(+), 26 deletions(-) diff --git a/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java b/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java index a54adb4e59b..35e3b6eabb8 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_40); + configBuilder.withApiVersion(RemoteApiVersion.VERSION_1_30); } 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 90594a87fd9..f4b04712913 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.exception.DockerClientException; import com.github.dockerjava.api.exception.InternalServerErrorException; -import com.github.dockerjava.api.model.Info; import com.google.common.util.concurrent.Futures; import lombok.AccessLevel; import lombok.AllArgsConstructor; @@ -11,7 +10,6 @@ import lombok.SneakyThrows; import lombok.ToString; import lombok.experimental.Wither; -import org.apache.commons.lang.SystemUtils; import org.slf4j.Logger; import org.testcontainers.DockerClientFactory; import org.testcontainers.containers.ContainerFetchException; @@ -77,7 +75,11 @@ protected final String resolve() { while (Instant.now().isBefore(lastRetryAllowed)) { try { - pullImageWithEmulatedFallback(imageName, logger); + dockerClient + .pullImageCmd(imageName.getUnversionedPart()) + .withTag(imageName.getVersionPart()) + .exec(new TimeLimitedLoggedPullImageResultCallback(logger)) + .awaitCompletion(); LocalImagesCache.INSTANCE.refreshCache(imageName); @@ -98,28 +100,6 @@ protected final String resolve() { } } - private void pullImageWithEmulatedFallback(DockerImageName imageName, Logger logger) throws InterruptedException { - try { - dockerClient - .pullImageCmd(imageName.getUnversionedPart()) - .withTag(imageName.getVersionPart()) - .exec(new TimeLimitedLoggedPullImageResultCallback(logger)) - .awaitCompletion(); - } catch (DockerClientException e) { - Info info = dockerClient.infoCmd().exec(); - if ("aarch64".equals(info.getArchitecture())) { - dockerClient - .pullImageCmd(imageName.getUnversionedPart()) - .withTag(imageName.getVersionPart()) - .withPlatform("linux/amd64") - .exec(new TimeLimitedLoggedPullImageResultCallback(logger)) - .awaitCompletion(); - } else { - throw e; - } - } - } - private DockerImageName getImageName() throws InterruptedException, ExecutionException { final DockerImageName specifiedImageName = imageNameFuture.get(); From 10244b88e150a765c54a63b5299b248e367d30c9 Mon Sep 17 00:00:00 2001 From: Richard North Date: Fri, 7 May 2021 11:52:36 +0100 Subject: [PATCH 3/5] Use MySQL 8.0.x for R2DBC testing As workaround for https://github.com/mirromutth/r2dbc-mysql/issues/182 Also bring library versions up-to-date/in-sync so that exceptions are surfaced appropriately --- modules/mysql/build.gradle | 2 +- .../containers/MySQLR2DBCDatabaseContainerTest.java | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) 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/test/java/org/testcontainers/containers/MySQLR2DBCDatabaseContainerTest.java b/modules/mysql/src/test/java/org/testcontainers/containers/MySQLR2DBCDatabaseContainerTest.java index e42f01b8a0c..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=" + MySQLTestImages.MYSQL_57_IMAGE.getVersionPart(); + return "r2dbc:tc:mysql:///db?TC_IMAGE_TAG=" + MySQLTestImages.MYSQL_80_IMAGE.getVersionPart(); } @Override protected MySQLContainer createContainer() { - return new MySQLContainer<>(MySQLTestImages.MYSQL_57_IMAGE); + return new MySQLContainer<>(MySQLTestImages.MYSQL_80_IMAGE); } } From 104266d45670c307b124a9789abea436b971b564 Mon Sep 17 00:00:00 2001 From: Richard North Date: Fri, 7 May 2021 12:21:42 +0100 Subject: [PATCH 4/5] Remove unnecessary config change in tests --- .../org/testcontainers/db/AbstractContainerDatabaseTest.java | 1 - 1 file changed, 1 deletion(-) 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 2edc7d18562..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()); - hikariConfig.addDataSourceProperty("enabledTLSProtocols", "TLSv1,TLSv1.1,TLSv1.2"); return new HikariDataSource(hikariConfig); } } From b77b0aaa0c61bc8aa7f0092aab130db1700e3d26 Mon Sep 17 00:00:00 2001 From: Richard North Date: Fri, 7 May 2021 12:54:55 +0100 Subject: [PATCH 5/5] Update docs/features/image_name_substitution.md Co-authored-by: Sergei Egorov --- docs/features/image_name_substitution.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/features/image_name_substitution.md b/docs/features/image_name_substitution.md index 4a30c3f315f..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.24` 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: