runTimeConfigBuilderProducer) {
+ runTimeConfigBuilderProducer.produce(new RunTimeConfigBuilderBuildItem(OtlpExporterConfigBuilder.class));
+ }
+
@SuppressWarnings("deprecation")
@BuildStep(onlyIf = OtlpExporterProcessor.OtlpTracingExporterEnabled.class)
@Record(ExecutionTime.RUNTIME_INIT)
diff --git a/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/config/OpenTelemetryConfigBuilderCustomizer.java b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/config/runtime/exporter/OtlpExporterConfigBuilder.java
similarity index 72%
rename from extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/config/OpenTelemetryConfigBuilderCustomizer.java
rename to extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/config/runtime/exporter/OtlpExporterConfigBuilder.java
index 07a7148fa0e72..2f67052b210fe 100644
--- a/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/config/OpenTelemetryConfigBuilderCustomizer.java
+++ b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/config/runtime/exporter/OtlpExporterConfigBuilder.java
@@ -1,4 +1,4 @@
-package io.quarkus.opentelemetry.runtime.config;
+package io.quarkus.opentelemetry.runtime.config.runtime.exporter;
import static io.quarkus.opentelemetry.runtime.config.runtime.exporter.OtlpExporterConfig.DEFAULT_GRPC_BASE_URI;
import static io.quarkus.opentelemetry.runtime.config.runtime.exporter.OtlpExporterConfig.Protocol.GRPC;
@@ -6,13 +6,29 @@
import java.util.HashMap;
import java.util.Map;
+import io.quarkus.runtime.configuration.ConfigBuilder;
import io.smallrye.config.FallbackConfigSourceInterceptor;
import io.smallrye.config.SmallRyeConfigBuilder;
-import io.smallrye.config.SmallRyeConfigBuilderCustomizer;
-public class OpenTelemetryConfigBuilderCustomizer implements SmallRyeConfigBuilderCustomizer {
+/**
+ * Adds fallbacks to {@link OtlpExporterRuntimeConfig#traces()} and {@link OtlpExporterRuntimeConfig#metrics()} from
+ * the base configuration {@link OtlpExporterRuntimeConfig}. The goal is to fallback specific properties from either
+ * tracer
or metrics
to their parent configuration. For instance, if there is no value for
+ * quarkus.otel.exporter.otlp.traces.endpoint
it fallbacks to
+ * quarkus.otel.exporter.otlp.endpoint
.
+ *
+ * Defaults are set using the {@link SmallRyeConfigBuilder#withDefaultValue(String, String)}, instead of
+ * {@link io.smallrye.config.WithDefault} because the mapping {@link OtlpExporterConfig} is shared between base
+ * (unnamed), traces
, and metrics
, which means that every path would always have a default,
+ * and it won't be possible to fallback from the specific configuration to the base configuration.
+ *
+ * This builder is only set to customize the runtime configuration since the mapping {@link OtlpExporterRuntimeConfig}
+ * is only for runtime. The builder executes with a very high priority to ensure that it sets the default
+ * configuration early in the config setup to allow other configurations to override it (like Dev Services).
+ */
+public class OtlpExporterConfigBuilder implements ConfigBuilder {
@Override
- public void configBuilder(final SmallRyeConfigBuilder builder) {
+ public SmallRyeConfigBuilder configBuilder(final SmallRyeConfigBuilder builder) {
// Main defaults
builder.withDefaultValue("quarkus.otel.exporter.otlp.endpoint", DEFAULT_GRPC_BASE_URI);
builder.withDefaultValue("quarkus.otel.exporter.otlp.protocol", GRPC);
@@ -60,6 +76,11 @@ public void configBuilder(final SmallRyeConfigBuilder builder) {
fallbacks.put("quarkus.otel.exporter.otlp.metrics.proxy-options.host", "quarkus.otel.exporter.otlp.proxy-options.host");
fallbacks.put("quarkus.otel.exporter.otlp.metrics.proxy-options.port", "quarkus.otel.exporter.otlp.proxy-options.port");
- builder.withInterceptors(new FallbackConfigSourceInterceptor(fallbacks));
+ return builder.withInterceptors(new FallbackConfigSourceInterceptor(fallbacks));
+ }
+
+ @Override
+ public int priority() {
+ return Integer.MIN_VALUE;
}
}
diff --git a/extensions/opentelemetry/runtime/src/main/resources/META-INF/services/io.smallrye.config.SmallRyeConfigBuilderCustomizer b/extensions/opentelemetry/runtime/src/main/resources/META-INF/services/io.smallrye.config.SmallRyeConfigBuilderCustomizer
deleted file mode 100644
index 0f8cc80239dcb..0000000000000
--- a/extensions/opentelemetry/runtime/src/main/resources/META-INF/services/io.smallrye.config.SmallRyeConfigBuilderCustomizer
+++ /dev/null
@@ -1 +0,0 @@
-io.quarkus.opentelemetry.runtime.config.OpenTelemetryConfigBuilderCustomizer
diff --git a/extensions/opentelemetry/runtime/src/test/java/io/quarkus/opentelemetry/runtime/config/OpenTelemetryConfigTest.java b/extensions/opentelemetry/runtime/src/test/java/io/quarkus/opentelemetry/runtime/config/OpenTelemetryConfigTest.java
index 239526fd1476e..7a07f1191e1ad 100644
--- a/extensions/opentelemetry/runtime/src/test/java/io/quarkus/opentelemetry/runtime/config/OpenTelemetryConfigTest.java
+++ b/extensions/opentelemetry/runtime/src/test/java/io/quarkus/opentelemetry/runtime/config/OpenTelemetryConfigTest.java
@@ -16,6 +16,7 @@
import org.junit.jupiter.api.Test;
+import io.quarkus.opentelemetry.runtime.config.runtime.exporter.OtlpExporterConfigBuilder;
import io.quarkus.opentelemetry.runtime.config.runtime.exporter.OtlpExporterMetricsConfig;
import io.quarkus.opentelemetry.runtime.config.runtime.exporter.OtlpExporterRuntimeConfig;
import io.quarkus.opentelemetry.runtime.config.runtime.exporter.OtlpExporterTracesConfig;
@@ -39,7 +40,7 @@ void fromBase() {
baseExporterConfig.put("quarkus.otel.exporter.otlp.proxy-options.port", "9999");
SmallRyeConfig config = new SmallRyeConfigBuilder()
- .withCustomizers(new OpenTelemetryConfigBuilderCustomizer())
+ .withCustomizers(builder -> new OtlpExporterConfigBuilder().configBuilder(builder))
.withMapping(OtlpExporterRuntimeConfig.class)
.withDefaultValues(baseExporterConfig)
.build();
@@ -160,7 +161,7 @@ void overrideTraces() {
tracesExporterConfig.put("quarkus.otel.exporter.otlp.traces.proxy-options.port", "6666");
SmallRyeConfig config = new SmallRyeConfigBuilder()
- .withCustomizers(new OpenTelemetryConfigBuilderCustomizer())
+ .withCustomizers(builder -> new OtlpExporterConfigBuilder().configBuilder(builder))
.withMapping(OtlpExporterRuntimeConfig.class)
.withDefaultValues(baseExporterConfig)
.withDefaultValues(tracesExporterConfig)
diff --git a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/HttpConfiguration.java b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/HttpConfiguration.java
index b79377c6547f4..2214f37912b70 100644
--- a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/HttpConfiguration.java
+++ b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/HttpConfiguration.java
@@ -327,5 +327,6 @@ public enum InsecureRequests {
public enum PayloadHint {
JSON,
HTML,
+ TEXT
}
}
diff --git a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/QuarkusErrorHandler.java b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/QuarkusErrorHandler.java
index 7e7021abd8ecf..7e357151898a2 100644
--- a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/QuarkusErrorHandler.java
+++ b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/QuarkusErrorHandler.java
@@ -1,13 +1,17 @@
package io.quarkus.vertx.http.runtime;
+import static io.quarkus.vertx.http.runtime.HttpConfiguration.PayloadHint.JSON;
import static org.jboss.logging.Logger.getLogger;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
+import java.io.UncheckedIOException;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
+import java.util.Locale;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.RejectedExecutionException;
@@ -20,6 +24,7 @@
import io.netty.handler.codec.http.HttpResponseStatus;
import io.quarkus.runtime.ErrorPageAction;
import io.quarkus.runtime.TemplateHtmlBuilder;
+import io.quarkus.runtime.logging.DecorateStackUtil;
import io.quarkus.security.AuthenticationException;
import io.quarkus.security.ForbiddenException;
import io.quarkus.security.UnauthorizedException;
@@ -32,6 +37,9 @@
public class QuarkusErrorHandler implements Handler {
private static final Logger log = getLogger(QuarkusErrorHandler.class);
+ private static final String NL = "\n";
+ private static final String TAB = "\t";
+ private static final String HEADING = "500 - Internal Server Error";
/**
* we don't want to generate a new UUID each time as it is slowish. Instead, we just generate one based one
@@ -172,6 +180,7 @@ public void accept(Throwable throwable) {
if (responseContentType == null) {
responseContentType = "";
}
+
switch (responseContentType) {
case ContentTypes.TEXT_HTML:
case ContentTypes.APPLICATION_XHTML:
@@ -181,30 +190,94 @@ public void accept(Throwable throwable) {
break;
case ContentTypes.APPLICATION_JSON:
case ContentTypes.TEXT_JSON:
- jsonResponse(event, responseContentType, details, stack);
+ jsonResponse(event, responseContentType, details, stack, exception);
+ break;
+ case ContentTypes.TEXT_PLAIN:
+ textResponse(event, details, stack, exception);
break;
default:
- // We default to JSON representation
- switch (contentTypeDefault.orElse(HttpConfiguration.PayloadHint.JSON)) {
- case HTML:
- htmlResponse(event, details, exception);
- break;
- case JSON:
- default:
- jsonResponse(event, ContentTypes.APPLICATION_JSON, details, stack);
- break;
+ if (contentTypeDefault.isPresent()) {
+ switch (contentTypeDefault.get()) {
+ case HTML:
+ htmlResponse(event, details, exception);
+ break;
+ case JSON:
+ jsonResponse(event, ContentTypes.APPLICATION_JSON, details, stack, exception);
+ break;
+ case TEXT:
+ textResponse(event, details, stack, exception);
+ break;
+ default:
+ defaultResponse(event, details, stack, exception);
+ break;
+ }
+ } else {
+ defaultResponse(event, details, stack, exception);
+ break;
}
break;
}
}
- private void jsonResponse(RoutingContext event, String contentType, String details, String stack) {
+ private void defaultResponse(RoutingContext event, String details, String stack, Throwable throwable) {
+ String userAgent = event.request().getHeader("User-Agent");
+ if (userAgent != null && (userAgent.toLowerCase(Locale.ROOT).startsWith("wget/")
+ || userAgent.toLowerCase(Locale.ROOT).startsWith("curl/"))) {
+ textResponse(event, details, stack, throwable);
+ } else {
+ jsonResponse(event, ContentTypes.APPLICATION_JSON, details, stack, throwable);
+ }
+ }
+
+ private void textResponse(RoutingContext event, String details, String stack, Throwable throwable) {
+ event.response().headers().set(HttpHeaderNames.CONTENT_TYPE, ContentTypes.TEXT_PLAIN + "; charset=utf-8");
+ String decoratedString = null;
+ if (decorateStack && throwable != null) {
+ decoratedString = DecorateStackUtil.getDecoratedString(throwable, srcMainJava, knowClasses);
+ }
+
+ try (StringWriter sw = new StringWriter()) {
+ sw.write(NL + HEADING + NL);
+ sw.write("------------------------" + NL);
+ sw.write(NL);
+ sw.write("Details:");
+ sw.write(NL);
+ sw.write(TAB + details);
+ sw.write(NL);
+ if (decoratedString != null) {
+ sw.write("Decorate (Source code):");
+ sw.write(NL);
+ sw.write(TAB + decoratedString);
+ sw.write(NL);
+ }
+ sw.write("Stack:");
+ sw.write(NL);
+ sw.write(TAB + stack);
+ sw.write(NL);
+ writeResponse(event, sw.toString());
+ } catch (IOException ex) {
+ throw new UncheckedIOException(ex);
+ }
+ }
+
+ private void jsonResponse(RoutingContext event, String contentType, String details, String stack, Throwable throwable) {
event.response().headers().set(HttpHeaderNames.CONTENT_TYPE, contentType + "; charset=utf-8");
String escapedDetails = escapeJsonString(details);
String escapedStack = escapeJsonString(stack);
+ String decoratedString = null;
+ if (decorateStack && throwable != null) {
+ decoratedString = DecorateStackUtil.getDecoratedString(throwable, srcMainJava, knowClasses);
+ }
+
StringBuilder jsonPayload = new StringBuilder("{\"details\":\"")
- .append(escapedDetails)
- .append("\",\"stack\":\"")
+ .append(escapedDetails);
+
+ if (decoratedString != null) {
+ jsonPayload = jsonPayload.append("\",\"decorate\":\"")
+ .append(escapeJsonString(decoratedString));
+ }
+
+ jsonPayload = jsonPayload.append("\",\"stack\":\"")
.append(escapedStack)
.append("\"}");
writeResponse(event, jsonPayload.toString());
@@ -297,23 +370,41 @@ private ContentTypes() {
private static final String APPLICATION_JSON = "application/json";
private static final String TEXT_JSON = "text/json";
private static final String TEXT_HTML = "text/html";
+ private static final String TEXT_PLAIN = "text/plain";
private static final String APPLICATION_XHTML = "application/xhtml+xml";
private static final String APPLICATION_XML = "application/xml";
private static final String TEXT_XML = "text/xml";
// WARNING: The order matters for wildcards: if text/json is before text/html, then text/* will match text/json.
- private static final Collection SUPPORTED = Arrays.asList(
+ private static final MIMEHeader[] BASE_HEADERS = {
new ParsableMIMEValue(APPLICATION_JSON).forceParse(),
new ParsableMIMEValue(TEXT_JSON).forceParse(),
new ParsableMIMEValue(TEXT_HTML).forceParse(),
new ParsableMIMEValue(APPLICATION_XHTML).forceParse(),
new ParsableMIMEValue(APPLICATION_XML).forceParse(),
- new ParsableMIMEValue(TEXT_XML).forceParse());
+ new ParsableMIMEValue(TEXT_XML).forceParse()
+ };
+
+ private static final Collection SUPPORTED = new ArrayList<>(Arrays.asList(BASE_HEADERS));
+ private static final Collection SUPPORTED_CURL = new ArrayList<>();
+ static {
+ SUPPORTED_CURL.add(new ParsableMIMEValue(TEXT_PLAIN).forceParse());
+ SUPPORTED_CURL.addAll(Arrays.asList(BASE_HEADERS));
+ ((ArrayList) SUPPORTED).add(new ParsableMIMEValue(TEXT_PLAIN).forceParse());
+ }
static String pickFirstSupportedAndAcceptedContentType(RoutingContext context) {
List acceptableTypes = context.parsedHeaders().accept();
- MIMEHeader result = context.parsedHeaders().findBestUserAcceptedIn(acceptableTypes, SUPPORTED);
- return result == null ? null : result.value();
+
+ String userAgent = context.request().getHeader("User-Agent");
+ if (userAgent != null && (userAgent.toLowerCase(Locale.ROOT).startsWith("wget/")
+ || userAgent.toLowerCase(Locale.ROOT).startsWith("curl/"))) {
+ MIMEHeader result = context.parsedHeaders().findBestUserAcceptedIn(acceptableTypes, SUPPORTED_CURL);
+ return result == null ? null : result.value();
+ } else {
+ MIMEHeader result = context.parsedHeaders().findBestUserAcceptedIn(acceptableTypes, SUPPORTED);
+ return result == null ? null : result.value();
+ }
}
}
}
diff --git a/independent-projects/resteasy-reactive/common/processor/src/main/java/org/jboss/resteasy/reactive/common/processor/scanning/ResteasyReactiveScanner.java b/independent-projects/resteasy-reactive/common/processor/src/main/java/org/jboss/resteasy/reactive/common/processor/scanning/ResteasyReactiveScanner.java
index e97bacc60b9a9..34fc0611c48a2 100644
--- a/independent-projects/resteasy-reactive/common/processor/src/main/java/org/jboss/resteasy/reactive/common/processor/scanning/ResteasyReactiveScanner.java
+++ b/independent-projects/resteasy-reactive/common/processor/src/main/java/org/jboss/resteasy/reactive/common/processor/scanning/ResteasyReactiveScanner.java
@@ -44,29 +44,20 @@
public class ResteasyReactiveScanner {
- public static final Map BUILTIN_HTTP_ANNOTATIONS_TO_METHOD;
- public static final Map METHOD_TO_BUILTIN_HTTP_ANNOTATIONS;
-
- static {
- Map map = new HashMap<>();
- Map reverseMap = new HashMap<>();
- map.put(GET, "GET");
- reverseMap.put("GET", GET);
- map.put(POST, "POST");
- reverseMap.put("POST", POST);
- map.put(HEAD, "HEAD");
- reverseMap.put("HEAD", HEAD);
- map.put(PUT, "PUT");
- reverseMap.put("PUT", PUT);
- map.put(DELETE, "DELETE");
- reverseMap.put("DELETE", DELETE);
- map.put(PATCH, "PATCH");
- reverseMap.put("PATCH", PATCH);
- map.put(OPTIONS, "OPTIONS");
- reverseMap.put("OPTIONS", OPTIONS);
- BUILTIN_HTTP_ANNOTATIONS_TO_METHOD = Collections.unmodifiableMap(map);
- METHOD_TO_BUILTIN_HTTP_ANNOTATIONS = Collections.unmodifiableMap(reverseMap);
- }
+ public static final Map BUILTIN_HTTP_ANNOTATIONS_TO_METHOD = Map.of(GET, "GET",
+ POST, "POST",
+ HEAD, "HEAD",
+ PUT, "PUT",
+ DELETE, "DELETE",
+ PATCH, "PATCH",
+ OPTIONS, "OPTIONS");
+ public static final Map METHOD_TO_BUILTIN_HTTP_ANNOTATIONS = Map.of("GET", GET,
+ "POST", POST,
+ "HEAD", HEAD,
+ "PUT", PUT,
+ "DELETE", DELETE,
+ "PATCH", PATCH,
+ "OPTIONS", OPTIONS);
public static ApplicationScanningResult scanForApplicationClass(IndexView index, Set excludedClasses) {
Collection applications = index
@@ -279,7 +270,7 @@ public static ResourceScanningResult scanResources(
});
Map clientInterfaces = new HashMap<>(pathInterfaces);
- // for clients it is enough to have @PATH annotations on methods only
+ // for clients, it is enough to have @PATH annotations on methods only
for (DotName interfaceName : interfacesWithPathOnMethods) {
if (!clientInterfaces.containsKey(interfaceName)) {
clientInterfaces.put(interfaceName, "");
@@ -327,7 +318,7 @@ public static ResourceScanningResult scanResources(
httpAnnotationToMethod.put(httpMethodInstance.target().asClass().name(), httpMethodInstance.value().asString());
}
- // for clients it is also enough to only have @GET, @POST, etc on methods and no PATH whatsoever
+ // for clients, it is also enough to only have @GET, @POST, etc on methods and no PATH whatsoever
Set methodAnnotations = httpAnnotationToMethod.keySet();
for (DotName methodAnnotation : methodAnnotations) {
for (AnnotationInstance methodAnnotationInstance : index.getAnnotations(methodAnnotation)) {
@@ -335,7 +326,7 @@ public static ResourceScanningResult scanResources(
MethodInfo annotatedMethod = methodAnnotationInstance.target().asMethod();
ClassInfo classWithJaxrsMethod = annotatedMethod.declaringClass();
if (Modifier.isAbstract(annotatedMethod.flags())
- && Modifier.isAbstract(classWithJaxrsMethod.flags())
+ && Modifier.isInterface(classWithJaxrsMethod.flags())
&& !clientInterfaces.containsKey(classWithJaxrsMethod.name())) {
clientInterfaces.put(classWithJaxrsMethod.name(), "");
}
@@ -371,7 +362,7 @@ public static ResourceScanningResult scanResources(
continue;
}
possibleSubResources.put(classInfo.name(), classInfo);
- //we need to also look for all sub classes and interfaces
+ //we need to also look for all subclasses and interfaces
//they may have type variables that need to be handled
toScan.addAll(index.getKnownDirectImplementors(classInfo.name()));
toScan.addAll(index.getKnownDirectSubclasses(classInfo.name()));
diff --git a/integration-tests/kubernetes/quarkus-standard-way/src/test/java/io/quarkus/it/kubernetes/KnativeScaleBoundsTest.java b/integration-tests/kubernetes/quarkus-standard-way/src/test/java/io/quarkus/it/kubernetes/KnativeScaleBoundsTest.java
index 2d98390bb816c..4e88c8b061f0f 100644
--- a/integration-tests/kubernetes/quarkus-standard-way/src/test/java/io/quarkus/it/kubernetes/KnativeScaleBoundsTest.java
+++ b/integration-tests/kubernetes/quarkus-standard-way/src/test/java/io/quarkus/it/kubernetes/KnativeScaleBoundsTest.java
@@ -43,8 +43,8 @@ public void assertGeneratedResources() throws IOException {
assertThat(kubernetesList).filteredOn(i -> "Service".equals(i.getKind())).singleElement().satisfies(i -> {
Service service = (Service) i;
Map annotations = service.getSpec().getTemplate().getMetadata().getAnnotations();
- assertThat(annotations).contains(entry("autoscaling.knative.dev/minScale", "3"));
- assertThat(annotations).contains(entry("autoscaling.knative.dev/maxScale", "5"));
+ assertThat(annotations).contains(entry("autoscaling.knative.dev/min-scale", "3"));
+ assertThat(annotations).contains(entry("autoscaling.knative.dev/max-scale", "5"));
});
}
}
diff --git a/integration-tests/kubernetes/quarkus-standard-way/src/test/java/io/quarkus/it/kubernetes/KnativeWithAppConfigMapTest.java b/integration-tests/kubernetes/quarkus-standard-way/src/test/java/io/quarkus/it/kubernetes/KnativeWithAppConfigMapTest.java
new file mode 100644
index 0000000000000..cec524807dbb0
--- /dev/null
+++ b/integration-tests/kubernetes/quarkus-standard-way/src/test/java/io/quarkus/it/kubernetes/KnativeWithAppConfigMapTest.java
@@ -0,0 +1,79 @@
+package io.quarkus.it.kubernetes;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.List;
+
+import org.assertj.core.api.AbstractObjectAssert;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.RegisterExtension;
+
+import io.fabric8.knative.serving.v1.RevisionSpec;
+import io.fabric8.knative.serving.v1.Service;
+import io.fabric8.kubernetes.api.model.EnvVar;
+import io.fabric8.kubernetes.api.model.HasMetadata;
+import io.fabric8.kubernetes.api.model.Volume;
+import io.fabric8.kubernetes.api.model.VolumeMount;
+import io.quarkus.test.ProdBuildResults;
+import io.quarkus.test.ProdModeTestResults;
+import io.quarkus.test.QuarkusProdModeTest;
+
+public class KnativeWithAppConfigMapTest {
+
+ @RegisterExtension
+ static final QuarkusProdModeTest config = new QuarkusProdModeTest()
+ .withApplicationRoot((jar) -> jar.addClasses(GreetingResource.class))
+ .setApplicationName("knative-with-app-config-map")
+ .setApplicationVersion("0.1-SNAPSHOT")
+ .withConfigurationResource("knative-with-app-config-map.properties");
+
+ @ProdBuildResults
+ private ProdModeTestResults prodModeTestResults;
+
+ @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"))
+ .satisfies(p -> assertThat(p.toFile().listFiles()).hasSize(2));
+
+ 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.getSpec()).satisfies(spec -> {
+ assertThat(s.getMetadata()).satisfies(m -> {
+ assertThat(m.getName()).isEqualTo("knative-with-app-config-map");
+ });
+ });
+ });
+
+ AbstractObjectAssert, ?> specAssert = assertThat(i).extracting("spec");
+ specAssert.extracting("template").extracting("spec").isInstanceOfSatisfying(RevisionSpec.class,
+ revisionSpec -> {
+ assertThat(revisionSpec.getContainers()).singleElement().satisfies(container -> {
+ List envVars = container.getEnv();
+ assertThat(envVars).anySatisfy(envVar -> {
+ assertThat(envVar.getName()).isEqualTo("SMALLRYE_CONFIG_LOCATIONS");
+ assertThat(envVar.getValue()).isEqualTo("/mnt/app-config-map");
+ });
+
+ List mounts = container.getVolumeMounts();
+ assertThat(mounts).anySatisfy(mount -> {
+ assertThat(mount.getName()).isEqualTo("app-config-map");
+ assertThat(mount.getMountPath()).isEqualTo("/mnt/app-config-map");
+ });
+ });
+ List volumes = revisionSpec.getVolumes();
+ assertThat(volumes).anySatisfy(volume -> {
+ assertThat(volume.getName()).isEqualTo("app-config-map");
+ assertThat(volume.getConfigMap().getName()).isEqualTo("my-kn-config-map");
+ });
+ });
+ });
+ }
+}
diff --git a/integration-tests/kubernetes/quarkus-standard-way/src/test/java/io/quarkus/it/kubernetes/KnativeWithAppSecretTest.java b/integration-tests/kubernetes/quarkus-standard-way/src/test/java/io/quarkus/it/kubernetes/KnativeWithAppSecretTest.java
new file mode 100644
index 0000000000000..820cdf22395fc
--- /dev/null
+++ b/integration-tests/kubernetes/quarkus-standard-way/src/test/java/io/quarkus/it/kubernetes/KnativeWithAppSecretTest.java
@@ -0,0 +1,79 @@
+package io.quarkus.it.kubernetes;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.List;
+
+import org.assertj.core.api.AbstractObjectAssert;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.RegisterExtension;
+
+import io.fabric8.knative.serving.v1.RevisionSpec;
+import io.fabric8.knative.serving.v1.Service;
+import io.fabric8.kubernetes.api.model.EnvVar;
+import io.fabric8.kubernetes.api.model.HasMetadata;
+import io.fabric8.kubernetes.api.model.Volume;
+import io.fabric8.kubernetes.api.model.VolumeMount;
+import io.quarkus.test.ProdBuildResults;
+import io.quarkus.test.ProdModeTestResults;
+import io.quarkus.test.QuarkusProdModeTest;
+
+public class KnativeWithAppSecretTest {
+
+ @RegisterExtension
+ static final QuarkusProdModeTest config = new QuarkusProdModeTest()
+ .withApplicationRoot((jar) -> jar.addClasses(GreetingResource.class))
+ .setApplicationName("knative-with-app-secret")
+ .setApplicationVersion("0.1-SNAPSHOT")
+ .withConfigurationResource("knative-with-app-secret.properties");
+
+ @ProdBuildResults
+ private ProdModeTestResults prodModeTestResults;
+
+ @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"))
+ .satisfies(p -> assertThat(p.toFile().listFiles()).hasSize(2));
+
+ 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.getSpec()).satisfies(spec -> {
+ assertThat(s.getMetadata()).satisfies(m -> {
+ assertThat(m.getName()).isEqualTo("knative-with-app-secret");
+ });
+ });
+ });
+
+ AbstractObjectAssert, ?> specAssert = assertThat(i).extracting("spec");
+ specAssert.extracting("template").extracting("spec").isInstanceOfSatisfying(RevisionSpec.class,
+ revisionSpec -> {
+ assertThat(revisionSpec.getContainers()).singleElement().satisfies(container -> {
+ List envVars = container.getEnv();
+ assertThat(envVars).anySatisfy(envVar -> {
+ assertThat(envVar.getName()).isEqualTo("SMALLRYE_CONFIG_LOCATIONS");
+ assertThat(envVar.getValue()).isEqualTo("/mnt/app-secret");
+ });
+
+ List mounts = container.getVolumeMounts();
+ assertThat(mounts).anySatisfy(mount -> {
+ assertThat(mount.getName()).isEqualTo("app-secret");
+ assertThat(mount.getMountPath()).isEqualTo("/mnt/app-secret");
+ });
+ });
+ List volumes = revisionSpec.getVolumes();
+ assertThat(volumes).anySatisfy(volume -> {
+ assertThat(volume.getName()).isEqualTo("app-secret");
+ assertThat(volume.getSecret().getSecretName()).isEqualTo("my-kn-secret");
+ });
+ });
+ });
+ }
+}
diff --git a/integration-tests/kubernetes/quarkus-standard-way/src/test/java/io/quarkus/it/kubernetes/KnativeWithExtendedPropertiesTest.java b/integration-tests/kubernetes/quarkus-standard-way/src/test/java/io/quarkus/it/kubernetes/KnativeWithExtendedPropertiesTest.java
index 0b7a1f126641a..803505bec6a01 100644
--- a/integration-tests/kubernetes/quarkus-standard-way/src/test/java/io/quarkus/it/kubernetes/KnativeWithExtendedPropertiesTest.java
+++ b/integration-tests/kubernetes/quarkus-standard-way/src/test/java/io/quarkus/it/kubernetes/KnativeWithExtendedPropertiesTest.java
@@ -56,8 +56,8 @@ public void assertGeneratedResources() throws IOException {
assertThat(s.getSpec()).satisfies(serviceSpec -> {
assertThat(serviceSpec.getTemplate()).satisfies(template -> {
assertThat(template.getMetadata()).satisfies(m -> {
- assertThat(m.getAnnotations()).contains(entry("autoscaling.knative.dev/minScale", "5"));
- assertThat(m.getAnnotations()).contains(entry("autoscaling.knative.dev/maxScale", "10"));
+ assertThat(m.getAnnotations()).contains(entry("autoscaling.knative.dev/min-scale", "5"));
+ assertThat(m.getAnnotations()).contains(entry("autoscaling.knative.dev/max-scale", "10"));
});
assertThat(template.getSpec()).satisfies(revisionSpec -> {
assertThat(revisionSpec.getContainerConcurrency()).isEqualTo(5);
diff --git a/integration-tests/kubernetes/quarkus-standard-way/src/test/resources/knative-with-app-config-map.properties b/integration-tests/kubernetes/quarkus-standard-way/src/test/resources/knative-with-app-config-map.properties
new file mode 100644
index 0000000000000..ea7497503112b
--- /dev/null
+++ b/integration-tests/kubernetes/quarkus-standard-way/src/test/resources/knative-with-app-config-map.properties
@@ -0,0 +1,2 @@
+quarkus.kubernetes.deployment-target=knative
+quarkus.knative.app-config-map=my-kn-config-map
diff --git a/integration-tests/kubernetes/quarkus-standard-way/src/test/resources/knative-with-app-secret.properties b/integration-tests/kubernetes/quarkus-standard-way/src/test/resources/knative-with-app-secret.properties
new file mode 100644
index 0000000000000..baabf949dea8e
--- /dev/null
+++ b/integration-tests/kubernetes/quarkus-standard-way/src/test/resources/knative-with-app-secret.properties
@@ -0,0 +1,2 @@
+quarkus.kubernetes.deployment-target=knative
+quarkus.knative.app-secret=my-kn-secret
diff --git a/integration-tests/observability-lgtm/src/main/resources/application.properties b/integration-tests/observability-lgtm/src/main/resources/application.properties
index b88a2d3f9bb17..8715ab7947e24 100644
--- a/integration-tests/observability-lgtm/src/main/resources/application.properties
+++ b/integration-tests/observability-lgtm/src/main/resources/application.properties
@@ -6,12 +6,9 @@ quarkus.micrometer.export.otlp.enabled=true
quarkus.micrometer.export.otlp.publish=true
quarkus.micrometer.export.otlp.step=PT5S
quarkus.micrometer.export.otlp.default-registry=true
-%dev.quarkus.micrometer.export.otlp.url=http://${quarkus.otel-collector.url}/v1/metrics
%prod.quarkus.micrometer.export.otlp.url=http://localhost:4318/v1/metrics
#opentelemetry
-quarkus.otel.exporter.otlp.traces.protocol=http/protobuf
-%dev.quarkus.otel.exporter.otlp.traces.endpoint=http://${quarkus.otel-collector.url}
%prod.quarkus.otel.exporter.otlp.traces.endpoint=http://localhost:4318
#quarkus.observability.lgtm.image-name=grafana/otel-lgtm
diff --git a/integration-tests/observability-lgtm/src/test/java/io/quarkus/observability/test/LgtmTestBase.java b/integration-tests/observability-lgtm/src/test/java/io/quarkus/observability/test/LgtmTestBase.java
index ee49956a5a296..b30d11c6f7160 100644
--- a/integration-tests/observability-lgtm/src/test/java/io/quarkus/observability/test/LgtmTestBase.java
+++ b/integration-tests/observability-lgtm/src/test/java/io/quarkus/observability/test/LgtmTestBase.java
@@ -28,6 +28,9 @@ public void testTracing() {
Awaitility.await().atMost(61, TimeUnit.SECONDS).until(
() -> client.query("xvalue_X"),
result -> !result.data.result.isEmpty());
+ Awaitility.await().atMost(61, TimeUnit.SECONDS).until(
+ () -> client.traces("quarkus-integration-test-observability-lgtm", 20, 3),
+ result -> !result.traces.isEmpty());
}
}
diff --git a/integration-tests/observability-lgtm/src/test/java/io/quarkus/observability/test/support/GrafanaClient.java b/integration-tests/observability-lgtm/src/test/java/io/quarkus/observability/test/support/GrafanaClient.java
index 40f31cc689589..f0b9938101b76 100644
--- a/integration-tests/observability-lgtm/src/test/java/io/quarkus/observability/test/support/GrafanaClient.java
+++ b/integration-tests/observability-lgtm/src/test/java/io/quarkus/observability/test/support/GrafanaClient.java
@@ -89,4 +89,23 @@ public QueryResult query(String query) {
});
return ref.get();
}
+
+ public TempoResult traces(String service, int limit, int spss) {
+ AtomicReference ref = new AtomicReference<>();
+ String path = "/api/datasources/proxy/uid/tempo/api/search?q=%7Bresource.service.name%3D%22"
+ + service + "%22%7D&limit=" + limit + "&spss=" + spss;
+ handle(
+ path,
+ HttpRequest.Builder::GET,
+ HttpResponse.BodyHandlers.ofString(),
+ (r, b) -> {
+ try {
+ TempoResult result = MAPPER.readValue(b, TempoResult.class);
+ ref.set(result);
+ } catch (JsonProcessingException e) {
+ throw new UncheckedIOException(e);
+ }
+ });
+ return ref.get();
+ }
}
diff --git a/integration-tests/observability-lgtm/src/test/java/io/quarkus/observability/test/support/TempoResult.java b/integration-tests/observability-lgtm/src/test/java/io/quarkus/observability/test/support/TempoResult.java
new file mode 100644
index 0000000000000..480b04b5a8d92
--- /dev/null
+++ b/integration-tests/observability-lgtm/src/test/java/io/quarkus/observability/test/support/TempoResult.java
@@ -0,0 +1,38 @@
+package io.quarkus.observability.test.support;
+
+import java.util.List;
+import java.util.Map;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class TempoResult {
+ public List