Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
b03fa81
Extract port-checking logic into common module
holly-cummins Jun 23, 2025
43a064a
Add more test coverage for Kafka dev services
holly-cummins Jun 23, 2025
315df35
Bump org.hibernate.reactive:hibernate-reactive-core
dependabot[bot] Jun 23, 2025
fefe67a
Bump io.smallrye.common:smallrye-common-bom from 2.12.0 to 2.12.2
dependabot[bot] Jun 24, 2025
0b48790
Reorder tests
holly-cummins Jun 24, 2025
83921bb
Merge pull request #48544 from quarkusio/dependabot/maven/org.hiberna…
yrodiere Jun 25, 2025
031eef6
Redis Client: fix BLPOP and BRPOP
Ladicek Jun 25, 2025
0296649
Merge pull request #48543 from quarkusio/dependabot/maven/io.smallrye…
gsmet Jun 25, 2025
39231c8
Merge pull request #48596 from Ladicek/redis-fix-blpop-brpop
gsmet Jun 25, 2025
28429c6
Polish OpenTelemetryDestroyer
geoand Jun 25, 2025
6a7dc43
Merge pull request #48584 from holly-cummins/reorder-test-methods
holly-cummins Jun 25, 2025
663e799
Merge pull request #48599 from geoand/OpenTelemetryDestroyer-polish
geoand Jun 25, 2025
b209439
Add the possibility to configure a custom image for k8s dev container
anisikram Jun 17, 2025
bb4a65d
Merge pull request #48552 from anisikram/add-config-for-custom-k8s-de…
geoand Jun 25, 2025
0c49734
expose subprotocol to client connections
emattheis Jun 20, 2025
b857e19
Merge pull request #48502 from emattheis/wsnext-clientconnection-subp…
geoand Jun 25, 2025
3289f66
Merge pull request #48555 from holly-cummins/more-kafka-devservice-tests
holly-cummins Jun 25, 2025
9ccf42e
using systemPropertiesPrefixedBy to retrieve quarkus.test properties
cdsap Jun 25, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
import java.util.HashSet;
import java.util.Map;
import java.util.Optional;
import java.util.Properties;
import java.util.Set;
import java.util.stream.Collectors;

Expand Down Expand Up @@ -462,7 +461,7 @@ public boolean isSatisfiedBy(Task t) {
});

