Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Compose Dev Service #46848

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

ozangunalp
Copy link
Contributor

@ozangunalp ozangunalp commented Mar 17, 2025

The Compose Dev Service discovers Compose files in a Quarkus project and starts services defined in the Compose project, using Docker Compose or Podman Compose.

Detailed feature docs in compose-dev-services.adoc.


Extension dev services build steps can consume the DevServicesComposeProjectBuildItem and can locate whether compose has created an eligible service that they can configure the Quarkus app for, much like how dev service instances shared between projects.

This change already integrates following platform dev services: Datasource Dev Services (DB2, MariaDB, MS SQL, MySQL, Oracle, Postgres), Keycloak, Elasticsearch, Infinispan, Kafka, Kubernetes, AMQP, MQTT, Pulsar, RabbitMQ, MongoDB, Observability, Redis, Apicurio Schema Registry.

For any not-yet-integrated service, it is possible to include description in the compose files to produce runtime configuration for the Quarkus application in dev/test.

The dev service hashes targeted compose files in order to detect changes in the content and restart compose.

Dev UI Devservices pane shows a brief description of the Compose Dev Services.

If a Quarkus project doesn't contain Compose files, it can still discover Compose services using a configuration, effectively sharing a single compose project between multiple projects.

Copy link

quarkus-bot bot commented Mar 17, 2025

/cc @gsmet (elasticsearch), @loicmathieu (elasticsearch), @marko-bekhta (elasticsearch)

This comment has been minimized.

@geoand
Copy link
Contributor

geoand commented Mar 17, 2025

cc @iocanel @alesj

Copy link

github-actions bot commented Mar 17, 2025

🎊 PR Preview 21ab89f has been successfully built and deployed to https://quarkus-pr-main-46848-preview.surge.sh/version/main/guides/

  • Images of blog posts older than 3 months are not available.
  • Newsletters older than 3 months are not available.

This comment has been minimized.

@ozangunalp ozangunalp force-pushed the compose_dev_service branch from 5c8ef2f to 2dd25b2 Compare March 18, 2025 08:44

This comment has been minimized.

@ozangunalp ozangunalp force-pushed the compose_dev_service branch from 2dd25b2 to 3232191 Compare March 18, 2025 09:10

This comment has been minimized.

@ozangunalp
Copy link
Contributor Author

@geoand last commit contains a change to enable the use of the shared network with compose services and the container-based app test runner.
Combining compose services with regular dev services for this container-based run is still problematic. I think the best way is for dev service implementations to re-use the default network created by the compose, when it is available.

It also smooths already running compose app discovery scenarios and continuous-testing support. With the addition of start-containers flag and reuse-project-for-tests flags, it's easier to run docker compose up separately and keep it running. cf @tqvarnst (Would need some documentation on that)

This comment has been minimized.

This comment has been minimized.

@ozangunalp
Copy link
Contributor Author

I don't think that failing test integration-tests/container-image/maven-invoker-way is related.


1. A working *local* container environment: https://docs.docker.com/get-started/get-docker/[Docker] or https://podman.io/docs/installation[Podman]

2. Compose tooling such as `docker-compose` or `podman-compose`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

how does it pick which to use?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i.e. if i have both installed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It uses the container runtime detection we've in Quarkus. It is overridable via quarkus.native.container-runtime=podman|docker (maybe we need to remove the native from that property). I don't know if I mentioned it in the doc but podman uses docker-compose binary when available.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is overridable via quarkus.native.container-runtime=podman|docker (maybe we need to remove the native from that property)

At some point (not part of this PR) we need to unify this with quarkus.container-image.builder

Copy link
Member

@maxandersen maxandersen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this will be awesome :) added a few comments/question for clarficiation.


1. A working *local* container environment: https://docs.docker.com/get-started/get-docker/[Docker] or https://podman.io/docs/installation[Podman]

2. Compose tooling such as `docker-compose` or `podman-compose`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i.e. if i have both installed.

