diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 2efe111f0a409..97d93ed4c2cb3 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -35,6 +35,7 @@ updates: - dependency-name: org.glassfish:jakarta-el - dependency-name: com.google.cloud.tools:jib-core - dependency-name: org.jboss.threads:jboss-threads + - dependency-name: org.jboss.marshalling:* # Quarkus - dependency-name: io.quarkus.*:* - dependency-name: io.quarkus:* diff --git a/bom/application/pom.xml b/bom/application/pom.xml index d5fcf0841a9d8..ebd8bd5d95f1d 100644 --- a/bom/application/pom.xml +++ b/bom/application/pom.xml @@ -111,7 +111,7 @@ 1.7.0.Final 1.0.1.Final 2.5.2.Final - 2.1.4.SP1 + 2.2.0.Final 3.6.1.Final 4.5.9 4.5.14 @@ -161,7 +161,7 @@ 3.2.0 4.2.2 3.0.6.Final - 10.17.1 + 10.17.3 3.0.4 4.29.1 diff --git a/core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/NativeImageBuildStep.java b/core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/NativeImageBuildStep.java index 3025847af8817..64ad00259e468 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/NativeImageBuildStep.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/NativeImageBuildStep.java @@ -339,7 +339,7 @@ public NativeImageRunnerBuildItem resolveNativeImageBuildRunner(NativeConfig nat } String executableName = getNativeImageExecutableName(); String errorMessage = "Cannot find the `" + executableName - + "` in the GRAALVM_HOME, JAVA_HOME and System PATH. Install it using `gu install native-image`."; + + "` in the GRAALVM_HOME, JAVA_HOME and System PATH."; if (!SystemUtils.IS_OS_LINUX) { // Delay the error: if we're just building native sources, we may not need the build runner at all. return new NativeImageRunnerBuildItem(new NativeImageBuildRunnerError(errorMessage)); diff --git a/docs/src/main/asciidoc/scripting.adoc b/docs/src/main/asciidoc/scripting.adoc index 6f0fe640c3c14..6da9a91b06691 100644 --- a/docs/src/main/asciidoc/scripting.adoc +++ b/docs/src/main/asciidoc/scripting.adoc @@ -415,7 +415,7 @@ INFO: Building native image source jar: /tmp/quarkus-jbang8082065952748314720/qu Mar 22, 2023 9:58:47 A.M. io.quarkus.deployment.pkg.steps.NativeImageBuildStep build INFO: Building native image from /tmp/quarkus-jbang8082065952748314720/quarkus-application-native-image-source-jar/quarkus-application-runner.jar Mar 22, 2023 9:58:47 A.M. io.quarkus.deployment.pkg.steps.NativeImageBuildStep getNativeImageBuildRunner -WARN: Cannot find the `native-image` in the GRAALVM_HOME, JAVA_HOME and System PATH. Install it using `gu install native-image` Attempting to fall back to container build. +WARN: Cannot find the `native-image` in the GRAALVM_HOME, JAVA_HOME and System PATH. Attempting to fall back to container build. Mar 22, 2023 9:58:47 A.M. io.quarkus.deployment.pkg.steps.NativeImageBuildContainerRunner INFO: Using docker to run the native image builder Mar 22, 2023 9:58:47 A.M. io.quarkus.deployment.pkg.steps.NativeImageBuildContainerRunner setup diff --git a/docs/src/main/asciidoc/security-oauth2.adoc b/docs/src/main/asciidoc/security-oauth2.adoc index b4ece34f5f3fd..50f93424e846f 100644 --- a/docs/src/main/asciidoc/security-oauth2.adoc +++ b/docs/src/main/asciidoc/security-oauth2.adoc @@ -418,7 +418,7 @@ class TokenSecuredResourceTest { void testPermitAll() { RestAssured.given() .when() - .header("Authorization", "Bearer: " + BEARER_TOKEN) // <3> + .header("Authorization", "Bearer " + BEARER_TOKEN) // <3> .get("/secured/permit-all") .then() .statusCode(200) @@ -429,7 +429,7 @@ class TokenSecuredResourceTest { void testRolesAllowed() { RestAssured.given() .when() - .header("Authorization", "Bearer: " + BEARER_TOKEN) + .header("Authorization", "Bearer " + BEARER_TOKEN) .get("/secured/roles-allowed") .then() .statusCode(200) diff --git a/extensions/flyway/runtime/src/main/java/io/quarkus/flyway/runtime/graal/ClassicConfigurationSubstitutions.java b/extensions/flyway/runtime/src/main/java/io/quarkus/flyway/runtime/graal/ClassicConfigurationSubstitutions.java deleted file mode 100644 index 6ee61f922bb4b..0000000000000 --- a/extensions/flyway/runtime/src/main/java/io/quarkus/flyway/runtime/graal/ClassicConfigurationSubstitutions.java +++ /dev/null @@ -1,66 +0,0 @@ -package io.quarkus.flyway.runtime.graal; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import org.flywaydb.core.extensibility.ConfigurationExtension; -import org.flywaydb.core.internal.plugin.PluginRegister; -import org.flywaydb.core.internal.util.MergeUtils; - -import com.google.gson.Gson; -import com.oracle.svm.core.annotate.Alias; -import com.oracle.svm.core.annotate.RecomputeFieldValue; -import com.oracle.svm.core.annotate.Substitute; -import com.oracle.svm.core.annotate.TargetClass; - -@TargetClass(className = "org.flywaydb.core.api.configuration.ClassicConfiguration") -public final class ClassicConfigurationSubstitutions { - - @Alias - @RecomputeFieldValue(kind = RecomputeFieldValue.Kind.FromAlias) - private static Pattern ANY_WORD_BETWEEN_TWO_QUOTES_PATTERN = Pattern.compile("\"([^\"]*)\""); - - @Alias - PluginRegister pluginRegister; - - @Substitute - private void determineKeysToRemoveAndRemoveFromProps(HashMap> configExtensionsPropertyMap, - List keysToRemove, Map props) { - for (Map.Entry> property : configExtensionsPropertyMap.entrySet()) { - ConfigurationExtension cfg = null; - for (ConfigurationExtension c : pluginRegister.getPlugins(ConfigurationExtension.class)) { - if (c.getClass().toString().equals(property.getKey())) { - cfg = c; - break; - } - } - if (cfg != null) { - Map mp = property.getValue(); - try { - Gson gson = new Gson(); - ConfigurationExtension newConfigurationExtension = gson.fromJson(gson.toJson(mp), cfg.getClass()); - MergeUtils.mergeModel(newConfigurationExtension, cfg); - } catch (Exception e) { - Matcher matcher = ANY_WORD_BETWEEN_TWO_QUOTES_PATTERN.matcher(e.getMessage()); - if (matcher.find()) { - String errorProperty = matcher.group(1); - List propsToRemove = new ArrayList<>(); - for (String k : keysToRemove) { - if (k.endsWith(errorProperty)) { - propsToRemove.add(k); - } - } - keysToRemove.removeAll(propsToRemove); - } - } - } - } - - props.keySet().removeAll(keysToRemove); - } - -} 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 49ce2c288cc17..fede6a8105aad 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 @@ -7,8 +7,11 @@ import static io.quarkus.kubernetes.spi.KubernetesDeploymentTargetBuildItem.DEFAULT_PRIORITY; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; import java.util.stream.Stream; import io.dekorate.knative.decorator.AddAwsElasticBlockStoreVolumeToRevisionDecorator; @@ -20,21 +23,31 @@ import io.dekorate.knative.decorator.AddPvcVolumeToRevisionDecorator; import io.dekorate.knative.decorator.AddSecretVolumeToRevisionDecorator; import io.dekorate.knative.decorator.AddSidecarToRevisionDecorator; -import io.dekorate.knative.decorator.ApplyAnnotationsToServiceTemplate; import io.dekorate.knative.decorator.ApplyGlobalAutoscalingClassDecorator; +import io.dekorate.knative.decorator.ApplyGlobalContainerConcurrencyDecorator; import io.dekorate.knative.decorator.ApplyGlobalRequestsPerSecondTargetDecorator; import io.dekorate.knative.decorator.ApplyGlobalTargetUtilizationDecorator; +import io.dekorate.knative.decorator.ApplyLocalAutoscalingClassDecorator; +import io.dekorate.knative.decorator.ApplyLocalAutoscalingMetricDecorator; +import io.dekorate.knative.decorator.ApplyLocalAutoscalingTargetDecorator; import io.dekorate.knative.decorator.ApplyLocalContainerConcurrencyDecorator; +import io.dekorate.knative.decorator.ApplyLocalTargetUtilizationPercentageDecorator; +import io.dekorate.knative.decorator.ApplyMaxScaleDecorator; +import io.dekorate.knative.decorator.ApplyMinScaleDecorator; import io.dekorate.knative.decorator.ApplyRevisionNameDecorator; import io.dekorate.knative.decorator.ApplyServiceAccountToRevisionSpecDecorator; import io.dekorate.knative.decorator.ApplyTrafficDecorator; +import io.dekorate.kubernetes.config.ConfigMapVolumeBuilder; import io.dekorate.kubernetes.config.EnvBuilder; +import io.dekorate.kubernetes.config.MountBuilder; import io.dekorate.kubernetes.config.Port; +import io.dekorate.kubernetes.config.SecretVolumeBuilder; import io.dekorate.kubernetes.decorator.AddConfigMapDataDecorator; import io.dekorate.kubernetes.decorator.AddConfigMapResourceProvidingDecorator; import io.dekorate.kubernetes.decorator.AddEnvVarDecorator; import io.dekorate.kubernetes.decorator.AddImagePullSecretToServiceAccountDecorator; import io.dekorate.kubernetes.decorator.AddLabelDecorator; +import io.dekorate.kubernetes.decorator.AddMountDecorator; import io.dekorate.kubernetes.decorator.AddServiceAccountResourceDecorator; import io.dekorate.kubernetes.decorator.ApplicationContainerDecorator; import io.dekorate.kubernetes.decorator.ApplyImagePullPolicyDecorator; @@ -75,14 +88,6 @@ public class KnativeProcessor { private static final String KNATIVE_CONFIG_AUTOSCALER = "config-autoscaler"; private static final String KNATIVE_CONFIG_DEFAULTS = "config-defaults"; private static final String KNATIVE_SERVING = "knative-serving"; - private static final String KNATIVE_MIN_SCALE = "autoscaling.knative.dev/minScale"; - private static final String KNATIVE_MAX_SCALE = "autoscaling.knative.dev/maxScale"; - private static final String KNATIVE_AUTOSCALING_METRIC = "autoscaling.knative.dev/metric"; - private static final String KNATIVE_AUTOSCALING_CLASS = "autoscaling.knative.dev/class"; - private static final String KNATIVE_AUTOSCALING_CLASS_SUFFIX = ".autoscaling.knative.dev"; - private static final String KNATIVE_UTILIZATION_PERCENTAGE = "autoscaling.knative.dev/target-utilization-percentage"; - private static final String KNATIVE_AUTOSCALING_TARGET = "autoscaling.knative.dev/target"; - private static final String KNATIVE_CONTAINER_CONCURRENCY = "container-concurrency"; private static final String KNATIVE_DEV_VISIBILITY = "networking.knative.dev/visibility"; @BuildStep @@ -194,55 +199,20 @@ public List createDecorators(ApplicationInfoBuildItem applic result.add(new DecoratorBuildItem(KNATIVE, new AddLabelDecorator(name, KNATIVE_DEV_VISIBILITY, "cluster-local"))); } - } - /** - * Once the Dekorate issue is fixed https://github.com/dekorateio/dekorate/issues/869, - * we should replace ApplyAnnotationsToServiceTemplate by ApplyMinScaleDecorator. - */ - config.minScale.map(String::valueOf).ifPresent(min -> result.add(new DecoratorBuildItem(KNATIVE, - new ApplyAnnotationsToServiceTemplate(name, KNATIVE_MIN_SCALE, min)))); - /** - * Once the Dekorate issue is fixed https://github.com/dekorateio/dekorate/issues/869, - * we should replace ApplyAnnotationsToServiceTemplate by ApplyMaxScaleDecorator. - */ - config.maxScale.map(String::valueOf).ifPresent(max -> result.add(new DecoratorBuildItem(KNATIVE, - new ApplyAnnotationsToServiceTemplate(name, KNATIVE_MAX_SCALE, max)))); - /** - * Once the Dekorate issue is fixed https://github.com/dekorateio/dekorate/issues/869, - * we should replace ApplyAnnotationsToServiceTemplate by ApplyLocalAutoscalingClassDecorator. - */ + config.minScale.ifPresent(min -> result.add(new DecoratorBuildItem(KNATIVE, new ApplyMinScaleDecorator(name, min)))); + config.maxScale.ifPresent(max -> result.add(new DecoratorBuildItem(KNATIVE, new ApplyMaxScaleDecorator(name, max)))); config.revisionAutoScaling.autoScalerClass.map(AutoScalerClassConverter::convert) - .ifPresent(a -> result.add(new DecoratorBuildItem(KNATIVE, new ApplyAnnotationsToServiceTemplate(name, - KNATIVE_AUTOSCALING_CLASS, a.name().toLowerCase() + KNATIVE_AUTOSCALING_CLASS_SUFFIX)))); - /** - * Once the Dekorate issue is fixed https://github.com/dekorateio/dekorate/issues/869, - * we should replace ApplyAnnotationsToServiceTemplate by ApplyLocalAutoscalingMetricDecorator. - */ + .ifPresent(a -> result.add(new DecoratorBuildItem(KNATIVE, new ApplyLocalAutoscalingClassDecorator(name, a)))); config.revisionAutoScaling.metric.map(AutoScalingMetricConverter::convert) - .ifPresent(m -> result.add(new DecoratorBuildItem(KNATIVE, - new ApplyAnnotationsToServiceTemplate(name, KNATIVE_AUTOSCALING_METRIC, m.name().toLowerCase())))); - - config.revisionAutoScaling.containerConcurrency - .ifPresent( - c -> result.add(new DecoratorBuildItem(KNATIVE, new ApplyLocalContainerConcurrencyDecorator(name, c)))); - - /** - * Once the Dekorate issue is fixed https://github.com/dekorateio/dekorate/issues/869, - * we should replace ApplyAnnotationsToServiceTemplate by ApplyLocalTargetUtilizationPercentageDecorator. - */ - config.revisionAutoScaling.targetUtilizationPercentage.map(String::valueOf) - .ifPresent(t -> result - .add(new DecoratorBuildItem(KNATIVE, - new ApplyAnnotationsToServiceTemplate(name, KNATIVE_UTILIZATION_PERCENTAGE, t)))); - /** - * Once the Dekorate issue is fixed https://github.com/dekorateio/dekorate/issues/869, - * we should replace ApplyAnnotationsToServiceTemplate by ApplyLocalAutoscalingTargetDecorator. - */ - config.revisionAutoScaling.target.map(String::valueOf) - .ifPresent(t -> result.add(new DecoratorBuildItem(KNATIVE, - new ApplyAnnotationsToServiceTemplate(name, KNATIVE_AUTOSCALING_TARGET, t)))); + .ifPresent(m -> result.add(new DecoratorBuildItem(KNATIVE, new ApplyLocalAutoscalingMetricDecorator(name, m)))); + config.revisionAutoScaling.containerConcurrency.ifPresent( + c -> result.add(new DecoratorBuildItem(KNATIVE, new ApplyLocalContainerConcurrencyDecorator(name, c)))); + config.revisionAutoScaling.targetUtilizationPercentage.ifPresent(t -> result + .add(new DecoratorBuildItem(KNATIVE, new ApplyLocalTargetUtilizationPercentageDecorator(name, t)))); + config.revisionAutoScaling.target + .ifPresent(t -> result.add(new DecoratorBuildItem(KNATIVE, new ApplyLocalAutoscalingTargetDecorator(name, t)))); config.globalAutoScaling.autoScalerClass .map(AutoScalerClassConverter::convert) .ifPresent(a -> { @@ -251,17 +221,11 @@ public List createDecorators(ApplicationInfoBuildItem applic new AddConfigMapResourceProvidingDecorator(KNATIVE_CONFIG_AUTOSCALER, KNATIVE_SERVING))); result.add(new DecoratorBuildItem(KNATIVE, new ApplyGlobalAutoscalingClassDecorator(a))); }); - config.globalAutoScaling.containerConcurrency.map(String::valueOf) - .ifPresent(c -> { - result.add(new DecoratorBuildItem(KNATIVE, - new AddConfigMapResourceProvidingDecorator(KNATIVE_CONFIG_DEFAULTS, KNATIVE_SERVING))); - /** - * Once the Dekorate issue is fixed https://github.com/dekorateio/dekorate/issues/869, - * we should replace ApplyAnnotationsToServiceTemplate by ApplyGlobalContainerConcurrencyDecorator. - */ - result.add(new DecoratorBuildItem(KNATIVE, - new AddConfigMapDataDecorator(KNATIVE_CONFIG_DEFAULTS, KNATIVE_CONTAINER_CONCURRENCY, c))); - }); + config.globalAutoScaling.containerConcurrency.ifPresent(c -> { + result.add(new DecoratorBuildItem(KNATIVE, + new AddConfigMapResourceProvidingDecorator(KNATIVE_CONFIG_DEFAULTS, KNATIVE_SERVING))); + result.add(new DecoratorBuildItem(KNATIVE, new ApplyGlobalContainerConcurrencyDecorator(c))); + }); config.globalAutoScaling.requestsPerSecond .ifPresent(r -> { @@ -311,6 +275,7 @@ public List createDecorators(ApplicationInfoBuildItem applic //Add revision decorators result.addAll(createVolumeDecorators(project, name, config)); + result.addAll(createAppConfigVolumeAndEnvDecorators(project, name, config)); config.getHostAliases().entrySet().forEach(e -> { result.add(new DecoratorBuildItem(KNATIVE, new AddHostAliasesToRevisionDecorator(name, HostAliasConverter.convert(e)))); @@ -375,4 +340,45 @@ private static List createVolumeDecorators(Optional }); return result; } + + private static List createAppConfigVolumeAndEnvDecorators(Optional project, String name, + PlatformConfiguration config) { + + List result = new ArrayList<>(); + Set paths = new HashSet<>(); + + config.getAppSecret().ifPresent(s -> { + result.add(new DecoratorBuildItem(KNATIVE, new AddSecretVolumeToRevisionDecorator(new SecretVolumeBuilder() + .withSecretName(s) + .withVolumeName("app-secret") + .build()))); + result.add(new DecoratorBuildItem(KNATIVE, new AddMountDecorator(new MountBuilder() + .withName("app-secret") + .withPath("/mnt/app-secret") + .build()))); + paths.add("/mnt/app-secret"); + }); + + config.getAppConfigMap().ifPresent(s -> { + result.add(new DecoratorBuildItem(KNATIVE, new AddConfigMapVolumeToRevisionDecorator(new ConfigMapVolumeBuilder() + .withConfigMapName(s) + .withVolumeName("app-config-map") + .build()))); + result.add(new DecoratorBuildItem(KNATIVE, new AddMountDecorator(new MountBuilder() + .withName("app-config-map") + .withPath("/mnt/app-config-map") + .build()))); + paths.add("/mnt/app-config-map"); + }); + + if (!paths.isEmpty()) { + result.add(new DecoratorBuildItem(KNATIVE, + new AddEnvVarDecorator(ApplicationContainerDecorator.ANY, name, new EnvBuilder() + .withName("SMALLRYE_CONFIG_LOCATIONS") + .withValue(paths.stream().collect(Collectors.joining(","))) + .build()))); + } + return result; + } + } diff --git a/extensions/opentelemetry/deployment/src/main/java/io/quarkus/opentelemetry/deployment/exporter/otlp/OtlpExporterProcessor.java b/extensions/opentelemetry/deployment/src/main/java/io/quarkus/opentelemetry/deployment/exporter/otlp/OtlpExporterProcessor.java index 1b6eac1ebadd4..c2b993c9cda24 100644 --- a/extensions/opentelemetry/deployment/src/main/java/io/quarkus/opentelemetry/deployment/exporter/otlp/OtlpExporterProcessor.java +++ b/extensions/opentelemetry/deployment/src/main/java/io/quarkus/opentelemetry/deployment/exporter/otlp/OtlpExporterProcessor.java @@ -20,9 +20,11 @@ import io.quarkus.arc.deployment.SyntheticBeanBuildItem; import io.quarkus.deployment.annotations.*; import io.quarkus.deployment.annotations.Record; +import io.quarkus.deployment.builditem.RunTimeConfigBuilderBuildItem; import io.quarkus.opentelemetry.runtime.config.build.OTelBuildConfig; import io.quarkus.opentelemetry.runtime.config.build.exporter.OtlpExporterBuildConfig; import io.quarkus.opentelemetry.runtime.config.runtime.OTelRuntimeConfig; +import io.quarkus.opentelemetry.runtime.config.runtime.exporter.OtlpExporterConfigBuilder; import io.quarkus.opentelemetry.runtime.config.runtime.exporter.OtlpExporterRuntimeConfig; import io.quarkus.opentelemetry.runtime.exporter.otlp.OTelExporterRecorder; import io.quarkus.opentelemetry.runtime.exporter.otlp.tracing.LateBoundBatchSpanProcessor; @@ -59,6 +61,11 @@ public boolean getAsBoolean() { } } + @BuildStep + void config(BuildProducer 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> traces; + public Metrics metrics; + + // getters and setters + + @Override + public String toString() { + return "TempoResult{" + + "traces=" + traces + + ", metrics=" + metrics + + '}'; + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public static class Metrics { + public int inspectedBytes; + public int completedJobs; + public int totalJobs; + + @Override + public String toString() { + return "Metrics{" + + "inspectedBytes=" + inspectedBytes + + ", completedJobs=" + completedJobs + + ", totalJobs=" + totalJobs + + '}'; + } + } +} \ No newline at end of file diff --git a/integration-tests/rest-client-reactive-http2/src/main/java/io/quarkus/it/rest/client/http2/AbstractGreetingResource.java b/integration-tests/rest-client-reactive-http2/src/main/java/io/quarkus/it/rest/client/http2/AbstractGreetingResource.java new file mode 100644 index 0000000000000..2cfd5d34687da --- /dev/null +++ b/integration-tests/rest-client-reactive-http2/src/main/java/io/quarkus/it/rest/client/http2/AbstractGreetingResource.java @@ -0,0 +1,14 @@ +package io.quarkus.it.rest.client.http2; + +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; + +@Path("/greet") +public abstract class AbstractGreetingResource { + + @GET + @Produces(MediaType.TEXT_PLAIN) + public abstract String hello(); +} diff --git a/integration-tests/rest-client-reactive-http2/src/main/java/io/quarkus/it/rest/client/http2/GreetingResource.java b/integration-tests/rest-client-reactive-http2/src/main/java/io/quarkus/it/rest/client/http2/GreetingResource.java new file mode 100644 index 0000000000000..f90e828ab2c3a --- /dev/null +++ b/integration-tests/rest-client-reactive-http2/src/main/java/io/quarkus/it/rest/client/http2/GreetingResource.java @@ -0,0 +1,8 @@ +package io.quarkus.it.rest.client.http2; + +public class GreetingResource extends AbstractGreetingResource { + @Override + public String hello() { + return "Hello"; + } +} diff --git a/integration-tests/rest-client-reactive-http2/src/test/java/io/quarkus/it/rest/client/http2/GreetingResourceTest.java b/integration-tests/rest-client-reactive-http2/src/test/java/io/quarkus/it/rest/client/http2/GreetingResourceTest.java new file mode 100644 index 0000000000000..2e344b9e3dafd --- /dev/null +++ b/integration-tests/rest-client-reactive-http2/src/test/java/io/quarkus/it/rest/client/http2/GreetingResourceTest.java @@ -0,0 +1,19 @@ +package io.quarkus.it.rest.client.http2; + +import static io.restassured.RestAssured.when; + +import org.junit.jupiter.api.Test; + +import io.quarkus.test.junit.QuarkusTest; + +@QuarkusTest +class GreetingResourceTest { + + @Test + void testGreeting() { + when() + .get("/greet") + .then() + .statusCode(200); + } +} diff --git a/pom.xml b/pom.xml index a6f25dbd90895..3d837ff70e7bb 100644 --- a/pom.xml +++ b/pom.xml @@ -55,7 +55,7 @@ jdbc:postgresql:hibernate_orm_test 4.5.4 - 0.0.107 + 0.0.108 false false