diff --git a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/ProbeConfig.java b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/ProbeConfig.java index 4efdbf7543e75..b186b82da956c 100644 --- a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/ProbeConfig.java +++ b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/ProbeConfig.java @@ -24,7 +24,7 @@ public class ProbeConfig { Optional httpActionPortName; /** - * The http path to use for the probe For this to work, the container port also + * The http path to use for the probe. For this to work, the container port also * needs to be set. * * Assuming the container port has been set (as per above comment), if @@ -48,6 +48,12 @@ public class ProbeConfig { @ConfigItem Optional tcpSocketAction; + /** + * The gRPC port to use for the probe (the format is either port or port:service). + */ + @ConfigItem + Optional grpcAction; + /** * The amount of time to wait before starting to probe. */ @@ -79,6 +85,6 @@ public class ProbeConfig { Integer failureThreshold; public boolean hasUserSuppliedAction() { - return httpActionPath.isPresent() || tcpSocketAction.isPresent() || execAction.isPresent(); + return httpActionPath.isPresent() || tcpSocketAction.isPresent() || execAction.isPresent() || grpcAction.isPresent(); } } diff --git a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/ProbeConverter.java b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/ProbeConverter.java index 8e1c0a520b33e..52b1ad05ca23c 100644 --- a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/ProbeConverter.java +++ b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/ProbeConverter.java @@ -12,9 +12,10 @@ public static Probe convert(ProbeConfig probe) { public static ProbeBuilder builder(ProbeConfig probe) { ProbeBuilder b = new ProbeBuilder(); - probe.httpActionPath.ifPresent(v -> b.withHttpActionPath(v)); - probe.execAction.ifPresent(v -> b.withExecAction(v)); - probe.tcpSocketAction.ifPresent(v -> b.withTcpSocketAction(v)); + probe.httpActionPath.ifPresent(b::withHttpActionPath); + probe.execAction.ifPresent(b::withExecAction); + probe.tcpSocketAction.ifPresent(b::withTcpSocketAction); + probe.grpcAction.ifPresent(b::withGrpcAction); b.withInitialDelaySeconds((int) probe.initialDelay.getSeconds()); b.withPeriodSeconds((int) probe.period.getSeconds()); b.withTimeoutSeconds((int) probe.timeout.getSeconds()); diff --git a/integration-tests/kubernetes/quarkus-standard-way/src/test/java/io/quarkus/it/kubernetes/KubernetesWithGrpcProbeTest.java b/integration-tests/kubernetes/quarkus-standard-way/src/test/java/io/quarkus/it/kubernetes/KubernetesWithGrpcProbeTest.java new file mode 100644 index 0000000000000..5d8a55e74209e --- /dev/null +++ b/integration-tests/kubernetes/quarkus-standard-way/src/test/java/io/quarkus/it/kubernetes/KubernetesWithGrpcProbeTest.java @@ -0,0 +1,68 @@ +package io.quarkus.it.kubernetes; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.List; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.fabric8.kubernetes.api.model.HasMetadata; +import io.fabric8.kubernetes.api.model.apps.Deployment; +import io.quarkus.builder.Version; +import io.quarkus.maven.dependency.Dependency; +import io.quarkus.test.ProdBuildResults; +import io.quarkus.test.ProdModeTestResults; +import io.quarkus.test.QuarkusProdModeTest; + +public class KubernetesWithGrpcProbeTest { + + private static final String APP_NAME = "kubernetes-with-grpc-probe"; + + @RegisterExtension + static final QuarkusProdModeTest config = new QuarkusProdModeTest() + .withApplicationRoot((jar) -> jar.addClasses(GreetingResource.class)) + .setApplicationName(APP_NAME) + .setApplicationVersion("0.1-SNAPSHOT") + .overrideConfigKey("quarkus.kubernetes.readiness-probe.grpc-action", "9191:my-service") + .setLogFileName("k8s.log") + .setForcedDependencies(List.of( + Dependency.of("io.quarkus", "quarkus-kubernetes", Version.getVersion()), + Dependency.of("io.quarkus", "quarkus-smallrye-health", Version.getVersion()))); + + @ProdBuildResults + private ProdModeTestResults prodModeTestResults; + + @Test + public void assertGeneratedResources() throws IOException { + + final Path kubernetesDir = prodModeTestResults.getBuildDir().resolve("kubernetes"); + assertThat(kubernetesDir) + .isDirectoryContaining(p -> p.getFileName().endsWith("kubernetes.json")) + .isDirectoryContaining(p -> p.getFileName().endsWith("kubernetes.yml")); + List kubernetesList = DeserializationUtil + .deserializeAsList(kubernetesDir.resolve("kubernetes.yml")); + assertThat(kubernetesList.get(0)).isInstanceOfSatisfying(Deployment.class, d -> { + assertThat(d.getMetadata()).satisfies(m -> { + assertThat(m.getName()).isEqualTo(APP_NAME); + }); + + assertThat(d.getSpec()).satisfies(deploymentSpec -> { + assertThat(deploymentSpec.getTemplate()).satisfies(t -> { + assertThat(t.getSpec()).satisfies(podSpec -> { + assertThat(podSpec.getContainers()).singleElement() + .satisfies(container -> { + assertThat(container.getReadinessProbe()).isNotNull().satisfies(p -> { + assertEquals(p.getGrpc().getPort().intValue(), 9191); + assertEquals(p.getGrpc().getService(), "my-service"); + }); + }); + }); + }); + }); + }); + } +}