Repository: quarkusio/quarkus
Issue: ElasticSearch Dev Service image cannot be replaced with custom-built image

### Describe the bug

In the ElasticSearch DEV service you can use your own image (`quarkus.elasticsearch.devservices.image-name`), however it is not accepted because it is not **name-compatible** with the expected value: `docker.elastic.co/elasticsearch/elasticsearch`.

### Expected behavior

Any image that extends an ElasticSearch image is accepted and used.

### Actual behavior

```
ERROR [io.qua.dep.dev.IsolatedDevModeMain] (main) Failed to start quarkus: java.lang.RuntimeException: io.quarkus.builder.BuildException: Build failure: Build failed due to errors
        [error]: Build step io.quarkus.elasticsearch.restclient.common.deployment.DevServicesElasticsearchProcessor#startElasticsearchDevService threw an exception: java.lang.RuntimeException: java.lang.IllegalStateException: Failed to verify that image 'registry.gitlab.com/xxxxx/search-service/elastic-with-files:my-tag' is a compatible substitute for 'docker.elastic.co/elasticsearch/elasticsearch'. 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:
   DockerImageName myImage = DockerImageName.parse("registry.gitlab.com/xxxxx/search-service/elastic-with-files:my-tag").asCompatibleSubstituteFor("docker.elastic.co/elasticsearch/elasticsearch");
and then use `myImage` instead.
        at io.quarkus.elasticsearch.restclient.common.deployment.DevServicesElasticsearchProcessor.startElasticsearchDevService(DevServicesElasticsearchProcessor.java:98)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.base/java.lang.reflect.Method.invoke(Method.java:568)
        at io.quarkus.deployment.ExtensionLoader$3.execute(ExtensionLoader.java:977)
        at io.quarkus.builder.BuildContext.run(BuildContext.java:281)
        at org.jboss.threads.ContextHandler$1.runWith(ContextHandler.java:18)
        at org.jboss.threads.EnhancedQueueExecutor$Task.run(EnhancedQueueExecutor.java:2449)
        at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1478)
        at java.base/java.lang.Thread.run(Thread.java:833)
        at org.jboss.threads.JBossThread.run(JBossThread.java:501)
Caused by: java.lang.IllegalStateException: Failed to verify that image 'registry.gitlab.com/xxxxx/search-service/elastic-with-files:my-tag' is a compatible substitute for 'docker.elastic.co/elasticsearch/elasticsearch'. 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:
   DockerImageName myImage = DockerImageName.parse("registry.gitlab.com/xxxxx/search-service/elastic-with-files:my-tag").asCompatibleSubstituteFor("docker.elastic.co/elasticsearch/elasticsearch");
and then use `myImage` instead.
        at org.testcontainers.utility.DockerImageName.assertCompatibleWith(DockerImageName.java:270)
        at org.testcontainers.elasticsearch.ElasticsearchContainer.<init>(ElasticsearchContainer.java:95)
        at io.quarkus.elasticsearch.restclient.common.deployment.DevServicesElasticsearchProcessor.lambda$startElasticsearch$1(DevServicesElasticsearchProcessor.java:198)
        at java.base/java.util.Optional.orElseGet(Optional.java:364)
        at io.quarkus.elasticsearch.restclient.common.deployment.DevServicesElasticsearchProcessor.startElasticsearch(DevServicesElasticsearchProcessor.java:224)
        at io.quarkus.elasticsearch.restclient.common.deployment.DevServicesElasticsearchProcessor.startElasticsearchDevService(DevServicesElasticsearchProcessor.java:88)
        ... 11 more
```

### How to Reproduce?

mvn io.quarkus.platform:quarkus-maven-plugin:2.12.1.Final:create \\
          -DprojectGroupId=org.acme \\
          -DprojectArtifactId=elasticsearch-quickstart \\
          -DclassName="org.acme.datasource.GreetingResource" \\
          -Dextensions="resteasy,resteasy-jackson,elasticsearch-rest-client"

cd elasticsearch-quickstart/

echo "quarkus.elasticsearch.devservices.image-name = debian:buster" >> ./src/main/resources/application.properties

mvn verify

### Output of `uname -a` or `ver`

Linux ****** 5.4.72-microsoft-standard-WSL2 #1 SMP Wed Oct 28 23:40:43 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux

### Output of `java -version`

openjdk version "17.0.3" 2022-04-19 OpenJDK Runtime Environment (build 17.0.3+7-Ubuntu-0ubuntu0.20.04.1) OpenJDK 64-Bit Server VM (build 17.0.3+7-Ubuntu-0ubuntu0.20.04.1, mixed mode, sharing)

### GraalVM version (if different from Java)

