diff --git a/bom/application/pom.xml b/bom/application/pom.xml
index 3d8d487e0e628..dbebd9d7917e4 100644
--- a/bom/application/pom.xml
+++ b/bom/application/pom.xml
@@ -86,8 +86,8 @@
3.12.0
1.15
1.5.1
- 5.6.7.Final
- 1.12.8
+ 5.6.8.Final
+ 1.12.9
1.1.4.Final
6.2.3.Final
6.1.3.Final
@@ -153,7 +153,7 @@
1.6.10
1.6.0
1.3.2
- 2.9.0
+ 2.9.2
3.1.0
4.2.0
1.0.9
@@ -195,8 +195,8 @@
2.2.1.Final
0.1.7.Final
0.8.7
- 1.16.3
- 3.2.12
+ 1.17.1
+ 3.2.13
2.2
2.6
diff --git a/devtools/cli/distribution/jreleaser.yml b/devtools/cli/distribution/jreleaser.yml
index 31f15b55c7b60..40a4b01e8a033 100644
--- a/devtools/cli/distribution/jreleaser.yml
+++ b/devtools/cli/distribution/jreleaser.yml
@@ -41,7 +41,6 @@ distributions:
executable:
name: quarkus
windowsExtension: bat
- executableExtension: bat
tags:
- quarkus
- cli
diff --git a/devtools/cli/distribution/release-cli.sh b/devtools/cli/distribution/release-cli.sh
index 80410588077f8..786b207f485b2 100755
--- a/devtools/cli/distribution/release-cli.sh
+++ b/devtools/cli/distribution/release-cli.sh
@@ -45,7 +45,7 @@ export JRELEASER_PROJECT_VERSION=${VERSION}
export JRELEASER_BRANCH=${BRANCH}
export JRELEASER_CHOCOLATEY_GITHUB_BRANCH=${BRANCH}
-jbang org.jreleaser:jreleaser:1.0.0-M3 full-release \
+jbang org.jreleaser:jreleaser:1.0.0 full-release \
--git-root-search \
-od target
diff --git a/docs/src/main/asciidoc/building-my-first-extension.adoc b/docs/src/main/asciidoc/building-my-first-extension.adoc
index 761cbf2147bc9..7ae69185458bd 100644
--- a/docs/src/main/asciidoc/building-my-first-extension.adoc
+++ b/docs/src/main/asciidoc/building-my-first-extension.adoc
@@ -849,7 +849,7 @@ mvn io.quarkus.platform:quarkus-maven-plugin:{quarkus-version}:create \
-DprojectGroupId=org.acme \
-DprojectArtifactId=greeting-app \
-Dextensions="org.acme:greeting-extension:1.0.0-SNAPSHOT" \
- -DnoExamples
+ -DnoCode
----
`cd` into `greeting-app`.
diff --git a/docs/src/main/asciidoc/kafka.adoc b/docs/src/main/asciidoc/kafka.adoc
index d6137301e99a5..37b84824e3627 100644
--- a/docs/src/main/asciidoc/kafka.adoc
+++ b/docs/src/main/asciidoc/kafka.adoc
@@ -2401,6 +2401,103 @@ quarkus.log.category."org.apache.kafka.common.utils".level=INFO
quarkus.log.category."org.apache.kafka.common.metrics".level=INFO
----
+== Connecting to Managed Kafka clusters
+
+This section explains how to connect to notorious Kafka Cloud Services.
+
+=== Azure Event Hub
+
+https://docs.microsoft.com/en-us/azure/event-hubs/event-hubs-for-kafka-ecosystem-overview[Azure Event Hub] provides an endpoint compatible with Apache Kafka.
+
+NOTE: Azure Event Hubs for Kafka is not available in the _basic_ tier.
+You need at least the _standard_ tier to use Kafka.
+See https://azure.microsoft.com/en-us/pricing/details/event-hubs/[Azure Event Hubs Pricing] to see the other options.
+
+To connect to Azure Event Hub, using the Kafka protocol with TLS, you need the following configuration:
+
+[source, properties]
+----
+kafka.bootstrap.servers=my-event-hub.servicebus.windows.net:9093 # <1>
+kafka.security.protocol=SASL_SSL
+kafka.sasl.mechanism=PLAIN
+kafka.sasl.jaas.config=org.apache.kafka.common.security.plain.PlainLoginModule required \ # <2>
+ username="$ConnectionString" \ # <3>
+ password=""; # <4>
+----
+<1> The port is `9093`.
+<2> You need to use the JAAS `PlainLoginModule`.
+<3> The username is the `$ConnectionString` string.
+<4> The Event Hub connection string given by Azure.
+
+Replace `` with the connection string for your Event Hubs namespace.
+For instructions on getting the connection string, see https://docs.microsoft.com/en-us/azure/event-hubs/event-hubs-get-connection-string[Get an Event Hubs connection string].
+The result would be something like:
+
+[source, properties]
+----
+kafka.sasl.jaas.config=org.apache.kafka.common.security.plain.PlainLoginModule required \
+ username="$ConnectionString" \
+ password="Endpoint=sb://my-event-hub.servicebus.windows.net/;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=XXXXXXXXXXXXXXXX";
+----
+
+This configuration can be global (as above), or set in the channel configuration:
+
+[source, properties]
+----
+mp.messaging.incoming.$channel.bootstrap.servers=my-event-hub.servicebus.windows.net:9093
+mp.messaging.incoming.$channel.security.protocol=SASL_SSL
+mp.messaging.incoming.$channel.sasl.mechanism=PLAIN
+mp.messaging.incoming.$channel.sasl.jaas.config=org.apache.kafka.common.security.plain.PlainLoginModule required \
+ username="$ConnectionString" \
+ password="Endpoint=sb://my-event-hub.servicebus.windows.net/;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=...";
+----
+
+=== Red Hat OpenShift Streams for Apache Kafka
+
+https://cloud.redhat.com/[Red Hat OpenShift Streams for Apache Kafka] provides managed Kafka brokers.
+First, follow the instructions from https://access.redhat.com/documentation/en-us/red_hat_openshift_streams_for_apache_kafka/1/guide/88e1487a-2a14-4b35-85b9-a7a2d67a37f3[Getting started with the `rhoas` CLI for Red Hat OpenShift Streams for Apache Kafka] to create your Kafka broker instance.
+Make sure you copied the client id and client secret associated with the _ServiceAccount_ you created.
+
+Then, you can configure the Quarkus application to connect to the broker as follows:
+
+[source, properties]
+----
+kafka.bootstrap.servers= # <1>
+kafka.security.protocol=SASL_SSL
+kafka.sasl.mechanism=PLAIN
+kafka.sasl.jaas.config=org.apache.kafka.common.security.plain.PlainLoginModule required \
+ username="${KAFKA_USERNAME}" \ # <2>
+ password="${KAFKA_PASSWORD}"; # <3>
+----
+<1> The connection string, given on the admin console, such as `demo-c--bjsv-ldd-cvavkc-a.bf2.kafka.rhcloud.com:443`
+<2> The kafka username (the client id from the service account)
+<3> the kafka password (the client secret from the service account)
+
+NOTE: In general, these properties are prefixed using `%prod` to enable them only when running in production mode.
+
+IMPORTANT: As explained in https://access.redhat.com/documentation/en-us/red_hat_openshift_streams_for_apache_kafka/1/guide/88e1487a-2a14-4b35-85b9-a7a2d67a37f3[Getting started with the rhoas CLI for Red Hat OpenShift Streams for Apache Kafka], to use Red Hat OpenShift Streams for Apache Kafka, you must create the topic beforehand, create a _Service Account_, and provide permissions to read and write to your topic from that service account.
+The authentication data (client id and secret) relates to the service account, which means you can implement fine-grain permissions and restrict access to the topic.
+
+When using Kubernetes, it is recommended to set the client id and secret in a Kubernetes secret:
+
+[source, yaml]
+----
+apiVersion: v1
+kind: Secret
+metadata:
+ name: kafka-credentials
+stringData:
+ KAFKA_USERNAME: "..."
+ KAFKA_PASSWORD: "..."
+----
+
+To allow your Quarkus application to use that secret, add the following line to the `application.properties` file:
+
+[source, properties]
+----
+%prod.quarkus.openshift.env.secrets=kafka-credentials
+----
+
== Going further
This guide has shown how you can interact with Kafka using Quarkus.
diff --git a/docs/src/main/asciidoc/rest-client.adoc b/docs/src/main/asciidoc/rest-client.adoc
index 2a61527b55439..6be30f043a969 100644
--- a/docs/src/main/asciidoc/rest-client.adoc
+++ b/docs/src/main/asciidoc/rest-client.adoc
@@ -685,7 +685,7 @@ public class WireMockExtensions implements QuarkusTestResourceLifecycleManager {
wireMockServer = new WireMockServer();
wireMockServer.start(); // <3>
- stubFor(get(urlEqualTo("/extensions?id=io.quarkus:quarkus-rest-client")) // <4>
+ wireMockServer.stubFor(get(urlEqualTo("/extensions?id=io.quarkus:quarkus-rest-client")) // <4>
.willReturn(aResponse()
.withHeader("Content-Type", "application/json")
.withBody(
@@ -695,7 +695,7 @@ public class WireMockExtensions implements QuarkusTestResourceLifecycleManager {
"}]"
)));
- stubFor(get(urlMatching(".*")).atPriority(10).willReturn(aResponse().proxiedFrom("https://stage.code.quarkus.io/api"))); // <5>
+ wireMockServer.stubFor(get(urlMatching(".*")).atPriority(10).willReturn(aResponse().proxiedFrom("https://stage.code.quarkus.io/api"))); // <5>
return Collections.singletonMap("quarkus.rest-client.\"org.acme.rest.client.ExtensionsService\".url", wireMockServer.baseUrl()); // <6>
}
diff --git a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/AzureDiskVolumeConverter.java b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/AzureDiskVolumeConverter.java
index a48ebb1ed2395..4a248b66d73bc 100644
--- a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/AzureDiskVolumeConverter.java
+++ b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/AzureDiskVolumeConverter.java
@@ -14,7 +14,7 @@ public static AzureDiskVolume convert(Map.Entry e
private static AzureDiskVolumeBuilder convert(AzureDiskVolumeConfig c) {
AzureDiskVolumeBuilder b = new AzureDiskVolumeBuilder();
- b.withNewDiskName(c.diskName);
+ b.withDiskName(c.diskName);
b.withDiskURI(c.diskURI);
b.withKind(c.kind.name());
b.withCachingMode(c.cachingMode.name());
diff --git a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/KnativeProcessor.java b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/KnativeProcessor.java
index 42c6b455214d6..57e9574d610a9 100644
--- a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/KnativeProcessor.java
+++ b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/KnativeProcessor.java
@@ -24,11 +24,14 @@
import io.dekorate.knative.decorator.ApplyGlobalTargetUtilizationDecorator;
import io.dekorate.knative.decorator.ApplyLocalContainerConcurrencyDecorator;
import io.dekorate.knative.decorator.ApplyRevisionNameDecorator;
+import io.dekorate.knative.decorator.ApplyServiceAccountToRevisionSpecDecorator;
import io.dekorate.knative.decorator.ApplyTrafficDecorator;
import io.dekorate.kubernetes.config.EnvBuilder;
import io.dekorate.kubernetes.decorator.AddConfigMapDataDecorator;
import io.dekorate.kubernetes.decorator.AddEnvVarDecorator;
+import io.dekorate.kubernetes.decorator.AddImagePullSecretToServiceAccountDecorator;
import io.dekorate.kubernetes.decorator.AddLabelDecorator;
+import io.dekorate.kubernetes.decorator.AddServiceAccountResourceDecorator;
import io.dekorate.kubernetes.decorator.ApplicationContainerDecorator;
import io.dekorate.project.Project;
import io.quarkus.container.spi.BaseImageInfoBuildItem;
@@ -136,17 +139,20 @@ public List createDecorators(ApplicationInfoBuildItem applic
Optional readinessPath,
List roles,
List roleBindings,
- Optional customProjectRoot) {
+ Optional customProjectRoot,
+ List targets) {
List result = new ArrayList<>();
- String name = ResourceNameUtil.getResourceName(config, applicationInfo);
+ if (!targets.stream().filter(KubernetesDeploymentTargetBuildItem::isEnabled)
+ .anyMatch(t -> KNATIVE.equals(t.getName()))) {
+ return result;
+ }
+ String name = ResourceNameUtil.getResourceName(config, applicationInfo);
Optional project = KubernetesCommonHelper.createProject(applicationInfo, customProjectRoot, outputTarget,
packageConfig);
- result.addAll(KubernetesCommonHelper.createDecorators(project, KNATIVE, name, config,
- metricsConfiguration, annotations,
- labels, command,
- ports, livenessPath, readinessPath, roles, roleBindings));
+ result.addAll(KubernetesCommonHelper.createDecorators(project, KNATIVE, name, config, metricsConfiguration, annotations,
+ labels, command, ports, livenessPath, readinessPath, roles, roleBindings));
image.ifPresent(i -> {
result.add(new DecoratorBuildItem(KNATIVE, new ApplyContainerImageDecorator(name, i.getImage())));
@@ -294,6 +300,16 @@ public List createDecorators(ApplicationInfoBuildItem applic
result.add(new DecoratorBuildItem(new ApplyServiceAccountNameToRevisionSpecDecorator()));
}
+ //Handle Image Pull Secrets
+ config.getImagePullSecrets().ifPresent(imagePullSecrets -> {
+ String serviceAccountName = config.getServiceAccount().orElse(name);
+ result.add(new DecoratorBuildItem(KNATIVE, new AddServiceAccountResourceDecorator(name)));
+ result.add(
+ new DecoratorBuildItem(KNATIVE, new ApplyServiceAccountToRevisionSpecDecorator(name, serviceAccountName)));
+ result.add(new DecoratorBuildItem(KNATIVE,
+ new AddImagePullSecretToServiceAccountDecorator(serviceAccountName, imagePullSecrets)));
+ });
+
return result;
}
diff --git a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/KubernetesCommonHelper.java b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/KubernetesCommonHelper.java
index 20ca37f526a12..49e9304788a9d 100644
--- a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/KubernetesCommonHelper.java
+++ b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/KubernetesCommonHelper.java
@@ -183,15 +183,16 @@ public static List createDecorators(Optional projec
//Handle RBAC
if (!roleBindings.isEmpty()) {
- result.add(new DecoratorBuildItem(new ApplyServiceAccountNameDecorator()));
- result.add(new DecoratorBuildItem(new AddServiceAccountResourceDecorator()));
- roles.forEach(r -> result.add(new DecoratorBuildItem(new AddRoleResourceDecorator(name, r))));
+ result.add(new DecoratorBuildItem(target, new ApplyServiceAccountNameDecorator()));
+ result.add(new DecoratorBuildItem(target, new AddServiceAccountResourceDecorator()));
+ roles.forEach(r -> result.add(new DecoratorBuildItem(target, new AddRoleResourceDecorator(name, r))));
roleBindings.forEach(rb -> {
- result.add(new DecoratorBuildItem(new AddRoleBindingResourceDecorator(rb.getName(), null, rb.getRole(),
- rb.isClusterWide() ? AddRoleBindingResourceDecorator.RoleKind.ClusterRole
- : AddRoleBindingResourceDecorator.RoleKind.Role)));
+ result.add(new DecoratorBuildItem(target,
+ new AddRoleBindingResourceDecorator(rb.getName(), null, rb.getRole(),
+ rb.isClusterWide() ? AddRoleBindingResourceDecorator.RoleKind.ClusterRole
+ : AddRoleBindingResourceDecorator.RoleKind.Role)));
labels.forEach(l -> {
- result.add(new DecoratorBuildItem(
+ result.add(new DecoratorBuildItem(target,
new AddLabelDecorator(rb.getName(), l.getKey(), l.getValue(), "RoleBinding")));
});
});
diff --git a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/MinikubeManifestGenerator.java b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/MinikubeManifestGenerator.java
index 04887c821e6e2..657c8d9aa2f7b 100644
--- a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/MinikubeManifestGenerator.java
+++ b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/MinikubeManifestGenerator.java
@@ -21,7 +21,7 @@
import io.dekorate.kubernetes.decorator.AddServiceResourceDecorator;
import io.dekorate.kubernetes.decorator.ApplyHeadlessDecorator;
import io.dekorate.kubernetes.decorator.ApplyImageDecorator;
-import io.dekorate.kubernetes.decorator.ApplyReplicasDecorator;
+import io.dekorate.kubernetes.decorator.ApplyReplicasToDeploymentDecorator;
import io.dekorate.project.ApplyProjectInfo;
import io.dekorate.project.Project;
import io.dekorate.utils.Images;
@@ -48,11 +48,8 @@ public class MinikubeManifestGenerator extends AbstractKubernetesManifestGenerat
private static final String METADATA_NAMESPACE = "metadata.namespace";
private static final String MINIKUBE = "minikube";
- private final ConfigurationRegistry configurationRegistry;
-
public MinikubeManifestGenerator(ResourceRegistry resourceRegistry, ConfigurationRegistry configurationRegistry) {
- super(resourceRegistry);
- this.configurationRegistry = configurationRegistry;
+ super(resourceRegistry, configurationRegistry);
}
@Override
@@ -86,7 +83,9 @@ public void generate(KubernetesConfig config) {
}
if (config.getReplicas() != 1) {
- resourceRegistry.decorate(MINIKUBE, new ApplyReplicasDecorator(config.getName(), config.getReplicas()));
+ resourceRegistry.decorate(MINIKUBE, new ApplyReplicasToDeploymentDecorator(config.getName(), config.getReplicas()));
+ resourceRegistry.decorate(MINIKUBE,
+ new ApplyReplicasToStatefulSetDecorator(config.getName(), config.getReplicas()));
}
String image = Strings.isNotNullOrEmpty(imageConfig.getImage())
diff --git a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/OpenshiftProcessor.java b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/OpenshiftProcessor.java
index fce53fda3d200..389f4531ab484 100644
--- a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/OpenshiftProcessor.java
+++ b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/OpenshiftProcessor.java
@@ -25,7 +25,7 @@
import io.dekorate.kubernetes.decorator.ApplyImagePullPolicyDecorator;
import io.dekorate.kubernetes.decorator.RemoveFromSelectorDecorator;
import io.dekorate.kubernetes.decorator.RemoveLabelDecorator;
-import io.dekorate.openshift.decorator.ApplyReplicasDecorator;
+import io.dekorate.openshift.decorator.ApplyReplicasToDeploymentConfigDecorator;
import io.dekorate.project.Project;
import io.dekorate.s2i.config.S2iBuildConfig;
import io.dekorate.s2i.config.S2iBuildConfigBuilder;
@@ -175,9 +175,15 @@ public List createDecorators(ApplicationInfoBuildItem applic
Optional readinessPath,
List roles,
List roleBindings,
- Optional customProjectRoot) {
+ Optional customProjectRoot,
+ List targets) {
List result = new ArrayList<>();
+ if (!targets.stream().filter(KubernetesDeploymentTargetBuildItem::isEnabled)
+ .anyMatch(t -> OPENSHIFT.equals(t.getName()))) {
+ return result;
+ }
+
String name = ResourceNameUtil.getResourceName(config, applicationInfo);
Optional project = KubernetesCommonHelper.createProject(applicationInfo, customProjectRoot, outputTarget,
@@ -215,10 +221,11 @@ public List createDecorators(ApplicationInfoBuildItem applic
if (config.getReplicas() != 1) {
// This only affects DeploymentConfig
- result.add(new DecoratorBuildItem(OPENSHIFT, new ApplyReplicasDecorator(name, config.getReplicas())));
+ result.add(new DecoratorBuildItem(OPENSHIFT,
+ new ApplyReplicasToDeploymentConfigDecorator(name, config.getReplicas())));
// This only affects Deployment
result.add(new DecoratorBuildItem(OPENSHIFT,
- new io.dekorate.kubernetes.decorator.ApplyReplicasDecorator(name, config.getReplicas())));
+ new io.dekorate.kubernetes.decorator.ApplyReplicasToDeploymentDecorator(name, config.getReplicas())));
// This only affects StatefulSet
result.add(new DecoratorBuildItem(OPENSHIFT, new ApplyReplicasToStatefulSetDecorator(name, config.getReplicas())));
}
diff --git a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/VanillaKubernetesProcessor.java b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/VanillaKubernetesProcessor.java
index b409c1d991005..e0a8e31d9fce0 100644
--- a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/VanillaKubernetesProcessor.java
+++ b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/VanillaKubernetesProcessor.java
@@ -20,7 +20,8 @@
import io.dekorate.kubernetes.decorator.AddEnvVarDecorator;
import io.dekorate.kubernetes.decorator.ApplicationContainerDecorator;
import io.dekorate.kubernetes.decorator.ApplyImagePullPolicyDecorator;
-import io.dekorate.kubernetes.decorator.ApplyReplicasDecorator;
+import io.dekorate.kubernetes.decorator.ApplyReplicasToDeploymentDecorator;
+import io.dekorate.kubernetes.decorator.ApplyReplicasToStatefulSetDecorator;
import io.dekorate.kubernetes.decorator.RemoveFromMatchingLabelsDecorator;
import io.dekorate.kubernetes.decorator.RemoveFromSelectorDecorator;
import io.dekorate.kubernetes.decorator.RemoveLabelDecorator;
@@ -120,15 +121,19 @@ public List createDecorators(ApplicationInfoBuildItem applic
Optional image, Optional command,
List ports, Optional livenessPath,
Optional readinessPath, List roles,
- List roleBindings, Optional customProjectRoot) {
+ List roleBindings, Optional customProjectRoot,
+ List targets) {
final List result = new ArrayList<>();
+ if (!targets.stream().filter(KubernetesDeploymentTargetBuildItem::isEnabled)
+ .anyMatch(t -> KUBERNETES.equals(t.getName()))) {
+ return result;
+ }
final String name = ResourceNameUtil.getResourceName(config, applicationInfo);
Optional project = KubernetesCommonHelper.createProject(applicationInfo, customProjectRoot, outputTarget,
packageConfig);
- result.addAll(KubernetesCommonHelper.createDecorators(project, KUBERNETES, name, config,
- metricsConfiguration,
+ result.addAll(KubernetesCommonHelper.createDecorators(project, KUBERNETES, name, config, metricsConfiguration,
annotations, labels, command, ports, livenessPath, readinessPath, roles, roleBindings));
if (config.deploymentKind == KubernetesConfig.DeploymentResourceKind.StatefulSet) {
@@ -138,7 +143,7 @@ public List createDecorators(ApplicationInfoBuildItem applic
if (config.getReplicas() != 1) {
// This only affects Deployment
- result.add(new DecoratorBuildItem(KUBERNETES, new ApplyReplicasDecorator(name, config.getReplicas())));
+ result.add(new DecoratorBuildItem(KUBERNETES, new ApplyReplicasToDeploymentDecorator(name, config.getReplicas())));
// This only affects StatefulSet
result.add(new DecoratorBuildItem(KUBERNETES, new ApplyReplicasToStatefulSetDecorator(name, config.getReplicas())));
}
diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/customproviders/PreMatchingHeadersFilterTest.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/customproviders/PreMatchingHeadersFilterTest.java
new file mode 100644
index 0000000000000..25f203132763f
--- /dev/null
+++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/customproviders/PreMatchingHeadersFilterTest.java
@@ -0,0 +1,66 @@
+package io.quarkus.resteasy.reactive.server.test.customproviders;
+
+import static io.restassured.RestAssured.given;
+import static org.hamcrest.Matchers.equalTo;
+
+import java.util.List;
+import java.util.function.Supplier;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.MediaType;
+
+import org.jboss.resteasy.reactive.server.ServerRequestFilter;
+import org.jboss.shrinkwrap.api.ShrinkWrap;
+import org.jboss.shrinkwrap.api.spec.JavaArchive;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.RegisterExtension;
+
+import io.quarkus.test.QuarkusUnitTest;
+
+public class PreMatchingHeadersFilterTest {
+
+ @RegisterExtension
+ static QuarkusUnitTest test = new QuarkusUnitTest()
+ .setArchiveProducer(new Supplier<>() {
+ @Override
+ public JavaArchive get() {
+ return ShrinkWrap.create(JavaArchive.class)
+ .addClasses(Resource.class);
+ }
+ });
+
+ @Test
+ public void testJsonHeaderAdded() {
+ given()
+ .body("{\"foo\": \"bar\"}")
+ .when()
+ .post("/test")
+ .then()
+ .statusCode(200)
+ .body("foo", equalTo("bar"));
+ }
+
+ @Path("test")
+ public static class Resource {
+
+ @POST
+ @Consumes("application/json")
+ @Produces("application/json")
+ public String post(String json) {
+ return json;
+ }
+ }
+
+ public static class Filters {
+ @ServerRequestFilter(preMatching = true)
+ public void preMatchingFilter(ContainerRequestContext requestContext) {
+ // without this, RR would respond with HTTP 415
+ requestContext.getHeaders().put(HttpHeaders.CONTENT_TYPE, List.of(MediaType.APPLICATION_JSON));
+ }
+ }
+}
diff --git a/extensions/resteasy-reactive/rest-client-reactive/deployment/src/test/java/io/quarkus/rest/client/reactive/ArrayPairsQueryParamTest.java b/extensions/resteasy-reactive/rest-client-reactive/deployment/src/test/java/io/quarkus/rest/client/reactive/ArrayPairsQueryParamTest.java
new file mode 100644
index 0000000000000..19c285979b981
--- /dev/null
+++ b/extensions/resteasy-reactive/rest-client-reactive/deployment/src/test/java/io/quarkus/rest/client/reactive/ArrayPairsQueryParamTest.java
@@ -0,0 +1,95 @@
+package io.quarkus.rest.client.reactive;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.net.URI;
+import java.util.List;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+import javax.inject.Inject;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.QueryParam;
+
+import org.eclipse.microprofile.rest.client.RestClientBuilder;
+import org.eclipse.microprofile.rest.client.ext.QueryParamStyle;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.RegisterExtension;
+
+import io.quarkus.test.QuarkusUnitTest;
+import io.vertx.core.Vertx;
+import io.vertx.core.http.HttpServer;
+
+public class ArrayPairsQueryParamTest {
+ @RegisterExtension
+ static final QuarkusUnitTest TEST = new QuarkusUnitTest();
+
+ @Inject
+ Vertx vertx;
+
+ Client client;
+ HttpServer server;
+
+ @BeforeEach
+ void setUp() throws ExecutionException, InterruptedException {
+ setupServer();
+
+ client = createClient();
+ }
+
+ @AfterEach
+ void shutDown() throws IOException, ExecutionException, InterruptedException, TimeoutException {
+ server.close().toCompletionStage().toCompletableFuture().get(5, TimeUnit.SECONDS);
+
+ ((Closeable) client).close();
+ }
+
+ @Test
+ void shouldPassMultiParamWithBrackets() {
+ String response = client.getWithParams(List.of("one", "two"));
+
+ assertThat(response).contains("my-param[]=one").contains("my-param[]=two");
+ }
+
+ @Test
+ void shouldPassSingleParamWithoutBrackets() {
+ String response = client.getWithParams(List.of("one"));
+
+ assertThat(response).isEqualTo("my-param=one");
+ }
+
+ @Path("/")
+ public interface Client {
+
+ @GET
+ String getWithParams(@QueryParam("my-param") List paramValues);
+ }
+
+ private void setupServer() throws InterruptedException, ExecutionException {
+ CompletableFuture startResult = new CompletableFuture<>();
+ vertx.createHttpServer().requestHandler(request -> request.response().setStatusCode(200).end(request.query()))
+ .listen(8082)
+ .onComplete(server -> {
+ if (server.failed()) {
+ startResult.completeExceptionally(server.cause());
+ } else {
+ startResult.complete(server.result());
+ }
+ });
+ server = startResult.get();
+ }
+
+ private Client createClient() {
+ return RestClientBuilder.newBuilder()
+ .queryParamStyle(QueryParamStyle.ARRAY_PAIRS)
+ .baseUri(URI.create("http://localhost:8082"))
+ .build(Client.class);
+ }
+}
diff --git a/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/devmode/console/DevConsoleProcessor.java b/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/devmode/console/DevConsoleProcessor.java
index 596da0894df60..52af24d337a7d 100644
--- a/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/devmode/console/DevConsoleProcessor.java
+++ b/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/devmode/console/DevConsoleProcessor.java
@@ -100,6 +100,7 @@
import io.quarkus.vertx.http.runtime.devmode.RuntimeDevConsoleRoute;
import io.quarkus.vertx.http.runtime.logstream.LogStreamRecorder;
import io.quarkus.vertx.http.runtime.logstream.WebSocketLogHandler;
+import io.smallrye.common.vertx.VertxContext;
import io.vertx.core.Handler;
import io.vertx.core.Vertx;
import io.vertx.core.VertxOptions;
@@ -107,6 +108,7 @@
import io.vertx.core.http.HttpServerOptions;
import io.vertx.core.http.HttpServerRequest;
import io.vertx.core.http.impl.Http1xServerConnection;
+import io.vertx.core.impl.ContextInternal;
import io.vertx.core.impl.EventLoopContext;
import io.vertx.core.impl.VertxBuilder;
import io.vertx.core.impl.VertxInternal;
@@ -175,27 +177,15 @@ public void initChannel(VirtualChannel ch) throws Exception {
// Vert.x 4 Migration: Verify this behavior
EventLoopContext context = vertx.createEventLoopContext();
- // ContextInternal context = (ContextInternal) vertx
- // .createEventLoopContext(null, null, new JsonObject(),
- // Thread.currentThread().getContextClassLoader());
VertxHandler handler = VertxHandler.create(chctx -> {
Http1xServerConnection connection = new Http1xServerConnection(
- () -> context,
+ () -> (ContextInternal) VertxContext.getOrCreateDuplicatedContext(context),
null,
new HttpServerOptions(),
chctx,
context,
"localhost",
null);
-
- // Http1xServerConnection conn = new Http1xServerConnection(
- // context.owner(),
- // null,
- // new HttpServerOptions(),
- // chctx,
- // context,
- // "localhost",
- // null);
connection.handler(new Handler() {
@Override
public void handle(HttpServerRequest event) {
@@ -269,6 +259,7 @@ public void handle(RoutingContext event) {
};
router = Router.router(devConsoleVertx);
router.errorHandler(500, errorHandler);
+
router.route()
.order(Integer.MIN_VALUE)
.handler(new FlashScopeHandler());
@@ -508,22 +499,30 @@ public static void openBrowser(HttpRootPathBuildItem rp, NonApplicationRootPathB
Runtime rt = Runtime.getRuntime();
OS os = OS.determineOS();
+ String[] command = null;
try {
switch (os) {
case MAC:
- rt.exec(new String[] { "open", url });
+ command = new String[] { "open", url };
break;
case LINUX:
- rt.exec(new String[] { "xdg-open", url });
+ command = new String[] { "xdg-open", url };
break;
case WINDOWS:
- rt.exec(new String[] { "rundll32", "url.dll,FileProtocolHandler", url });
+ command = new String[] { "rundll32", "url.dll,FileProtocolHandler", url };
break;
case OTHER:
log.error("Cannot launch browser on this operating system");
}
+ if (command != null) {
+ rt.exec(command);
+ }
} catch (Exception e) {
- log.error("Failed to launch browser", e);
+ log.debug("Failed to launch browser", e);
+ if (command != null) {
+ log.warn("Unable to open browser using command: '" + String.join(" ", command) + "'. Failure is: '"
+ + e.getMessage() + "'");
+ }
}
}
diff --git a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/VertxHttpRecorder.java b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/VertxHttpRecorder.java
index cfb398cc681d5..fca78e6a0837c 100644
--- a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/VertxHttpRecorder.java
+++ b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/VertxHttpRecorder.java
@@ -891,6 +891,8 @@ private static HttpServerOptions createDomainSocketOptions(HttpConfiguration htt
HttpServerOptions options = new HttpServerOptions();
applyCommonOptions(options, httpConfiguration, websocketSubProtocols);
+ // Override the host (0.0.0.0 by default) with the configured domain socket.
+ options.setHost(httpConfiguration.domainSocket);
return options;
}
@@ -901,14 +903,6 @@ private static void setIdleTimeout(HttpConfiguration httpConfiguration, HttpServ
options.setIdleTimeoutUnit(TimeUnit.MILLISECONDS);
}
- public void warnIfPortChanged(HttpConfiguration config, int port) {
- if (config.port != port) {
- LOGGER.errorf(
- "quarkus.http.port was specified at build time as %s however run time value is %s, Kubernetes metadata will be incorrect.",
- port, config.port);
- }
- }
-
public void addRoute(RuntimeValue router, Function route, Handler handler,
HandlerType blocking) {
diff --git a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/devmode/DevConsoleFilter.java b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/devmode/DevConsoleFilter.java
index 4a870d1383fc1..a565c2d1f7d7c 100644
--- a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/devmode/DevConsoleFilter.java
+++ b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/devmode/DevConsoleFilter.java
@@ -34,6 +34,7 @@ public void handle(RoutingContext event) {
for (Map.Entry entry : event.request().headers()) {
headers.put(entry.getKey(), event.request().headers().getAll(entry.getKey()));
}
+ event.request().resume();
if (event.getBody() != null) {
DevConsoleRequest request = new DevConsoleRequest(event.request().method().name(), event.request().uri(), headers,
event.getBody().getBytes());
diff --git a/independent-projects/resteasy-reactive/common/runtime/src/main/java/org/jboss/resteasy/reactive/common/jaxrs/UriBuilderImpl.java b/independent-projects/resteasy-reactive/common/runtime/src/main/java/org/jboss/resteasy/reactive/common/jaxrs/UriBuilderImpl.java
index d9d20a87e55ae..dff2c2d4eedb5 100644
--- a/independent-projects/resteasy-reactive/common/runtime/src/main/java/org/jboss/resteasy/reactive/common/jaxrs/UriBuilderImpl.java
+++ b/independent-projects/resteasy-reactive/common/runtime/src/main/java/org/jboss/resteasy/reactive/common/jaxrs/UriBuilderImpl.java
@@ -900,7 +900,8 @@ public UriBuilder clientQueryParam(String name, Object... values) throws Illegal
break;
case ARRAY_PAIRS:
prefix = "&";
- sb.append(Encode.encodeQueryParamAsIs(name)).append("[]=")
+ String queryParamConnector = arrayPairsConnector(values);
+ sb.append(Encode.encodeQueryParamAsIs(name)).append(queryParamConnector)
.append(Encode.encodeQueryParamAsIs(value.toString()));
break;
}
@@ -943,7 +944,9 @@ public UriBuilder queryParam(String name, Object... values) throws IllegalArgume
break;
case ARRAY_PAIRS:
prefix = "&";
- sb.append(Encode.encodeQueryParam(name)).append("[]=").append(Encode.encodeQueryParam(value.toString()));
+ String queryParamConnector = arrayPairsConnector(values);
+ sb.append(Encode.encodeQueryParam(name)).append(queryParamConnector)
+ .append(Encode.encodeQueryParam(value.toString()));
break;
}
}
@@ -952,6 +955,10 @@ public UriBuilder queryParam(String name, Object... values) throws IllegalArgume
return this;
}
+ private String arrayPairsConnector(Object[] values) {
+ return values.length == 1 ? "=" : "[]=";
+ }
+
public UriBuilder replaceQueryParam(String name, Object... values) throws IllegalArgumentException {
if (name == null)
throw new IllegalArgumentException("Name parameter is null");
diff --git a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/handlers/ClassRoutingHandler.java b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/handlers/ClassRoutingHandler.java
index 05296f9e0cbbc..e64039a8659e9 100644
--- a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/handlers/ClassRoutingHandler.java
+++ b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/handlers/ClassRoutingHandler.java
@@ -109,7 +109,7 @@ public void handle(ResteasyReactiveRequestContext requestContext) throws Excepti
// according to the spec we need to return HTTP 415 when content-type header doesn't match what is specified in @Consumes
if (!target.value.getConsumes().isEmpty()) {
- String contentType = serverRequest.getRequestHeader(HttpHeaders.CONTENT_TYPE);
+ String contentType = (String) requestContext.getHeader(HttpHeaders.CONTENT_TYPE, true);
if (contentType != null) {
try {
if (MediaTypeHelper.getFirstMatch(
@@ -126,7 +126,7 @@ public void handle(ResteasyReactiveRequestContext requestContext) throws Excepti
if (target.value.getProduces() != null) {
// there could potentially be multiple Accept headers and we need to response with 406
// if none match the method's @Produces
- List accepts = serverRequest.getAllRequestHeaders(HttpHeaders.ACCEPT);
+ List accepts = (List) requestContext.getHeader(HttpHeaders.ACCEPT, false);
if (!accepts.isEmpty()) {
boolean hasAtLeastOneMatch = false;
for (int i = 0; i < accepts.size(); i++) {
diff --git a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/handlers/FixedProducesHandler.java b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/handlers/FixedProducesHandler.java
index 0626066d3c823..503ecb988190b 100644
--- a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/handlers/FixedProducesHandler.java
+++ b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/handlers/FixedProducesHandler.java
@@ -32,7 +32,7 @@ public FixedProducesHandler(MediaType mediaType, EntityWriter writer) {
@Override
public void handle(ResteasyReactiveRequestContext requestContext) throws Exception {
- List acceptValues = requestContext.serverRequest().getAllRequestHeaders(HttpHeaders.ACCEPT);
+ List acceptValues = (List) requestContext.getHeader(HttpHeaders.ACCEPT, false);
if (acceptValues.isEmpty()) {
requestContext.setResponseContentType(mediaType);
requestContext.setEntityWriter(writer);
diff --git a/independent-projects/tools/base-codestarts/src/main/resources/codestarts/quarkus/buildtool/gradle-kotlin-dsl/kotlin/build.tpl.qute.gradle.kts b/independent-projects/tools/base-codestarts/src/main/resources/codestarts/quarkus/buildtool/gradle-kotlin-dsl/kotlin/build.tpl.qute.gradle.kts
index a90e0705a1086..044ac95a86bd6 100644
--- a/independent-projects/tools/base-codestarts/src/main/resources/codestarts/quarkus/buildtool/gradle-kotlin-dsl/kotlin/build.tpl.qute.gradle.kts
+++ b/independent-projects/tools/base-codestarts/src/main/resources/codestarts/quarkus/buildtool/gradle-kotlin-dsl/kotlin/build.tpl.qute.gradle.kts
@@ -15,10 +15,6 @@ allOpen {
}
tasks.withType {
- {#if java.version == "11"}
- kotlinOptions.jvmTarget = JavaVersion.VERSION_11.toString()
- {#else}
- kotlinOptions.jvmTarget = JavaVersion.VERSION_1_8.toString()
- {/if}
+ kotlinOptions.jvmTarget = JavaVersion.VERSION_{java.version}.toString()
kotlinOptions.javaParameters = true
}
diff --git a/independent-projects/tools/base-codestarts/src/main/resources/codestarts/quarkus/buildtool/gradle/kotlin/build.tpl.qute.gradle b/independent-projects/tools/base-codestarts/src/main/resources/codestarts/quarkus/buildtool/gradle/kotlin/build.tpl.qute.gradle
index 2a7e4b6591428..6c51b5039f488 100644
--- a/independent-projects/tools/base-codestarts/src/main/resources/codestarts/quarkus/buildtool/gradle/kotlin/build.tpl.qute.gradle
+++ b/independent-projects/tools/base-codestarts/src/main/resources/codestarts/quarkus/buildtool/gradle/kotlin/build.tpl.qute.gradle
@@ -15,10 +15,10 @@ allOpen {
}
compileKotlin {
- kotlinOptions.jvmTarget = JavaVersion.VERSION_11
+ kotlinOptions.jvmTarget = JavaVersion.VERSION_{java.version}
kotlinOptions.javaParameters = true
}
compileTestKotlin {
- kotlinOptions.jvmTarget = JavaVersion.VERSION_11
+ kotlinOptions.jvmTarget = JavaVersion.VERSION_{java.version}
}
diff --git a/independent-projects/tools/base-codestarts/src/main/resources/codestarts/quarkus/buildtool/maven/kotlin/pom.tpl.qute.xml b/independent-projects/tools/base-codestarts/src/main/resources/codestarts/quarkus/buildtool/maven/kotlin/pom.tpl.qute.xml
index e4b6c73d62bc7..303e11409fcc7 100644
--- a/independent-projects/tools/base-codestarts/src/main/resources/codestarts/quarkus/buildtool/maven/kotlin/pom.tpl.qute.xml
+++ b/independent-projects/tools/base-codestarts/src/main/resources/codestarts/quarkus/buildtool/maven/kotlin/pom.tpl.qute.xml
@@ -40,7 +40,7 @@
true
- 11
+ {java.version}
all-open
diff --git a/independent-projects/tools/base-codestarts/src/main/resources/codestarts/quarkus/extension-codestarts/spring-web-codestart/codestart.yml b/independent-projects/tools/base-codestarts/src/main/resources/codestarts/quarkus/extension-codestarts/spring-web-codestart/codestart.yml
index 199ac0b1952e6..33e2454f8eab0 100644
--- a/independent-projects/tools/base-codestarts/src/main/resources/codestarts/quarkus/extension-codestarts/spring-web-codestart/codestart.yml
+++ b/independent-projects/tools/base-codestarts/src/main/resources/codestarts/quarkus/extension-codestarts/spring-web-codestart/codestart.yml
@@ -16,6 +16,6 @@ language:
package-name: org.acme
dependencies:
- io.quarkus:quarkus-spring-web
- - io.quarkus:quarkus-resteasy-jackson
+ - io.quarkus:quarkus-resteasy-reactive-jackson
test-dependencies:
- io.rest-assured:rest-assured
diff --git a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/codegen/CreateProjectHelper.java b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/codegen/CreateProjectHelper.java
index a8371290c7534..8fd67e16cc071 100644
--- a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/codegen/CreateProjectHelper.java
+++ b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/codegen/CreateProjectHelper.java
@@ -25,6 +25,7 @@ public class CreateProjectHelper {
// ordering is important here, so let's keep them ordered
public static final SortedSet JAVA_VERSIONS_LTS = new TreeSet<>(List.of(11, 17));
private static final int DEFAULT_JAVA_VERSION = 11;
+ private static final int MAX_LTS_SUPPORTED_BY_KOTLIN = 17;
public static final String DETECT_JAVA_RUNTIME_VERSION = "<>";
private static final Pattern JAVA_VERSION_PATTERN = Pattern.compile("(\\d+)(?:\\..*)?");
@@ -98,7 +99,14 @@ public static void setJavaVersion(Map values, String javaTarget)
javaFeatureVersionTarget = Runtime.version().feature();
}
- values.put(ProjectGenerator.JAVA_TARGET, String.valueOf(determineBestJavaLtsVersion(javaFeatureVersionTarget)));
+ int bestJavaLtsVersion = determineBestJavaLtsVersion(javaFeatureVersionTarget);
+
+ if (SourceType.KOTLIN.equals(values.get(ProjectGenerator.SOURCE_TYPE))
+ && bestJavaLtsVersion > MAX_LTS_SUPPORTED_BY_KOTLIN) {
+ bestJavaLtsVersion = MAX_LTS_SUPPORTED_BY_KOTLIN;
+ }
+
+ values.put(ProjectGenerator.JAVA_TARGET, String.valueOf(bestJavaLtsVersion));
}
public static int determineBestJavaLtsVersion() {
diff --git a/independent-projects/tools/devtools-common/src/test/java/io/quarkus/devtools/project/codegen/CreateProjectHelperTest.java b/independent-projects/tools/devtools-common/src/test/java/io/quarkus/devtools/project/codegen/CreateProjectHelperTest.java
index 8e0dc592e7c65..9cacd513b3e15 100644
--- a/independent-projects/tools/devtools-common/src/test/java/io/quarkus/devtools/project/codegen/CreateProjectHelperTest.java
+++ b/independent-projects/tools/devtools-common/src/test/java/io/quarkus/devtools/project/codegen/CreateProjectHelperTest.java
@@ -62,4 +62,13 @@ public void testDetermineBestLtsVersion() {
assertEquals(17, CreateProjectHelper.determineBestJavaLtsVersion(17));
assertEquals(17, CreateProjectHelper.determineBestJavaLtsVersion(18));
}
+
+ @Test
+ public void givenKotlinProjectWithVersion18ShouldReturn17() {
+ Map values = new HashMap<>();
+ values.put(ProjectGenerator.SOURCE_TYPE, SourceType.KOTLIN);
+
+ CreateProjectHelper.setJavaVersion(values, "18");
+ assertEquals("17", values.get("java_target"));
+ }
}
diff --git a/integration-tests/kubernetes/quarkus-standard-way/src/test/java/io/quarkus/it/kubernetes/KnativeContainerImageTest.java b/integration-tests/kubernetes/quarkus-standard-way/src/test/java/io/quarkus/it/kubernetes/KnativeContainerImageTest.java
index 159e2bf9e4f80..83df7a354eadd 100644
--- a/integration-tests/kubernetes/quarkus-standard-way/src/test/java/io/quarkus/it/kubernetes/KnativeContainerImageTest.java
+++ b/integration-tests/kubernetes/quarkus-standard-way/src/test/java/io/quarkus/it/kubernetes/KnativeContainerImageTest.java
@@ -31,6 +31,7 @@ public class KnativeContainerImageTest {
@Test
public void assertGeneratedResources() throws IOException {
Path kubernetesDir = prodModeTestResults.getBuildDir().resolve("kubernetes");
+
assertThat(kubernetesDir)
.isDirectoryContaining(p -> p.getFileName().endsWith("knative.json"))
.isDirectoryContaining(p -> p.getFileName().endsWith("knative.yml"));
diff --git a/integration-tests/kubernetes/quarkus-standard-way/src/test/java/io/quarkus/it/kubernetes/KnativeWithImagePullSecretTest.java b/integration-tests/kubernetes/quarkus-standard-way/src/test/java/io/quarkus/it/kubernetes/KnativeWithImagePullSecretTest.java
new file mode 100644
index 0000000000000..4836b59f0cf1c
--- /dev/null
+++ b/integration-tests/kubernetes/quarkus-standard-way/src/test/java/io/quarkus/it/kubernetes/KnativeWithImagePullSecretTest.java
@@ -0,0 +1,73 @@
+package io.quarkus.it.kubernetes;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.Collections;
+import java.util.List;
+
+import org.jboss.shrinkwrap.api.ShrinkWrap;
+import org.jboss.shrinkwrap.api.spec.JavaArchive;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.RegisterExtension;
+
+import io.fabric8.knative.serving.v1.Service;
+import io.fabric8.kubernetes.api.model.HasMetadata;
+import io.fabric8.kubernetes.api.model.ServiceAccount;
+import io.quarkus.bootstrap.model.AppArtifact;
+import io.quarkus.builder.Version;
+import io.quarkus.test.ProdBuildResults;
+import io.quarkus.test.ProdModeTestResults;
+import io.quarkus.test.QuarkusProdModeTest;
+
+public class KnativeWithImagePullSecretTest {
+
+ @RegisterExtension
+ static final QuarkusProdModeTest config = new QuarkusProdModeTest()
+ .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class).addClasses(GreetingResource.class))
+ .setApplicationName("knative-with-image-pull-secret")
+ .setApplicationVersion("0.1-SNAPSHOT")
+ .withConfigurationResource("knative-with-image-pull-secret.properties")
+ .setLogFileName("k8s.log")
+ .setForcedDependencies(
+ Collections.singletonList(new AppArtifact("io.quarkus", "quarkus-kubernetes", 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("knative.json"))
+ .isDirectoryContaining(p -> p.getFileName().endsWith("knative.yml"));
+
+ List kubernetesList = DeserializationUtil
+ .deserializeAsList(kubernetesDir.resolve("knative.yml"));
+
+ assertThat(kubernetesList).filteredOn(i -> "Service".equals(i.getKind())).singleElement().satisfies(i -> {
+ assertThat(i).isInstanceOfSatisfying(Service.class, s -> {
+ assertThat(s.getMetadata()).satisfies(m -> {
+ assertThat(m.getName()).isEqualTo("knative-with-image-pull-secret");
+ });
+
+ assertThat(s.getSpec()).satisfies(serviceSpec -> {
+ assertThat(serviceSpec.getTemplate()).satisfies(t -> {
+ assertThat(t.getSpec()).satisfies(r -> {
+ assertThat(r.getServiceAccountName()).isEqualTo("knative-with-image-pull-secret");
+ });
+ });
+ });
+ });
+ });
+
+ assertThat(kubernetesList).filteredOn(i -> "ServiceAccount".equals(i.getKind())).singleElement().satisfies(i -> {
+ assertThat(i).isInstanceOfSatisfying(ServiceAccount.class, s -> {
+ assertTrue(s.getImagePullSecrets().stream().anyMatch(r -> r.getName().equals("my-secret")));
+ });
+ });
+ }
+
+}
diff --git a/integration-tests/kubernetes/quarkus-standard-way/src/test/resources/knative-with-image-pull-secret.properties b/integration-tests/kubernetes/quarkus-standard-way/src/test/resources/knative-with-image-pull-secret.properties
new file mode 100644
index 0000000000000..12167dafaf894
--- /dev/null
+++ b/integration-tests/kubernetes/quarkus-standard-way/src/test/resources/knative-with-image-pull-secret.properties
@@ -0,0 +1,2 @@
+quarkus.kubernetes.deployment-target=knative
+quarkus.knative.image-pull-secrets=my-secret
\ No newline at end of file
diff --git a/integration-tests/locales/src/test/java/io/quarkus/locales/it/LocalesIT.java b/integration-tests/locales/src/test/java/io/quarkus/locales/it/LocalesIT.java
index 69e015e51da41..90228c7ac399c 100644
--- a/integration-tests/locales/src/test/java/io/quarkus/locales/it/LocalesIT.java
+++ b/integration-tests/locales/src/test/java/io/quarkus/locales/it/LocalesIT.java
@@ -11,6 +11,15 @@
import io.quarkus.test.junit.NativeImageTest;
import io.restassured.RestAssured;
+/**
+ * For the Native test cases to function, the operating system has to have locales
+ * support installed. A barebone system with only C.UTF-8 default locale available
+ * won't be able to pass the tests.
+ *
+ * For example, this package satisfies the dependency on a RHEL 9 type of OS:
+ * glibc-all-langpacks
+ *
+ */
@NativeImageTest
public class LocalesIT {
diff --git a/integration-tests/main/src/test/java/io/quarkus/it/main/QuarkusTestNestedWithTestProfileTestCase.java b/integration-tests/main/src/test/java/io/quarkus/it/main/QuarkusTestNestedWithTestProfileTestCase.java
new file mode 100644
index 0000000000000..ea34887a78915
--- /dev/null
+++ b/integration-tests/main/src/test/java/io/quarkus/it/main/QuarkusTestNestedWithTestProfileTestCase.java
@@ -0,0 +1,83 @@
+package io.quarkus.it.main;
+
+import static org.hamcrest.Matchers.is;
+
+import java.util.Collections;
+import java.util.Map;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Tag;
+import org.junit.jupiter.api.Test;
+
+import io.quarkus.test.junit.QuarkusTest;
+import io.quarkus.test.junit.QuarkusTestProfile;
+import io.quarkus.test.junit.TestProfile;
+import io.restassured.RestAssured;
+
+@QuarkusTest
+@Tag("nested")
+@TestProfile(QuarkusTestNestedWithTestProfileTestCase.OuterProfile.class)
+public class QuarkusTestNestedWithTestProfileTestCase {
+
+ private static final int TEST_PORT_FROM_PROFILE = 7777;
+
+ @Nested
+ class NestedCase {
+
+ @Test
+ void testProfileFromNested() {
+ Assertions.assertEquals(TEST_PORT_FROM_PROFILE, RestAssured.port);
+ RestAssured.when()
+ .get("/greeting/Stu")
+ .then()
+ .statusCode(200)
+ .body(is("OuterProfile Stu"));
+ }
+ }
+
+ @Nested
+ @TestProfile(QuarkusTestNestedWithTestProfileTestCase.ModernEnglishProfile.class)
+ class ModernEnglishCase {
+
+ @Test
+ void testProfileFromNested() {
+ RestAssured.when()
+ .get("/greeting/Stu")
+ .then()
+ .statusCode(200)
+ .body(is("Hey Stu"));
+ }
+ }
+
+ public static class OuterProfile implements QuarkusTestProfile {
+
+ @Override
+ public Map getConfigOverrides() {
+ return Collections.singletonMap("quarkus.http.test-port", "" + TEST_PORT_FROM_PROFILE);
+ }
+
+ @Override
+ public String[] commandLineParameters() {
+ return new String[] { "OuterProfile" };
+ }
+
+ @Override
+ public boolean runMainMethod() {
+ return true;
+ }
+ }
+
+ public static class ModernEnglishProfile implements QuarkusTestProfile {
+
+ @Override
+ public String[] commandLineParameters() {
+ return new String[] { "Hey" };
+ }
+
+ @Override
+ public boolean runMainMethod() {
+ return true;
+ }
+ }
+}
diff --git a/integration-tests/main/src/test/java/io/quarkus/it/main/RestClientTestCase.java b/integration-tests/main/src/test/java/io/quarkus/it/main/RestClientTestCase.java
index 268573e938fab..d5f41f180751b 100644
--- a/integration-tests/main/src/test/java/io/quarkus/it/main/RestClientTestCase.java
+++ b/integration-tests/main/src/test/java/io/quarkus/it/main/RestClientTestCase.java
@@ -47,6 +47,14 @@ public void testMicroprofileClientCDIIntegration() {
.body(is("TEST"));
}
+ /**
+ * This test in Native won't work on a barebone system,
+ * just with C.UTF-8 default fallback locale.
+ *
+ * For example, this package satisfies the dependency on a RHEL 9 type of OS:
+ * glibc-all-langpacks
+ *
+ */
@DisabledOnOs(OS.WINDOWS)
@Test
public void testEmojis() {
diff --git a/integration-tests/reactive-messaging-rabbitmq-dyn/src/test/java/io/quarkus/it/rabbitmq/RabbitMQConnectorDynCredsTest.java b/integration-tests/reactive-messaging-rabbitmq-dyn/src/test/java/io/quarkus/it/rabbitmq/RabbitMQConnectorDynCredsTest.java
index dc3746a31717a..1f810f34b1701 100644
--- a/integration-tests/reactive-messaging-rabbitmq-dyn/src/test/java/io/quarkus/it/rabbitmq/RabbitMQConnectorDynCredsTest.java
+++ b/integration-tests/reactive-messaging-rabbitmq-dyn/src/test/java/io/quarkus/it/rabbitmq/RabbitMQConnectorDynCredsTest.java
@@ -7,11 +7,11 @@
import java.util.List;
import java.util.Map;
+import org.apache.commons.lang3.RandomStringUtils;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.testcontainers.containers.Network;
import org.testcontainers.containers.RabbitMQContainer;
-import org.testcontainers.shaded.org.apache.commons.lang.RandomStringUtils;
import org.testcontainers.utility.DockerImageName;
import io.quarkus.it.rabbitmq.RabbitMQConnectorDynCredsTest.VaultResource;
diff --git a/test-framework/junit5/src/main/java/io/quarkus/test/junit/AbstractJvmQuarkusTestExtension.java b/test-framework/junit5/src/main/java/io/quarkus/test/junit/AbstractJvmQuarkusTestExtension.java
index 970242d209c98..3ec62a44e2ea8 100644
--- a/test-framework/junit5/src/main/java/io/quarkus/test/junit/AbstractJvmQuarkusTestExtension.java
+++ b/test-framework/junit5/src/main/java/io/quarkus/test/junit/AbstractJvmQuarkusTestExtension.java
@@ -184,12 +184,17 @@ protected PrepareResult createAugmentor(ExtensionContext context, Class extend
}
protected Class extends QuarkusTestProfile> getQuarkusTestProfile(ExtensionContext extensionContext) {
- TestProfile annotation = extensionContext.getRequiredTestClass().getAnnotation(TestProfile.class);
- Class extends QuarkusTestProfile> selectedProfile = null;
- if (annotation != null) {
- selectedProfile = annotation.value();
+ Class> testClass = extensionContext.getRequiredTestClass();
+ while (testClass != null) {
+ TestProfile annotation = testClass.getAnnotation(TestProfile.class);
+ if (annotation != null) {
+ return annotation.value();
+ }
+
+ testClass = testClass.getEnclosingClass();
}
- return selectedProfile;
+
+ return null;
}
protected static boolean hasPerTestResources(ExtensionContext extensionContext) {