Skip to content

Commit ea65f3e

Browse files
committed
add devservice
1 parent ec0e781 commit ea65f3e

File tree

59 files changed

+1481
-146
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

59 files changed

+1481
-146
lines changed

docs/modules/ROOT/pages/includes/quarkus-temporal.adoc

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@ a|icon:lock[title=Fixed at build time] [[quarkus-temporal_quarkus-temporal-enabl
1515

1616
[.description]
1717
--
18-
enable mock for testing
18+
enable mock for testing.
19+
20+
If enabled, the Temporal devservice will not be started.
1921

2022
ifdef::add-copy-button-to-env-var[]
2123
Environment variable: env_var_with_copy_button:+++QUARKUS_TEMPORAL_ENABLE_MOCK+++[]
@@ -717,7 +719,7 @@ a| [[quarkus-temporal_quarkus-temporal-connection-target]]`link:#quarkus-tempora
717719

718720
[.description]
719721
--
720-
Sets a target string, which can be either a valid `NameResolver`-compliant URI, or an authority string. See `ManagedChannelBuilder++#++forTarget(String)` for more information about parameter format. Default is 127.0.0.1:7233
722+
Sets a target string, which can be either a valid `NameResolver`-compliant URI, or an authority string. See `ManagedChannelBuilder++#++forTarget(String)` for more information about parameter format.
721723

722724
ifdef::add-copy-button-to-env-var[]
723725
Environment variable: env_var_with_copy_button:+++QUARKUS_TEMPORAL_CONNECTION_TARGET+++[]
@@ -726,7 +728,7 @@ ifndef::add-copy-button-to-env-var[]
726728
Environment variable: `+++QUARKUS_TEMPORAL_CONNECTION_TARGET+++`
727729
endif::add-copy-button-to-env-var[]
728730
--|string
729-
|`127.0.0.1:7233`
731+
|required icon:exclamation-circle[title=Configuration property is required]
730732

731733

732734
a| [[quarkus-temporal_quarkus-temporal-connection-enable-https]]`link:#quarkus-temporal_quarkus-temporal-connection-enable-https[quarkus.temporal.connection.enable-https]`

extension/deployment/pom.xml

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,29 @@
1919
</dependency>
2020
<dependency>
2121
<groupId>io.quarkus</groupId>
22-
<artifactId>quarkus-grpc-common-deployment</artifactId>
22+
<artifactId>quarkus-grpc-deployment</artifactId>
2323
</dependency>
2424
<dependency>
2525
<groupId>io.quarkus</groupId>
26-
<artifactId>quarkus-grpc-deployment</artifactId>
26+
<artifactId>quarkus-smallrye-health-spi</artifactId>
2727
</dependency>
2828
<dependency>
2929
<groupId>io.quarkus</groupId>
30-
<artifactId>quarkus-smallrye-health-spi</artifactId>
30+
<artifactId>quarkus-devservices-deployment</artifactId>
31+
</dependency>
32+
<dependency>
33+
<groupId>io.quarkus</groupId>
34+
<artifactId>quarkus-vertx-http-deployment</artifactId>
35+
</dependency>
36+
<dependency>
37+
<groupId>org.testcontainers</groupId>
38+
<artifactId>testcontainers</artifactId>
39+
<exclusions>
40+
<exclusion>
41+
<groupId>junit</groupId>
42+
<artifactId>junit</artifactId>
43+
</exclusion>
44+
</exclusions>
3145
</dependency>
3246
<dependency>
3347
<groupId>io.quarkiverse.temporal</groupId>

extension/deployment/src/main/java/io/quarkiverse/temporal/deployment/TemporalProcessor.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,8 @@ public class TemporalProcessor {
9292

9393
public static final DotName ACTIVITY_INTERFACE = DotName.createSimple(ActivityInterface.class);
9494

95-
private static final String FEATURE = "temporal";
95+
public static final String FEATURE = "temporal";
96+
public static final DotName CONTEXT_PROPAGATOR = DotName.createSimple(ContextPropagator.class);
9697

9798
@BuildStep
9899
FeatureBuildItem feature() {
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package io.quarkiverse.temporal.deployment.devui;
2+
3+
import org.testcontainers.containers.GenericContainer;
4+
import org.testcontainers.utility.DockerImageName;
5+
6+
public class TemporalContainer extends GenericContainer<TemporalContainer> {
7+
8+
private static final Integer SERVER_EXPOSED_PORT = 7233;
9+
private static final Integer UI_EXPOSED_PORT = 8233;
10+
private final String path;
11+
12+
public TemporalContainer(DockerImageName dockerImageName, String path, Boolean reuse) {
13+
super(dockerImageName);
14+
this.path = path;
15+
16+
withCreateContainerCmdModifier(cmd -> {
17+
cmd.withEntrypoint("/usr/local/bin/temporal");
18+
cmd.withCmd(
19+
"server", "start-dev",
20+
"--ip", "0.0.0.0",
21+
"--port", SERVER_EXPOSED_PORT.toString(),
22+
"--ui-public-path", path);
23+
})
24+
.withExposedPorts(SERVER_EXPOSED_PORT, UI_EXPOSED_PORT)
25+
.withReuse(reuse);
26+
}
27+
28+
public TemporalContainer(String dockerImageName, String path, Boolean reuse) {
29+
this(DockerImageName.parse(dockerImageName), path, reuse);
30+
}
31+
32+
public String getUiUrl() {
33+
return "http://" + getHost() + ":" + getMappedPort(UI_EXPOSED_PORT) + path;
34+
}
35+
36+
public String getServerUrl() {
37+
return getHost() + ":" + getMappedPort(SERVER_EXPOSED_PORT);
38+
}
39+
}

extension/deployment/src/main/java/io/quarkiverse/temporal/deployment/devui/TemporalDevUIProcessor.java

Lines changed: 56 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,28 +2,44 @@
22

33
import java.util.List;
44
import java.util.Objects;
5+
import java.util.Optional;
56
import java.util.stream.Collectors;
67

8+
import io.quarkiverse.temporal.deployment.TemporalProcessor;
79
import io.quarkiverse.temporal.deployment.WorkerBuildItem;
810
import io.quarkiverse.temporal.deployment.WorkflowBuildItem;
911
import io.quarkus.deployment.IsDevelopment;
1012
import io.quarkus.deployment.annotations.BuildProducer;
1113
import io.quarkus.deployment.annotations.BuildStep;
14+
import io.quarkus.deployment.annotations.BuildSteps;
15+
import io.quarkus.deployment.builditem.LaunchModeBuildItem;
16+
import io.quarkus.deployment.dev.devservices.GlobalDevServicesConfig;
1217
import io.quarkus.devui.spi.page.CardPageBuildItem;
1318
import io.quarkus.devui.spi.page.ExternalPageBuilder;
1419
import io.quarkus.devui.spi.page.Page;
1520
import io.quarkus.devui.spi.page.PageBuilder;
1621
import io.quarkus.devui.spi.page.TableDataPageBuilder;
22+
import io.quarkus.vertx.http.deployment.NonApplicationRootPathBuildItem;
23+
import io.quarkus.vertx.http.runtime.management.ManagementInterfaceBuildTimeConfig;
1724
import io.temporal.client.WorkflowClient;
1825

1926
/**
2027
* Dev UI card for displaying important details such Temporal version.
2128
*/
29+
@BuildSteps(onlyIf = IsDevelopment.class)
2230
public class TemporalDevUIProcessor {
2331

24-
@BuildStep(onlyIf = IsDevelopment.class)
25-
void createCard(BuildProducer<CardPageBuildItem> cardPageBuildItemBuildProducer, List<WorkflowBuildItem> workflows,
26-
List<WorkerBuildItem> workers) {
32+
@BuildStep
33+
void createCard(
34+
BuildProducer<CardPageBuildItem> cardPageBuildItemBuildProducer,
35+
List<WorkflowBuildItem> workflows,
36+
List<WorkerBuildItem> workers,
37+
GlobalDevServicesConfig globalDevServicesConfig,
38+
TemporalUiConfig uiConfig,
39+
TemporalDevserviceConfig temporalDevserviceConfig,
40+
ManagementInterfaceBuildTimeConfig managementInterfaceBuildTimeConfig,
41+
LaunchModeBuildItem launchMode,
42+
NonApplicationRootPathBuildItem nonApplicationRootPathBuildItem) {
2743
final CardPageBuildItem card = new CardPageBuildItem();
2844

2945
final PageBuilder<ExternalPageBuilder> versionPage = Page.externalPageBuilder("Version")
@@ -58,11 +74,47 @@ void createCard(BuildProducer<CardPageBuildItem> cardPageBuildItemBuildProducer,
5874
card.addBuildTimeData("workers",
5975
workers.stream().map(WorkerBuildTimeData::new).collect(Collectors.toList()));
6076

77+
uiPage(uiConfig.url(), temporalDevserviceConfig, managementInterfaceBuildTimeConfig, launchMode,
78+
nonApplicationRootPathBuildItem, card);
79+
6180
card.setCustomCard("qwc-temporal-card.js");
6281

6382
cardPageBuildItemBuildProducer.produce(card);
6483
}
6584

85+
private void uiPage(
86+
Optional<String> configPath,
87+
TemporalDevserviceConfig temporalDevserviceConfig,
88+
ManagementInterfaceBuildTimeConfig managementInterfaceBuildTimeConfig,
89+
LaunchModeBuildItem launchMode,
90+
NonApplicationRootPathBuildItem nonApplicationRootPathBuildItem,
91+
CardPageBuildItem card) {
92+
var path = configPath;
93+
94+
// check if the UI url is set in the config or if the devservice is enabled
95+
if (!path.isPresent() && Boolean.TRUE.equals(temporalDevserviceConfig.enabled())) {
96+
var defaultBasePath = nonApplicationRootPathBuildItem.resolveManagementPath(
97+
TemporalProcessor.FEATURE,
98+
managementInterfaceBuildTimeConfig,
99+
launchMode);
100+
101+
path = Optional.of(defaultBasePath);
102+
}
103+
104+
// if the path is not set, we don't have a UI to link to
105+
if (!path.isPresent()) {
106+
return;
107+
}
108+
109+
// add the UI page
110+
final PageBuilder<ExternalPageBuilder> uiPage = Page.externalPageBuilder("UI")
111+
.icon("font-awesome-solid:desktop")
112+
.url(path.get(), path.get())
113+
.isHtmlContent();
114+
115+
card.addPage(uiPage);
116+
}
117+
66118
static class WorkflowBuildTimeData {
67119
WorkflowBuildTimeData(WorkflowBuildItem item) {
68120
this.name = item.workflow.getName().replaceAll("\\B\\w+(\\.[a-z])", "$1");
@@ -88,7 +140,7 @@ static class WorkerBuildTimeData {
88140
this.workflows = item.workflows.stream().map(workflow -> workflow.getName().replaceAll("\\B\\w+(\\.[a-z])", "$1"))
89141
.collect(Collectors.toList());
90142
this.activities = item.activities.stream()
91-
.map(activities -> activities.getName().replaceAll("\\B\\w+(\\.[a-z])", "$1")).collect(Collectors.toList());
143+
.map(activity -> activity.getName().replaceAll("\\B\\w+(\\.[a-z])", "$1")).collect(Collectors.toList());
92144
}
93145

94146
private final String name;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package io.quarkiverse.temporal.deployment.devui;
2+
3+
import io.quarkus.runtime.annotations.ConfigPhase;
4+
import io.quarkus.runtime.annotations.ConfigRoot;
5+
import io.smallrye.config.ConfigMapping;
6+
import io.smallrye.config.WithDefault;
7+
8+
@ConfigMapping(prefix = "quarkus.temporal.devservice")
9+
@ConfigRoot(phase = ConfigPhase.BUILD_TIME)
10+
public interface TemporalDevserviceConfig {
11+
12+
/**
13+
* Enable the Temporal Devservice.
14+
*/
15+
@WithDefault("true")
16+
Boolean enabled();
17+
18+
/**
19+
* The image to use for the Temporal Devservice.
20+
*
21+
* <p>
22+
* Minimum supported version: <code>temporalio/auto-setup:1.24.3.0</code>
23+
*/
24+
@WithDefault("temporalio/auto-setup")
25+
String image();
26+
27+
/**
28+
* Whether to reuse the Temporal Devservice.
29+
*/
30+
@WithDefault("true")
31+
boolean reuse();
32+
33+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
package io.quarkiverse.temporal.deployment.devui;
2+
3+
import java.util.Map;
4+
5+
import io.quarkiverse.temporal.deployment.TemporalProcessor;
6+
import io.quarkiverse.temporal.devui.TemporalUiProxy;
7+
import io.quarkus.deployment.IsNormal;
8+
import io.quarkus.deployment.annotations.BuildProducer;
9+
import io.quarkus.deployment.annotations.BuildStep;
10+
import io.quarkus.deployment.annotations.BuildSteps;
11+
import io.quarkus.deployment.annotations.ExecutionTime;
12+
import io.quarkus.deployment.annotations.Record;
13+
import io.quarkus.deployment.builditem.DevServicesResultBuildItem;
14+
import io.quarkus.deployment.builditem.LaunchModeBuildItem;
15+
import io.quarkus.deployment.dev.devservices.GlobalDevServicesConfig;
16+
import io.quarkus.vertx.core.deployment.CoreVertxBuildItem;
17+
import io.quarkus.vertx.http.deployment.NonApplicationRootPathBuildItem;
18+
import io.quarkus.vertx.http.deployment.RouteBuildItem;
19+
import io.quarkus.vertx.http.runtime.management.ManagementInterfaceBuildTimeConfig;
20+
21+
@BuildSteps(onlyIfNot = IsNormal.class, onlyIf = GlobalDevServicesConfig.Enabled.class)
22+
public class TemporalDevserviceProcessor {
23+
24+
private static final String DEV_SERVICE_LABEL = "quarkus-devservice-temporal";
25+
26+
@BuildStep
27+
DevServicesResultBuildItem start(TemporalDevserviceConfig config,
28+
NonApplicationRootPathBuildItem nonApplicationRootPathBuildItem,
29+
LaunchModeBuildItem launchMode,
30+
ManagementInterfaceBuildTimeConfig managementInterfaceBuildTimeConfig) {
31+
if (Boolean.FALSE.equals(config.enabled())) {
32+
return null;
33+
}
34+
35+
String path = nonApplicationRootPathBuildItem.resolveManagementPath(
36+
TemporalProcessor.FEATURE,
37+
managementInterfaceBuildTimeConfig,
38+
launchMode);
39+
40+
TemporalContainer container = new TemporalContainer(config.image(), path, config.reuse());
41+
container.start();
42+
43+
Map<String, String> configOverrides = Map.of(
44+
"quarkus.temporal.connection.target", container.getServerUrl(),
45+
"quarkus.temporal.ui.url", container.getUiUrl());
46+
47+
return new DevServicesResultBuildItem.RunningDevService(
48+
TemporalProcessor.FEATURE,
49+
container.getContainerId(),
50+
container::close,
51+
configOverrides)
52+
.toBuildItem();
53+
}
54+
55+
@BuildStep
56+
@Record(ExecutionTime.RUNTIME_INIT)
57+
void registerWebProxy(
58+
TemporalDevserviceConfig config,
59+
TemporalUiProxy proxy,
60+
BuildProducer<RouteBuildItem> routes,
61+
NonApplicationRootPathBuildItem frameworkRoot,
62+
CoreVertxBuildItem coreVertxBuildItem) {
63+
if (Boolean.FALSE.equals(config.enabled())) {
64+
return;
65+
}
66+
67+
routes.produce(frameworkRoot.routeBuilder()
68+
.management()
69+
.route(TemporalProcessor.FEATURE + "/*")
70+
.displayOnNotFoundPage("Portal UI not found")
71+
.handler(proxy.handler(coreVertxBuildItem.getVertx()))
72+
.build());
73+
}
74+
75+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package io.quarkiverse.temporal.deployment.devui;
2+
3+
import java.util.Optional;
4+
5+
import io.quarkus.runtime.annotations.ConfigPhase;
6+
import io.quarkus.runtime.annotations.ConfigRoot;
7+
import io.smallrye.config.ConfigMapping;
8+
9+
@ConfigMapping(prefix = "quarkus.temporal.ui")
10+
@ConfigRoot(phase = ConfigPhase.BUILD_TIME)
11+
public interface TemporalUiConfig {
12+
13+
/**
14+
* The url of the Temporal UI.
15+
*/
16+
Optional<String> url();
17+
18+
}

extension/deployment/src/test/java/io/quarkiverse/temporal/deployment/HealthCheckEnabledTest.java

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,17 +19,18 @@ public class HealthCheckEnabledTest {
1919
.setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class)
2020
.addAsResource(
2121
new StringAsset(
22-
"quarkus.temporal.start-workers: false\n" + "quarkus.temporal.health.enabled: true\n"),
22+
"quarkus.temporal.start-workers: false\n" +
23+
"quarkus.temporal.health.enabled: true\n"),
2324
"application.properties"));
2425

2526
@Test
26-
public void testDataSourceHealthCheckExclusion() {
27+
public void testDataSourceHealthCheck() {
2728
RestAssured.when().get("/q/health/ready")
2829
.then()
29-
.body("status", equalTo("DOWN")) // Verifies that the status at the root level is "DOWN"
30+
.body("status", equalTo("UP")) // Verifies that the status at the root level is "DOWN"
3031
.body("checks", hasSize(1)) // Verifies that there is exactly one check in the "checks" array
3132
.body("checks[0].name", equalTo("Temporal")) // Verifies that the name of the first check is "Temporal"
32-
.body("checks[0].status", equalTo("DOWN")) // Verifies that the status of the first check is "DOWN"
33-
.body("checks[0].data.'127.0.0.1:7233'", equalTo("DOWN"));
33+
.body("checks[0].status", equalTo("UP")) // Verifies that the status of the first check is "DOWN"
34+
;
3435
}
3536
}

extension/runtime/pom.xml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,14 @@
6363
<groupId>io.opentelemetry</groupId>
6464
<artifactId>opentelemetry-opentracing-shim</artifactId>
6565
</dependency>
66+
<dependency>
67+
<groupId>io.quarkus</groupId>
68+
<artifactId>quarkus-vertx-http</artifactId>
69+
</dependency>
70+
<dependency>
71+
<groupId>io.vertx</groupId>
72+
<artifactId>vertx-web-client</artifactId>
73+
</dependency>
6674
</dependencies>
6775
<build>
6876
<plugins>

extension/runtime/src/main/java/io/quarkiverse/temporal/config/ConnectionRuntimeConfig.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,8 @@ public interface ConnectionRuntimeConfig {
1111
/**
1212
* Sets a target string, which can be either a valid {@link NameResolver}-compliant URI, or an
1313
* authority string. See {@link ManagedChannelBuilder#forTarget(String)} for more information
14-
* about parameter format. Default is 127.0.0.1:7233
14+
* about parameter format.
1515
*/
16-
@WithDefault("127.0.0.1:7233")
1716
String target();
1817

1918
/**

0 commit comments

Comments
 (0)