_No response_

### Quarkus version or git rev

2.12.1.Final

### Build tool (ie. output of `mvnw --version` or `gradlew --version`)

Apache Maven 3.8.6 (84538c9988a25aec085021c365c560670ad80f63) Maven home: /opt/maven Java version: 17.0.3, vendor: Private Build, runtime: /usr/lib/jvm/java-17-openjdk-amd64 Default locale: en, platform encoding: UTF-8 OS name: "linux", version: "5.4.72-microsoft-standard-wsl2", arch: "amd64", family: "unix"

### Additional information

This bug has the identical problem as: https://github.com/quarkusio/quarkus/issues/20945

Changed files:
extensions/smallrye-reactive-messaging-amqp/deployment/src/main/java/io/quarkus/smallrye/reactivemessaging/amqp/deployment/AmqpDevServicesProcessor.java
extensions/schema-registry/devservice/deployment/src/main/java/io/quarkus/apicurio/registry/devservice/DevServicesApicurioRegistryProcessor.java
extensions/kafka-client/deployment/src/main/java/io/quarkus/kafka/client/deployment/DevServicesKafkaProcessor.java
extensions/smallrye-reactive-messaging-rabbitmq/deployment/src/main/java/io/quarkus/smallrye/reactivemessaging/rabbitmq/deployment/RabbitMQDevServicesProcessor.java
extensions/elasticsearch-rest-client-common/deployment/src/main/java/io/quarkus/elasticsearch/restclient/common/deployment/DevServicesElasticsearchProcessor.java

diff --git a/extensions/elasticsearch-rest-client-common/deployment/src/main/java/io/quarkus/elasticsearch/restclient/common/deployment/DevServicesElasticsearchProcessor.java b/extensions/elasticsearch-rest-client-common/deployment/src/main/java/io/quarkus/elasticsearch/restclient/common/deployment/DevServicesElasticsearchProcessor.java
index 44803e7d84c..fae32662211 100644
--- a/extensions/elasticsearch-rest-client-common/deployment/src/main/java/io/quarkus/elasticsearch/restclient/common/deployment/DevServicesElasticsearchProcessor.java
+++ b/extensions/elasticsearch-rest-client-common/deployment/src/main/java/io/quarkus/elasticsearch/restclient/common/deployment/DevServicesElasticsearchProcessor.java
@@ -176,7 +176,7 @@ private DevServicesResultBuildItem.RunningDevService startElasticsearch(
             throw new BuildException("Dev services for Elasticsearch didn't support Opensearch", Collections.emptyList());
         }
 