tasks.withType(Test.class).configureEach(t -> {
t.setSystemProperties(extractQuarkusTestSystemProperties());
t.setSystemProperties(extractQuarkusTestSystemProperties(project));

t.getInputs().files(quarkusGenerateTestAppModelTask);
// Quarkus test configuration action which should be executed before any Quarkus test
Expand Down Expand Up @@ -731,16 +730,9 @@ private Optional<TaskProvider<Task>> getLazyTask(Project project, String name) {
}
}

private static Map<String, Object> extractQuarkusTestSystemProperties() {
Properties systemProperties = System.getProperties();
Map<String, Object> quarkusSystemProperties = new HashMap<>();
for (String propertyName : systemProperties.stringPropertyNames()) {
if (!propertyName.startsWith("quarkus.test.")) {
continue;
}

quarkusSystemProperties.put(propertyName, systemProperties.get(propertyName));
}
return quarkusSystemProperties;
private static Map<String, Object> extractQuarkusTestSystemProperties(Project project) {
return new HashMap<>(project.getProviders()
.systemPropertiesPrefixedBy("quarkus.test.")
.get());
}
}
9 changes: 9 additions & 0 deletions docs/src/main/asciidoc/kubernetes-dev-services.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,15 @@ quarkus.kubernetes-client.devservices.flavor=api-only # k3s or kind
quarkus.kubernetes-client.devservices.api-version=1.22
----

You can also configure a custom image compatible with standard images (kind, k3s & api-server) using the `quarkus.kubernetes-client.devservices.image-name` property. However, it must be consistent with the flavor and api-version properties:

[source, properties]
----
quarkus.kubernetes-client.devservices.flavor=api-only # k3s or kind
quarkus.kubernetes-client.devservices.api-version=1.24.1
quarkus.kubernetes-client.devservices.image-name=quay.io/giantswarm/kube-apiserver:v1.24.1
----

`api-only` only starts a Kubernetes API Server (plus the required etcd). If you need a fully-featured Kubernetes cluster that can spin up Pods, you can use `k3s` or `kind`. `k3s` requires to start the container with `privileged mode`. The `kind` test container now also supports using podman's rootless mode.

If `api-version` is not set, the latest version for the given flavor will be used. Otherwise, the version must match a https://github.com/dajudge/kindcontainer/blob/master/k8s-versions.json[version supported by the given flavor].
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;

Expand All @@ -30,6 +31,8 @@
import com.dajudge.kindcontainer.K3sContainerVersion;
import com.dajudge.kindcontainer.KindContainer;
import com.dajudge.kindcontainer.KindContainerVersion;
import com.dajudge.kindcontainer.KubernetesContainer;
import com.dajudge.kindcontainer.KubernetesImageSpec;
import com.dajudge.kindcontainer.KubernetesVersionEnum;
import com.dajudge.kindcontainer.client.KubeConfigUtils;
import com.dajudge.kindcontainer.client.config.Cluster;
Expand Down Expand Up @@ -225,18 +228,12 @@ private RunningDevService startKubernetes(DockerStatusBuildItem dockerStatusBuil
.orElse(api_only);

@SuppressWarnings("rawtypes")
final var container = switch (clusterType) {
case api_only -> new ApiServerContainer(
config.apiVersion
.map(version -> findOrElseThrow(clusterType, version, ApiServerContainerVersion.class))
.orElseGet(() -> latest(ApiServerContainerVersion.class)));
case k3s -> new K3sContainer(
config.apiVersion.map(version -> findOrElseThrow(clusterType, version, K3sContainerVersion.class))
.orElseGet(() -> latest(K3sContainerVersion.class)));
case kind -> new KindContainer(
config.apiVersion
.map(version -> findOrElseThrow(clusterType, version, KindContainerVersion.class))
.orElseGet(() -> latest(KindContainerVersion.class)));
KubernetesContainer container = switch (clusterType) {
case api_only -> createContainer(ApiServerContainer::new, ApiServerContainerVersion.class, config, clusterType);

case k3s -> createContainer(K3sContainer::new, K3sContainerVersion.class, config, clusterType);

case kind -> createContainer(KindContainer::new, KindContainerVersion.class, config, clusterType);
};

if (useSharedNetwork) {
Expand Down Expand Up @@ -270,7 +267,21 @@ private RunningDevService startKubernetes(DockerStatusBuildItem dockerStatusBuil
.orElseGet(defaultKubernetesClusterSupplier);
}

<T extends KubernetesVersionEnum<T>> T findOrElseThrow(final Flavor flavor, final String version,
@SuppressWarnings("rawtypes")
private <T extends KubernetesVersionEnum<T>, C extends KubernetesContainer> C createContainer(
Function<KubernetesImageSpec<T>, C> constructor,
Class<T> versionClass,
KubernetesDevServiceCfg config,
Flavor flavor) {
T version = config.apiVersion
.map(v -> findOrElseThrow(flavor, v, versionClass))
.orElseGet(() -> latest(versionClass));

KubernetesImageSpec<T> imageSpec = version.withImage(config.imageName);
return constructor.apply(imageSpec);
}

private <T extends KubernetesVersionEnum<T>> T findOrElseThrow(final Flavor flavor, final String version,
final Class<T> versions) {
final String versionWithPrefix = !version.startsWith("v") ? "v" + version : version;
return KubernetesVersionEnum.ascending(versions)
Expand Down Expand Up @@ -315,6 +326,7 @@ private KubernetesDevServiceCfg getConfiguration(KubernetesClientBuildConfig cfg
private static final class KubernetesDevServiceCfg {

public boolean devServicesEnabled;
public String imageName;
public Optional<Flavor> flavor;
public Optional<String> apiVersion;
public boolean overrideKubeconfig;
Expand All @@ -324,6 +336,8 @@ private static final class KubernetesDevServiceCfg {

public KubernetesDevServiceCfg(KubernetesDevServicesBuildTimeConfig config) {
this.devServicesEnabled = config.enabled();
this.imageName = config.imageName()
.orElse(null);
this.serviceName = config.serviceName();
this.apiVersion = config.apiVersion();
this.overrideKubeconfig = config.overrideKubeconfig();
Expand All @@ -334,7 +348,8 @@ public KubernetesDevServiceCfg(KubernetesDevServicesBuildTimeConfig config) {

@Override
public int hashCode() {
return Objects.hash(devServicesEnabled, flavor, apiVersion, overrideKubeconfig, shared, serviceName, containerEnv);
return Objects.hash(devServicesEnabled, imageName, flavor, apiVersion, overrideKubeconfig, shared, serviceName,
containerEnv);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,15 @@ public interface KubernetesDevServicesBuildTimeConfig {
*/
Optional<String> apiVersion();

/**
* The kubernetes image to use.
* <p>
* If not set, Dev Services for Kubernetes will use default image for the specified {@link #apiVersion()} for the given
* {@link #flavor()}.
*/

Optional<String> imageName();

/**
* The flavor to use (kind, k3s or api-only).
* <p>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,9 @@ public class OpenTelemetryDestroyer implements BeanDestroyer<OpenTelemetry> {
@Override
public void destroy(OpenTelemetry openTelemetry, CreationalContext<OpenTelemetry> creationalContext,
Map<String, Object> params) {
if (openTelemetry instanceof OpenTelemetrySdk) {
if (openTelemetry instanceof OpenTelemetrySdk openTelemetrySdk) {
// between flush and shutdown we will wait shutdown-wait-time, at the most.
var waitTime = getShutdownWaitTime().dividedBy(4);
var openTelemetrySdk = ((OpenTelemetrySdk) openTelemetry);
openTelemetrySdk.getSdkLoggerProvider().forceFlush().join(waitTime.toMillis(), MILLISECONDS);
openTelemetrySdk.getSdkTracerProvider().forceFlush().join(waitTime.toMillis(), MILLISECONDS);
openTelemetrySdk.getSdkMeterProvider().forceFlush().join(waitTime.toMillis(), MILLISECONDS);
Expand All @@ -31,8 +30,7 @@ public void destroy(OpenTelemetry openTelemetry, CreationalContext<OpenTelemetry

public static Duration getShutdownWaitTime() {
var config = ConfigProvider.getConfig().unwrap(SmallRyeConfig.class);
var waitTime = config.getOptionalValue("quarkus.otel.experimental.shutdown-wait-time", Duration.class)
return config.getOptionalValue("quarkus.otel.experimental.shutdown-wait-time", Duration.class)
.orElse(Duration.ofSeconds(2));
return waitTime;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,6 @@ Uni<Response> _blpop(Duration timeout, K... keys) {
validate(timeout, "timeout");

RedisCommand cmd = RedisCommand.of(Command.BLPOP);
cmd.put(timeout.toSeconds());
cmd.putAll(marshaller.encode(keys));
cmd.put(timeout.toSeconds());

Expand All @@ -140,7 +139,6 @@ Uni<Response> _brpop(Duration timeout, K... keys) {
validate(timeout, "timeout");

RedisCommand cmd = RedisCommand.of(Command.BRPOP);
cmd.put(timeout.toSeconds());
cmd.putAll(marshaller.encode(keys));
cmd.put(timeout.toSeconds());

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package io.quarkus.websockets.next.test.subprotocol;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;

import java.net.URI;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;

import jakarta.inject.Inject;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;

import io.quarkus.test.QuarkusUnitTest;
import io.quarkus.test.common.http.TestHTTPResource;
import io.quarkus.websockets.next.OnOpen;
import io.quarkus.websockets.next.WebSocket;
import io.quarkus.websockets.next.WebSocketClient;
import io.quarkus.websockets.next.WebSocketClientConnection;
import io.quarkus.websockets.next.WebSocketConnection;
import io.quarkus.websockets.next.WebSocketConnector;

public class SubprotocolFromConnectionTest {

@RegisterExtension
public static final QuarkusUnitTest test = new QuarkusUnitTest()
.withApplicationRoot(root -> {
root.addClasses(Endpoint.class, Client.class);
}).overrideConfigKey("quarkus.websockets-next.server.supported-subprotocols", "oak,larch");

@TestHTTPResource("/")
URI endpointUri;

@Inject
WebSocketConnector<Client> connector;

@Test
void testSubprotocolFromConnection() throws InterruptedException, ExecutionException {
var connection = connector.baseUri(endpointUri).addSubprotocol("larch").connectAndAwait();
assertEquals("larch", connection.subprotocol());
assertTrue(Client.OPEN_LATCH.await(5, TimeUnit.SECONDS));
assertEquals("larch", Client.SUB_PROTOCOL.get());
assertTrue(Endpoint.OPEN_LATCH.await(5, TimeUnit.SECONDS));
assertEquals("larch", Endpoint.SUB_PROTOCOL.get());
connection.closeAndAwait();
}

@WebSocket(path = "/endpoint")
public static class Endpoint {

static final CountDownLatch OPEN_LATCH = new CountDownLatch(1);

static final AtomicReference<String> SUB_PROTOCOL = new AtomicReference<>();

@OnOpen
void connected(WebSocketConnection connection) {
SUB_PROTOCOL.set(connection.subprotocol());
OPEN_LATCH.countDown();
}
}

@WebSocketClient(path = "/endpoint")
public static class Client {

static final CountDownLatch OPEN_LATCH = new CountDownLatch(1);

static final AtomicReference<String> SUB_PROTOCOL = new AtomicReference<>();

@OnOpen
void connected(WebSocketClientConnection connection) {
SUB_PROTOCOL.set(connection.subprotocol());
OPEN_LATCH.countDown();
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,12 @@ default void closeAndAwait(CloseReason reason) {
*/
HandshakeRequest handshakeRequest();

/**
*
* @return the subprotocol selected by the handshake
*/
String subprotocol();

/**
*
* @return the time when this connection was created
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,6 @@ public interface WebSocketConnection extends Connection {
*/
Set<WebSocketConnection> getOpenConnections();

/**
*
* @return the subprotocol selected by the handshake
*/
String subprotocol();

/**
* Makes it possible to send messages to all clients connected to the same WebSocket endpoint.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,11 @@ public HandshakeRequest handshakeRequest() {
return handshakeRequest;
}

@Override
public String subprotocol() {
return webSocket().subProtocol();
}

@Override
public Instant creationTime() {
return creationTime;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,11 +65,6 @@ public Set<WebSocketConnection> getOpenConnections() {
.collect(Collectors.toUnmodifiableSet());
}

@Override
public String subprotocol() {
return webSocket.subProtocol();
}

@Override
public String toString() {
return "WebSocket connection [endpointId=" + endpointId + ", path=" + webSocket.path() + ", id=" + identifier + "]";
Expand Down
2 changes: 1 addition & 1 deletion independent-projects/arc/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@
<version.jboss-logging>3.6.1.Final</version.jboss-logging>
<version.mutiny>2.9.2</version.mutiny>
<version.bridger>1.6.Final</version.bridger>
<version.smallrye-common>2.12.0</version.smallrye-common>
<version.smallrye-common>2.12.2</version.smallrye-common>
<!-- test versions -->
<version.assertj>3.27.3</version.assertj>
<version.junit5>5.13.2</version.junit5>
Expand Down
2 changes: 1 addition & 1 deletion independent-projects/bootstrap/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@
<plexus-interpolation.version>1.26</plexus-interpolation.version>
<plexus-sec-dispatcher.version>2.0</plexus-sec-dispatcher.version>
<plexus-utils.version>3.5.1</plexus-utils.version>
<smallrye-common.version>2.12.0</smallrye-common.version>
<smallrye-common.version>2.12.2</smallrye-common.version>
<smallrye-beanbag.version>1.5.3</smallrye-beanbag.version>
<gradle-tooling.version>8.14</gradle-tooling.version>
<quarkus-fs-util.version>1.0.0</quarkus-fs-util.version>
Expand Down
2 changes: 1 addition & 1 deletion independent-projects/qute/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
<version.jandex>3.3.1</version.jandex>
<version.gizmo>1.9.0</version.gizmo>
<version.jboss-logging>3.6.1.Final</version.jboss-logging>
<version.smallrye-common>2.12.0</version.smallrye-common>
<version.smallrye-common>2.12.2</version.smallrye-common>
<version.smallrye-mutiny>2.9.2</version.smallrye-mutiny>
</properties>

Expand Down
2 changes: 1 addition & 1 deletion independent-projects/resteasy-reactive/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@
<jakarta.persistence-api.version>3.1.0</jakarta.persistence-api.version>

<mutiny.version>2.9.2</mutiny.version>
<smallrye-common.version>2.12.0</smallrye-common.version>
<smallrye-common.version>2.12.2</smallrye-common.version>
<vertx.version>4.5.16</vertx.version>
<rest-assured.version>5.5.5</rest-assured.version>
<commons-logging-jboss-logging.version>1.0.0.Final</commons-logging-jboss-logging.version>
Expand Down
Loading