-
Notifications
You must be signed in to change notification settings - Fork 2.6k
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
Add "how to write dev services" documentation #42535
Merged
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
123 changes: 123 additions & 0 deletions
123
docs/src/main/asciidoc/extension-writing-dev-service.adoc
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,123 @@ | ||
//// | ||
This document is maintained in the main Quarkus repository | ||
and pull requests should be submitted there: | ||
https://github.com/quarkusio/quarkus/tree/main/docs/src/main/asciidoc | ||
//// | ||
[id="extension-writing-dev-service"] | ||
= Writing a Dev Service | ||
include::_attributes.adoc[] | ||
:categories: writing-extensions | ||
:diataxis-type: howto | ||
:topics: extensions | ||
//// | ||
//// | ||
|
||
|
||
== Prerequisites | ||
|
||
- You should already have an xref:building-my-first-extension.adoc[extension structure] in place | ||
- You should have a containerised version of your external service (not all Dev Services rely on containers, but most do) | ||
|
||
== Creating a Dev Service | ||
|
||
If your extension provides APIs for connecting to an external service, it's a good idea to provide a xref:dev-services.adoc[Dev Service] implementation. | ||
|
||
To create a Dev Service, add a new build step into the extension processor class that returns a `DevServicesResultBuildItem`. | ||
Here, the link:https://hub.docker.com/_/hello-world`hello-world` image is used, but you should set up the right image for your service. | ||
|
||
[source%nowrap,java] | ||
---- | ||
@BuildStep(onlyIfNot = IsNormal.class, onlyIf = GlobalDevServicesConfig.Enabled.class) { | ||
public DevServicesResultBuildItem createContainer() { | ||
DockerImageName dockerImageName = DockerImageName.parse("hello-world"); | ||
GenericContainer container = new GenericContainer<>(dockerImageName) | ||
.withExposedPorts(SERVICE_PORT, OTHER_SERVICE_PORT) | ||
.waitingFor(Wait.forLogMessage(".*" + "Started" + ".*", 1)) | ||
.withReuse(true); | ||
container.start(); | ||
String newUrl = "http://" + container.getHost() + ":" + container.getMappedPort(SERVICE_PORT); | ||
Map<String, String> configOverrides = Map.of("some-service.base-url", newUrl); | ||
return new DevServicesResultBuildItem.RunningDevService(FEATURE, container.getContainerId(), | ||
container::close, configOverrides) | ||
.toBuildItem(); | ||
} | ||
---- | ||
|
||
With this code, you should be able to see your container starting if you add your extension to a test application and run `quarkus dev`. | ||
However, the application will not be able to connect to it, because no ports are exposed. To expose ports, add `withExposedPorts` to the container construction. | ||
For example, | ||
|
||
[source%nowrap,java] | ||
---- | ||
GenericContainer container = new GenericContainer<>(dockerImageName) | ||
.withExposedPorts(SERVICE_PORT, OTHER_SERVICE_PORT); | ||
---- | ||
|
||
Testcontainers will map these ports to random ports on the host. This avoids port conflicts, but presents a new problem – how do applications connect to the service in the container? | ||
|
||
To allow applications to connect, the extension should override the default configuration for the service with the mapped ports. | ||
This must be done after starting the container. | ||
For example, | ||
|
||
[source%nowrap,java] | ||
---- | ||
container.start(); | ||
Map<String, String> configOverrides = Map.of("some-service.base-url", | ||
"http://" + container.getHost() + ":" + container.getMappedPort(SERVICE_PORT)); | ||
---- | ||
|
||
Other configuration overrides may be included in the same map. | ||
|
||
== Waiting for the container to start | ||
|
||
You should add a `.waitingFor` call to the container construction, to wait for the container to start. For example | ||
|
||
[source%nowrap,java] | ||
---- | ||
.waitingFor(Wait.forLogMessage(".*" + "Started" + ".*", 1)) | ||
---- | ||
|
||
Waiting for a port to be open is another option. See the link:https://java.testcontainers.org/features/startup_and_waits/[Testcontainers documentation] for a full discussion of wait strategies. | ||
|
||
== Configuring the Dev Service | ||
|
||
To configure the Dev Service launch process, your build step can accept a `ConfigPhase.BUILD_TIME` config class in its constructor. | ||
For example, | ||
|
||
[source%nowrap,java] | ||
---- | ||
@BuildStep(onlyIfNot = IsNormal.class, onlyIf = GlobalDevServicesConfig.Enabled.class) { | ||
public DevServicesResultBuildItem createContainer(MyConfig config) { | ||
---- | ||
|
||
You may wish to use this config to set a fixed port, or set an image name, for example. | ||
|
||
[source%nowrap,java] | ||
---- | ||
if (config.port.isPresent()) { | ||
container.setPortBindings(List.of(config.port.get() + ":" + SERVICE_PORT)); | ||
} | ||
---- | ||
|
||
== Controlling re-use | ||
|
||
In dev mode, with live reload, Quarkus may restart frequently. By default, this will also restart test containers. | ||
Quarkus restarts are usually very fast, but containers may take much longer to restart. | ||
To prevent containers restarting on every code change, you can mark the container as reusable: | ||
|
||
[source%nowrap,java] | ||
---- | ||
.withReuse(true) | ||
---- | ||
|
||
Some Dev Services implement sophisticated reuse logic in which they track the state of the container in the processor itself. | ||
You may need this if your service has more complex requirements, or needs sharing across instances. | ||
|
||
|
||
== References | ||
|
||
- xref:dev-services.adoc[Dev services overview] | ||
- xref:writing-extensions.adoc[Guide to writing extensions] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unfortunately, this is not as simple when dealing with the shared network config. But this thing is complex and a bit in motion as I'm not very satisfied with what we have so probably better to keep it simple for now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have to say, I looked at a lot of our core dev services as examples and the complexity was a bit overwhelming. I had a hard time deciding where the right balance was between a naive 4-line dev service like the ones we live-code in workshops, and the 60-LoC monsters we have for a lot of our services. We don't want to give people instructions that don't work, but we also don't want to make people decide that writing a dev service is the last thing they want to attempt. :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I think we need to find a way to hide the complexity better.
I'm not entirely sure it's going to be easy but we really need to.