-        // Hibernate search Elasticsearch have a version configuration property, we need to check that it is coherent
+        // Hibernate Search Elasticsearch have a version configuration property, we need to check that it is coherent
         // with the image we are about to launch
         if (buildItemConfig.version != null) {
             String containerTag = config.imageName.substring(config.imageName.indexOf(':') + 1);
@@ -197,7 +197,7 @@ private DevServicesResultBuildItem.RunningDevService startElasticsearch(
         // Starting the server
         final Supplier<DevServicesResultBuildItem.RunningDevService> defaultElasticsearchSupplier = () -> {
             ElasticsearchContainer container = new ElasticsearchContainer(
-                    DockerImageName.parse(config.imageName));
+                    DockerImageName.parse(config.imageName).asCompatibleSubstituteFor("elasticsearch/elasticsearch"));
             ConfigureUtil.configureSharedNetwork(container, "elasticsearch");
             if (config.serviceName != null) {
                 container.withLabel(DEV_SERVICE_LABEL, config.serviceName);
diff --git a/extensions/kafka-client/deployment/src/main/java/io/quarkus/kafka/client/deployment/DevServicesKafkaProcessor.java b/extensions/kafka-client/deployment/src/main/java/io/quarkus/kafka/client/deployment/DevServicesKafkaProcessor.java
index 319ff7d521d..0bbe4d81122 100644
--- a/extensions/kafka-client/deployment/src/main/java/io/quarkus/kafka/client/deployment/DevServicesKafkaProcessor.java
+++ b/extensions/kafka-client/deployment/src/main/java/io/quarkus/kafka/client/deployment/DevServicesKafkaProcessor.java
@@ -246,7 +246,7 @@ private RunningDevService startKafka(DockerStatusBuildItem dockerStatusBuildItem
                         KAFKA_BOOTSTRAP_SERVERS, container.getBootstrapServers());
             } else {
                 RedPandaKafkaContainer container = new RedPandaKafkaContainer(
-                        DockerImageName.parse(config.imageName),
+                        DockerImageName.parse(config.imageName).asCompatibleSubstituteFor("vectorized/redpanda"),
                         config.fixedExposedPort,
                         launchMode.getLaunchMode() == LaunchMode.DEVELOPMENT ? config.serviceName : null,
                         useSharedNetwork, config.redpanda);
diff --git a/extensions/schema-registry/devservice/deployment/src/main/java/io/quarkus/apicurio/registry/devservice/DevServicesApicurioRegistryProcessor.java b/extensions/schema-registry/devservice/deployment/src/main/java/io/quarkus/apicurio/registry/devservice/DevServicesApicurioRegistryProcessor.java
index 79e659a5562..7698a26f355 100644
--- a/extensions/schema-registry/devservice/deployment/src/main/java/io/quarkus/apicurio/registry/devservice/DevServicesApicurioRegistryProcessor.java
+++ b/extensions/schema-registry/devservice/deployment/src/main/java/io/quarkus/apicurio/registry/devservice/DevServicesApicurioRegistryProcessor.java
@@ -173,7 +173,8 @@ private RunningDevService startApicurioRegistry(DockerStatusBuildItem dockerStat
                         getRegistryUrlConfigs("http://" + address.getUrl())))
                 .orElseGet(() -> {
                     ApicurioRegistryContainer container = new ApicurioRegistryContainer(
-                            DockerImageName.parse(config.imageName), config.fixedExposedPort,
+                            DockerImageName.parse(config.imageName).asCompatibleSubstituteFor("apicurio/apicurio-registry-mem"),
+                            config.fixedExposedPort,
                             launchMode.getLaunchMode() == LaunchMode.DEVELOPMENT ? config.serviceName : null,
                             useSharedNetwork);
                     timeout.ifPresent(container::withStartupTimeout);
diff --git a/extensions/smallrye-reactive-messaging-amqp/deployment/src/main/java/io/quarkus/smallrye/reactivemessaging/amqp/deployment/AmqpDevServicesProcessor.java b/extensions/smallrye-reactive-messaging-amqp/deployment/src/main/java/io/quarkus/smallrye/reactivemessaging/amqp/deployment/AmqpDevServicesProcessor.java
index d6c6fa0146a..99fdf9a906b 100644
--- a/extensions/smallrye-reactive-messaging-amqp/deployment/src/main/java/io/quarkus/smallrye/reactivemessaging/amqp/deployment/AmqpDevServicesProcessor.java
+++ b/extensions/smallrye-reactive-messaging-amqp/deployment/src/main/java/io/quarkus/smallrye/reactivemessaging/amqp/deployment/AmqpDevServicesProcessor.java
@@ -176,7 +176,7 @@ private RunningDevService startAmqpBroker(DockerStatusBuildItem dockerStatusBuil
         final Supplier<RunningDevService> defaultAmqpBrokerSupplier = () -> {
             // Starting the broker
             ArtemisContainer container = new ArtemisContainer(
-                    DockerImageName.parse(config.imageName),
+                    DockerImageName.parse(config.imageName).asCompatibleSubstituteFor("artemiscloud/activemq-artemis-broker"),
                     config.extra,
                     config.fixedExposedPort,
                     launchMode.getLaunchMode() == LaunchMode.DEVELOPMENT ? config.serviceName : null);
diff --git a/extensions/smallrye-reactive-messaging-rabbitmq/deployment/src/main/java/io/quarkus/smallrye/reactivemessaging/rabbitmq/deployment/RabbitMQDevServicesProcessor.java b/extensions/smallrye-reactive-messaging-rabbitmq/deployment/src/main/java/io/quarkus/smallrye/reactivemessaging/rabbitmq/deployment/RabbitMQDevServicesProcessor.java
index 85763f737ae..39d102ff575 100644
--- a/extensions/smallrye-reactive-messaging-rabbitmq/deployment/src/main/java/io/quarkus/smallrye/reactivemessaging/rabbitmq/deployment/RabbitMQDevServicesProcessor.java
+++ b/extensions/smallrye-reactive-messaging-rabbitmq/deployment/src/main/java/io/quarkus/smallrye/reactivemessaging/rabbitmq/deployment/RabbitMQDevServicesProcessor.java
@@ -176,7 +176,7 @@ private RunningDevService startRabbitMQBroker(DockerStatusBuildItem dockerStatus
         }
 
         ConfiguredRabbitMQContainer container = new ConfiguredRabbitMQContainer(
-                DockerImageName.parse(config.imageName),
+                DockerImageName.parse(config.imageName).asCompatibleSubstituteFor("rabbitmq"),
                 config.fixedExposedPort,
                 config.fixedExposedHttpPort,
                 launchMode.getLaunchMode() == LaunchMode.DEVELOPMENT ? config.serviceName : null);