if (strategy != null) {
LOG.infov("Waiting for service {0} to be ready", serviceName);
try {
strategy.waitUntilReady(instance);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For information, when disabling ryuk and enabling reuse of tests

quarkus.compose.devservices.ryuk-enabled=true
quarkus.compose.devservices.reuse-project-for-tests=true

An error can be thrown because the listChildContainers also returns stopped containers and when the wait strategy is called on a stopped instance it throws an error.

For example:

            io.quarkus.builder.BuildException: Build failure: Build failed due to errors
                [error]: Build step io.quarkus.devservices.deployment.compose.ComposeDevServicesProcessor#config threw an exception: java.util.concurrent.CompletionException: java.lang.RuntimeException: java.lang.IllegalArgumentException: Requested port (5556) is not mapped
                at java.base/java.util.concurrent.CompletableFuture.encodeThrowable(CompletableFuture.java:315)
                at java.base/java.util.concurrent.CompletableFuture.completeThrowable(CompletableFuture.java:320)
                at java.base/java.util.concurrent.CompletableFuture$AsyncRun.run(CompletableFuture.java:1807)
                at org.jboss.threads.ContextHandler$1.runWith(ContextHandler.java:18)
                at org.jboss.threads.EnhancedQueueExecutor$Task.doRunWith(EnhancedQueueExecutor.java:2675)
                at org.jboss.threads.EnhancedQueueExecutor$Task.run(EnhancedQueueExecutor.java:2654)
                at org.jboss.threads.EnhancedQueueExecutor.runThreadBody(EnhancedQueueExecutor.java:1627)
                at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1594)
                at java.base/java.lang.Thread.run(Thread.java:1583)
                at org.jboss.threads.JBossThread.run(JBossThread.java:499)
            Caused by: java.lang.RuntimeException: java.lang.IllegalArgumentException: Requested port (5556) is not mapped
                at org.rnorth.ducttape.timeouts.Timeouts.callFuture(Timeouts.java:68)
                at org.rnorth.ducttape.timeouts.Timeouts.doWithTimeout(Timeouts.java:60)
                at org.testcontainers.containers.wait.strategy.WaitAllStrategy.waitUntilReady(WaitAllStrategy.java:54)
                at io.quarkus.devservices.deployment.compose.ComposeProject.waitUntilReady(ComposeProject.java:236)
                at io.quarkus.devservices.deployment.compose.ComposeProject.lambda$waitOnThread$7(ComposeProject.java:226)
                at java.base/java.util.concurrent.CompletableFuture$AsyncRun.run(CompletableFuture.java:1804)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's odd. Those config aren't that related. What are your steps to reproduce it?

If compose has an error starting services, like container created but not started etc. It leaves services in an intermediate state. I think we should capture errors on running compose up and run a compose down immediately and propagate the error so when the problem gets fixed we have the expected running state.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think a ran tests that started the compose services, then the containers stayed on because i disabled ryuk.

And after a while one of the container stopped and then the tests threw this error at each run until i removed the stopped container.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Normally dev services do compose down on shutdown. But during development, between changing the compose files and app restarts, things can get ugly and it can be in a state where compose up has run but didn't get registered for shutdown cleanup. ryuk is there to clean that up.

If a service container has stopped, the dev service is looking to validate that the container is healthy by checking mapped ports.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I created a reproducer here, https://github.com/Malandril/reproduce-compose you can run the ./reproduce-error.sh . If the listChildContainer is patched to only list running containers, the tests works and the service is started correctly.

diff --git i/extensions/devservices/deployment/src/main/java/io/quarkus/devservices/deployment/compose/ComposeProject.java w/extensions/devservices/deployment/src/main/java/io/quarkus/devservices/deployment/compose/ComposeProject.java
index 723cb3069b5..c6f14d45c78 100644
--- i/extensions/devservices/deployment/src/main/java/io/quarkus/devservices/deployment/compose/ComposeProject.java
+++ w/extensions/devservices/deployment/src/main/java/io/quarkus/devservices/deployment/compose/ComposeProject.java
@@ -299,6 +299,7 @@ private List<Container> listChildContainers() {
         return dockerClient
                 .listContainersCmd()
                 .withLabelFilter(Map.of(DOCKER_COMPOSE_PROJECT, project))
+                .withStatusFilter(List.of("running"))
                 .withShowAll(true)
                 .exec();

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the reproducer!
It may be interesting to capture running and restarting containers at this stage.
I remember testing without showAll(true), and it didn't work at some point, but I don't remember why and I changed quiet a few things since.

@ozangunalp ozangunalp force-pushed the compose_dev_service branch from 5c34b6b to 1d59196 Compare March 26, 2025 10:02
})
.or(() -> ComposeLocator.locateContainer(composeProjectBuildItem,
List.of(capturedDevServicesConfiguration.imageName()), LaunchMode.current())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ozangunalp so no need for additional label / tag? Image name is enough?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If observability service can be anything, it is better to specify the image-name directly or use the container locator label.

@geoand
Copy link
Contributor

geoand commented Mar 26, 2025

This will also need a rebase

@ozangunalp ozangunalp force-pushed the compose_dev_service branch from 1d59196 to 722062c Compare March 26, 2025 15:23
@ozangunalp
Copy link
Contributor Author

Rebased and reviewed the doc. I will squash once it's good to go.

This comment has been minimized.

.map(r -> {
Map<String, String> cfg = new LinkedHashMap<>();
for (ContainerInfo.ContainerPort port : r.containerInfo().exposedPorts()) {
cfg.putAll(dev.config(port.privatePort(),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@alesj could we use these properties to setup the extensions instead of relying on the internal system properties?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@brunobat I think we can make adjustments in later iterations..

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@brunobat wdym? On which internal system properties do we rely on?

Copy link
Contributor

@brunobat brunobat Mar 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the grafana.endpoint and otel-collector.url. I wonder if we can rid of them by using the container info

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@alesj @brunobat we could use the service-name attribute. if it would've been lgtm instead of quarkus.
Only keycloak and lgtm have quarkus as service name. I'd like to change that, if you're ok.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It makes sense

This comment has been minimized.

- Fix amqp username prop
- Introduced DevServicesNetworkIdBuildItem to set the dev services network id to the startup action, Regular dev services not reuse the compose default network when available
@ozangunalp ozangunalp force-pushed the compose_dev_service branch from 722062c to 74bbe72 Compare March 27, 2025 09:21
@ozangunalp
Copy link
Contributor Author

@Malandril thank you for testing this feature early.
I feel like this is stable enough to merge.
Please don't hesitate to come up with any other suggestions.

Copy link

quarkus-bot bot commented Mar 27, 2025

Status for workflow Quarkus Documentation CI

This is the status report for running Quarkus Documentation CI on commit 74bbe72.

✅ The latest workflow run for the pull request has completed successfully.

It should be safe to merge provided you have a look at the other checks in the summary.

Warning

There are other workflow runs running, you probably need to wait for their status before merging.

Copy link

quarkus-bot bot commented Mar 27, 2025

Status for workflow Quarkus CI

This is the status report for running Quarkus CI on commit 74bbe72.

✅ The latest workflow run for the pull request has completed successfully.

It should be safe to merge provided you have a look at the other checks in the summary.

You can consult the Develocity build scans.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

9 participants