diff --git a/core/deployment/src/main/java/io/quarkus/deployment/builditem/DevServicesRegistryBuildItem.java b/core/deployment/src/main/java/io/quarkus/deployment/builditem/DevServicesRegistryBuildItem.java index ea4e83fbb0e27..c01c2953e16e9 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/builditem/DevServicesRegistryBuildItem.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/builditem/DevServicesRegistryBuildItem.java @@ -125,7 +125,7 @@ private void reallyStart(DevServicesResultBuildItem request, List> applicationConfigProvider; + private final Set highPriorityConfig; public static DiscoveredServiceBuilder discovered() { return new DiscoveredServiceBuilder(); @@ -106,6 +108,7 @@ public DevServicesResultBuildItem(String name, String description, String contai this.serviceName = null; this.serviceConfig = null; this.applicationConfigProvider = null; + this.highPriorityConfig = null; this.startableSupplier = null; this.postStartAction = null; } @@ -121,7 +124,7 @@ public DevServicesResultBuildItem(String name, Map config, Supplier startableSupplier, Consumer postStartAction, - Map> applicationConfigProvider) { + Map> applicationConfigProvider, Set highPriorityConfig) { this.name = name; this.description = description; this.containerId = null; @@ -131,6 +134,7 @@ public DevServicesResultBuildItem(String name, this.startableSupplier = startableSupplier; this.postStartAction = postStartAction; this.applicationConfigProvider = applicationConfigProvider; + this.highPriorityConfig = highPriorityConfig; } public String getName() { @@ -175,6 +179,7 @@ public Map> getApplicationConfigProvider() { public Map getConfig(Startable startable) { SupplierMap map = new SupplierMap<>(); + // To make sure static config does make it into a config source, include it here if (config != null && !config.isEmpty()) { map.putAll(config); } @@ -186,6 +191,22 @@ public Map getConfig(Startable startable) { return map; } + /** + * @deprecated Subject to changes due to #51209 + */ + @Deprecated(forRemoval = true) + public Map getOverrideConfig(Startable startable) { + + SupplierMap map = new SupplierMap<>(); + + if (highPriorityConfig != null) { + for (String key : highPriorityConfig) { + map.put(key, () -> applicationConfigProvider.get(key).apply(startable)); + } + } + return map; + } + public static class DiscoveredServiceBuilder { private String name; private String containerId; @@ -226,10 +247,8 @@ public DevServicesResultBuildItem build() { } public static class OwnedServiceBuilder { - private static final String IO_QUARKUS_DEVSERVICES_CONFIG_BUILDER_CLASS = "io.quarkus.devservice.runtime.config.DevServicesConfigBuilder"; - private static final boolean CONFIG_BUILDER_AVAILABLE = isClassAvailable( - IO_QUARKUS_DEVSERVICES_CONFIG_BUILDER_CLASS); + private static final boolean CONFIG_BUILDER_AVAILABLE = isClassAvailable(IO_QUARKUS_DEVSERVICES_CONFIG_BUILDER_CLASS); private String name; private String description; @@ -239,6 +258,7 @@ public static class OwnedServiceBuilder { private Supplier startableSupplier; private Consumer postStartAction; private Map> applicationConfigProvider; + private Set highPriorityConfig; public OwnedServiceBuilder name(String name) { this.name = name; @@ -286,6 +306,15 @@ public OwnedServiceBuilder postStartHook(Consumer postStartAction) { return this; } + /** + * @deprecated Subject to changes due to #51209 + */ + @Deprecated(forRemoval = true) + public OwnedServiceBuilder highPriorityConfig(Set highPriorityConfig) { + this.highPriorityConfig = highPriorityConfig; + return this; + } + /** * Provides config to inject into the config system. If you've got values that don't change, use config(), and if you've * got values that you'll only know after starting the container, use configProvider() and provide a map of @@ -306,7 +335,7 @@ public DevServicesResultBuildItem build() { return new DevServicesResultBuildItem(name, description, serviceName, serviceConfig, config, (Supplier) startableSupplier, (Consumer) postStartAction, - applicationConfigProvider); + applicationConfigProvider, highPriorityConfig); } private static boolean isClassAvailable(String className) { diff --git a/core/runtime/src/main/java/io/quarkus/devservices/crossclassloader/runtime/RunningService.java b/core/runtime/src/main/java/io/quarkus/devservices/crossclassloader/runtime/RunningService.java index 479445a5891ad..31ac7dd66bf9c 100644 --- a/core/runtime/src/main/java/io/quarkus/devservices/crossclassloader/runtime/RunningService.java +++ b/core/runtime/src/main/java/io/quarkus/devservices/crossclassloader/runtime/RunningService.java @@ -16,14 +16,17 @@ public final class RunningService implements Closeable { private final String feature; private final String description; private final Map configs; + private final Map overrideConfigs; private final String containerId; private final Closeable closeable; - public RunningService(String feature, String description, Map configs, String containerId, + public RunningService(String feature, String description, Map configs, Map overrideConfig, + String containerId, Closeable closeable) { this.feature = feature; this.description = description; this.configs = configs; + this.overrideConfigs = overrideConfig; this.containerId = containerId; this.closeable = closeable; } @@ -45,8 +48,15 @@ public Map configs() { return configs; } + /** + * @deprecated Subject to changes due to #51209 + */ + @Deprecated(forRemoval = true) + public Map overrideConfigs() { + return overrideConfigs; + } + public String containerId() { return containerId; } - } diff --git a/extensions/amazon-lambda/common-deployment/src/main/java/io/quarkus/amazon/lambda/deployment/DevServicesLambdaProcessor.java b/extensions/amazon-lambda/common-deployment/src/main/java/io/quarkus/amazon/lambda/deployment/DevServicesLambdaProcessor.java index e7c71bcedd7a3..e340ba29ce48d 100644 --- a/extensions/amazon-lambda/common-deployment/src/main/java/io/quarkus/amazon/lambda/deployment/DevServicesLambdaProcessor.java +++ b/extensions/amazon-lambda/common-deployment/src/main/java/io/quarkus/amazon/lambda/deployment/DevServicesLambdaProcessor.java @@ -5,12 +5,15 @@ import java.io.IOException; import java.util.Map; import java.util.Optional; +import java.util.Set; +import org.eclipse.microprofile.config.ConfigProvider; import org.jboss.logging.Logger; import io.quarkus.amazon.lambda.runtime.AmazonLambdaApi; import io.quarkus.amazon.lambda.runtime.LambdaHotReplacementRecorder; import io.quarkus.amazon.lambda.runtime.MockEventServer; +import io.quarkus.amazon.lambda.runtime.MockEventServerConfig; import io.quarkus.deployment.Feature; import io.quarkus.deployment.IsLiveReloadSupportedByLaunchMode; import io.quarkus.deployment.IsProduction; @@ -51,7 +54,7 @@ private boolean legacyTestingEnabled() { @Produce(ServiceStartBuildItem.class) @BuildStep(onlyIfNot = IsProduction.class) // This is required for testing so run it even if devservices.enabled=false public void startEventServer(LaunchModeBuildItem launchModeBuildItem, - LambdaConfig config, + LambdaBuildConfig config, Optional override, BuildProducer devServicePropertiesProducer) { LaunchMode launchMode = launchModeBuildItem.getLaunchMode(); @@ -70,9 +73,8 @@ public void startEventServer(LaunchModeBuildItem launchModeBuildItem, server = new MockEventServer(); } - int configuredPort = launchMode == LaunchMode.TEST ? config.mockEventServer().testPort() - : config.mockEventServer().devPort(); - String portPropertySuffix = launchMode == LaunchMode.TEST ? "test-port" : "dev-port"; + boolean isTest = launchMode == LaunchMode.TEST; + String portPropertySuffix = isTest ? "test-port" : "dev-port"; String propName = "quarkus.lambda.mock-event-server." + portPropertySuffix; // No compose support, and no using of external services, so no need to discover existing services @@ -80,8 +82,8 @@ public void startEventServer(LaunchModeBuildItem launchModeBuildItem, DevServicesResultBuildItem buildItem = DevServicesResultBuildItem.owned().feature(Feature.AMAZON_LAMBDA) .serviceName(Feature.AMAZON_LAMBDA.getName()) .serviceConfig(config) - .startable(() -> new StartableEventServer( - server, configuredPort)) + .startable(() -> new StartableEventServer(server, propName, isTest)) + .highPriorityConfig(Set.of(propName)) // Pass through the external config for the port, so that it can be overridden if it's an ephemeral port .configProvider( Map.of(propName, s -> String.valueOf(s.getExposedPort()), AmazonLambdaApi.QUARKUS_INTERNAL_AWS_LAMBDA_TEST_API, @@ -93,17 +95,26 @@ public void startEventServer(LaunchModeBuildItem launchModeBuildItem, } private static class StartableEventServer implements Startable { - private final MockEventServer server; - private final int configuredPort; + private final String propName; + private final boolean isTest; - public StartableEventServer(MockEventServer server, int configuredPort) { + public StartableEventServer(MockEventServer server, String propName, boolean isTest) { this.server = server; - this.configuredPort = configuredPort; + this.propName = propName; + this.isTest = isTest; } @Override public void start() { + // Technically, we shouldn't peek at the runtime config, but every dev service does it + // However, we won't get defaults, so we need to fill our own in + int port = isTest ? Integer.parseInt(MockEventServerConfig.TEST_PORT) + : Integer.parseInt(MockEventServerConfig.DEV_PORT); + int configuredPort = ConfigProvider.getConfig().getOptionalValue(propName, Integer.class) + .or(() -> Optional.of(port)) + .get(); + server.start(configuredPort); log.debugf("Starting event server on port %d", configuredPort); } diff --git a/extensions/amazon-lambda/common-deployment/src/main/java/io/quarkus/amazon/lambda/deployment/LambdaBuildConfig.java b/extensions/amazon-lambda/common-deployment/src/main/java/io/quarkus/amazon/lambda/deployment/LambdaBuildConfig.java new file mode 100644 index 0000000000000..cb606291c8c7a --- /dev/null +++ b/extensions/amazon-lambda/common-deployment/src/main/java/io/quarkus/amazon/lambda/deployment/LambdaBuildConfig.java @@ -0,0 +1,16 @@ +package io.quarkus.amazon.lambda.deployment; + +import io.quarkus.runtime.annotations.ConfigPhase; +import io.quarkus.runtime.annotations.ConfigRoot; +import io.smallrye.config.ConfigMapping; + +@ConfigRoot(phase = ConfigPhase.BUILD_TIME) +@ConfigMapping(prefix = "quarkus.lambda") +public interface LambdaBuildConfig { + + /** + * Configuration for the mock event server that is run + * in dev mode and test mode + */ + MockEventServerBuildConfig mockEventServer(); +} diff --git a/extensions/amazon-lambda/common-deployment/src/main/java/io/quarkus/amazon/lambda/deployment/MockEventServerConfig.java b/extensions/amazon-lambda/common-deployment/src/main/java/io/quarkus/amazon/lambda/deployment/MockEventServerBuildConfig.java similarity index 55% rename from extensions/amazon-lambda/common-deployment/src/main/java/io/quarkus/amazon/lambda/deployment/MockEventServerConfig.java rename to extensions/amazon-lambda/common-deployment/src/main/java/io/quarkus/amazon/lambda/deployment/MockEventServerBuildConfig.java index c2aa666e9810f..4c6094e383e4b 100644 --- a/extensions/amazon-lambda/common-deployment/src/main/java/io/quarkus/amazon/lambda/deployment/MockEventServerConfig.java +++ b/extensions/amazon-lambda/common-deployment/src/main/java/io/quarkus/amazon/lambda/deployment/MockEventServerBuildConfig.java @@ -6,22 +6,11 @@ * Configuration for the mock event server that is run * in dev mode and test mode */ -public interface MockEventServerConfig { +public interface MockEventServerBuildConfig { /** * Setting to true will start event server even if quarkus.devservices.enabled=false */ @WithDefault("true") boolean enabled(); - /** - * Port to access mock event server in dev mode - */ - @WithDefault("8080") - int devPort(); - - /** - * Port to access mock event server in dev mode - */ - @WithDefault("8081") - int testPort(); } diff --git a/extensions/amazon-lambda/common-deployment/src/main/java/io/quarkus/amazon/lambda/deployment/LambdaConfig.java b/extensions/amazon-lambda/common-runtime/src/main/java/io/quarkus/amazon/lambda/runtime/LambdaConfig.java similarity index 80% rename from extensions/amazon-lambda/common-deployment/src/main/java/io/quarkus/amazon/lambda/deployment/LambdaConfig.java rename to extensions/amazon-lambda/common-runtime/src/main/java/io/quarkus/amazon/lambda/runtime/LambdaConfig.java index 36c15e3e3a77e..bc73bc8e36f9b 100644 --- a/extensions/amazon-lambda/common-deployment/src/main/java/io/quarkus/amazon/lambda/deployment/LambdaConfig.java +++ b/extensions/amazon-lambda/common-runtime/src/main/java/io/quarkus/amazon/lambda/runtime/LambdaConfig.java @@ -1,10 +1,10 @@ -package io.quarkus.amazon.lambda.deployment; +package io.quarkus.amazon.lambda.runtime; import io.quarkus.runtime.annotations.ConfigPhase; import io.quarkus.runtime.annotations.ConfigRoot; import io.smallrye.config.ConfigMapping; -@ConfigRoot(phase = ConfigPhase.BUILD_TIME) +@ConfigRoot(phase = ConfigPhase.RUN_TIME) @ConfigMapping(prefix = "quarkus.lambda") public interface LambdaConfig { @@ -13,4 +13,5 @@ public interface LambdaConfig { * in dev mode and test mode */ MockEventServerConfig mockEventServer(); + } diff --git a/extensions/amazon-lambda/common-runtime/src/main/java/io/quarkus/amazon/lambda/runtime/MockEventServerConfig.java b/extensions/amazon-lambda/common-runtime/src/main/java/io/quarkus/amazon/lambda/runtime/MockEventServerConfig.java new file mode 100644 index 0000000000000..cd56f4bbc3cdd --- /dev/null +++ b/extensions/amazon-lambda/common-runtime/src/main/java/io/quarkus/amazon/lambda/runtime/MockEventServerConfig.java @@ -0,0 +1,29 @@ +package io.quarkus.amazon.lambda.runtime; + +import io.quarkus.runtime.annotations.ConfigPhase; +import io.quarkus.runtime.annotations.ConfigRoot; +import io.smallrye.config.ConfigMapping; +import io.smallrye.config.WithDefault; + +/** + * Configuration for the mock event server that is run + * in dev mode and test mode + */ +@ConfigRoot(phase = ConfigPhase.RUN_TIME) +@ConfigMapping(prefix = "quarkus.lambda") +public interface MockEventServerConfig { + static final String DEV_PORT = "8080"; + static final String TEST_PORT = "8081"; + + /** + * Port to access mock event server in dev mode + */ + @WithDefault(DEV_PORT) + int devPort(); + + /** + * Port to access mock event server in dev mode + */ + @WithDefault(TEST_PORT) + int testPort(); +} diff --git a/extensions/devservices/deployment/src/main/java/io/quarkus/devservices/deployment/DevServicesProcessor.java b/extensions/devservices/deployment/src/main/java/io/quarkus/devservices/deployment/DevServicesProcessor.java index 4f0de87a56715..3413a1f318392 100644 --- a/extensions/devservices/deployment/src/main/java/io/quarkus/devservices/deployment/DevServicesProcessor.java +++ b/extensions/devservices/deployment/src/main/java/io/quarkus/devservices/deployment/DevServicesProcessor.java @@ -154,19 +154,18 @@ DevServicesRegistryBuildItem devServicesRegistry(LaunchModeBuildItem launchMode, // Because the devservices runtime module is new and the ecosystem needs time to catch up, don't die if the runtime module isn't available @BuildStep(onlyIf = { IsRuntimeModuleAvailable.class }) - public RunTimeConfigBuilderBuildItem registerDevResourcesConfigSource( - List devServicesRequestBuildItems) { + public void registerDevResourcesConfigSource(BuildProducer runtimeConfigBuilders) { // Once all the dev services are registered, we can share config try { // Use reflection, since we don't have an explicit dependency on the runtime module (because dependent extensions may not have that dependency) - Class builderClass = Class - .forName(IO_QUARKUS_DEVSERVICES_CONFIG_BUILDER_CLASS); - return new RunTimeConfigBuilderBuildItem(builderClass); + runtimeConfigBuilders + .produce(new RunTimeConfigBuilderBuildItem(Class.forName(IO_QUARKUS_DEVSERVICES_CONFIG_BUILDER_CLASS))); + runtimeConfigBuilders.produce(new RunTimeConfigBuilderBuildItem( + Class.forName("io.quarkus.devservice.runtime.config.DevServicesOverrideConfigBuilder"))); } catch (ClassNotFoundException e) { // Should never happen, because of the guard, as long as the runtime module is not a direct dependency of this module throw new RuntimeException(e); } - } @BuildStep(onlyIf = { IsDevelopment.class, DevServicesConfig.Enabled.class }) diff --git a/extensions/devservices/runtime/src/main/java/io/quarkus/devservice/runtime/config/DevServicesConfigBuilder.java b/extensions/devservices/runtime/src/main/java/io/quarkus/devservice/runtime/config/DevServicesConfigBuilder.java index 1aaebfd229194..762364c41cc40 100644 --- a/extensions/devservices/runtime/src/main/java/io/quarkus/devservice/runtime/config/DevServicesConfigBuilder.java +++ b/extensions/devservices/runtime/src/main/java/io/quarkus/devservice/runtime/config/DevServicesConfigBuilder.java @@ -13,11 +13,6 @@ public SmallRyeConfigBuilder configBuilder(SmallRyeConfigBuilder builder) { @Override public int priority() { - // What's the right priority? This is a cheeky dynamic override, so a high priority seems correct, but dev services are supposed to fill in gaps in existing information. - // Dev services should be looking at those sources and not doing anything if there's existing config, - // so a very low priority is also arguably correct. - // In principle the priority actually shouldn't matter much, but in practice it needs to not be higher than Arquillian config overrides or some tests fail - return 10; } } diff --git a/extensions/devservices/runtime/src/main/java/io/quarkus/devservice/runtime/config/DevServicesConfigSource.java b/extensions/devservices/runtime/src/main/java/io/quarkus/devservice/runtime/config/DevServicesConfigSource.java index 6bea4a2b25208..04cf8617aef0e 100644 --- a/extensions/devservices/runtime/src/main/java/io/quarkus/devservice/runtime/config/DevServicesConfigSource.java +++ b/extensions/devservices/runtime/src/main/java/io/quarkus/devservice/runtime/config/DevServicesConfigSource.java @@ -20,7 +20,6 @@ public DevServicesConfigSource(LaunchMode launchMode) { @Override public Set getPropertyNames() { - // We could make this more efficient by not invoking the supplier on the other end, but it would need a more complex interface Set names = new HashSet<>(); Set allConfig = RunningDevServicesRegistry.INSTANCE.getAllRunningServices(launchMode.name()); @@ -55,7 +54,6 @@ public String getName() { @Override public int getOrdinal() { - // See discussion on DevServicesConfigBuilder about what the right value here is - return 10; + return 240; // a bit less than application properties, because this config should only take effect if nothing is set } } diff --git a/extensions/devservices/runtime/src/main/java/io/quarkus/devservice/runtime/config/DevServicesOverrideConfigBuilder.java b/extensions/devservices/runtime/src/main/java/io/quarkus/devservice/runtime/config/DevServicesOverrideConfigBuilder.java new file mode 100644 index 0000000000000..fcf065c5f2891 --- /dev/null +++ b/extensions/devservices/runtime/src/main/java/io/quarkus/devservice/runtime/config/DevServicesOverrideConfigBuilder.java @@ -0,0 +1,22 @@ +package io.quarkus.devservice.runtime.config; + +import io.quarkus.runtime.LaunchMode; +import io.quarkus.runtime.configuration.ConfigBuilder; +import io.smallrye.config.SmallRyeConfigBuilder; + +/** + * @deprecated Subject to changes due to #51209 + */ +@Deprecated(forRemoval = true) +public class DevServicesOverrideConfigBuilder implements ConfigBuilder { + + @Override + public SmallRyeConfigBuilder configBuilder(SmallRyeConfigBuilder builder) { + return builder.withSources(new DevServicesOverrideConfigSource(LaunchMode.current())); + } + + @Override + public int priority() { + return 10; + } +} diff --git a/extensions/devservices/runtime/src/main/java/io/quarkus/devservice/runtime/config/DevServicesOverrideConfigSource.java b/extensions/devservices/runtime/src/main/java/io/quarkus/devservice/runtime/config/DevServicesOverrideConfigSource.java new file mode 100644 index 0000000000000..cd4c9c1554dd3 --- /dev/null +++ b/extensions/devservices/runtime/src/main/java/io/quarkus/devservice/runtime/config/DevServicesOverrideConfigSource.java @@ -0,0 +1,65 @@ +package io.quarkus.devservice.runtime.config; + +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import org.eclipse.microprofile.config.spi.ConfigSource; + +import io.quarkus.devservices.crossclassloader.runtime.RunningDevServicesRegistry; +import io.quarkus.devservices.crossclassloader.runtime.RunningService; +import io.quarkus.runtime.LaunchMode; + +/** + * @deprecated Subject to changes due to #51209 + */ +@Deprecated(forRemoval = true) +public class DevServicesOverrideConfigSource implements ConfigSource { + + private final LaunchMode launchMode; + + public DevServicesOverrideConfigSource(LaunchMode launchMode) { + this.launchMode = launchMode; + } + + @Override + public Set getPropertyNames() { + // We could make this more efficient by not invoking the supplier on the other end, but it would need a more complex interface + Set names = new HashSet<>(); + + Set allConfig = RunningDevServicesRegistry.INSTANCE.getAllRunningServices(launchMode.name()); + if (allConfig != null) { + for (RunningService service : allConfig) { + Map config = service.overrideConfigs(); + names.addAll(config.keySet()); + } + } + return names; + } + + @Override + public String getValue(String propertyName) { + Set allConfig = RunningDevServicesRegistry.INSTANCE.getAllRunningServices(launchMode.name()); + if (allConfig != null) { + for (RunningService service : allConfig) { + Map config = service.overrideConfigs(); + String answer = config.get(propertyName); + if (answer != null) { + return answer; + } + } + } + return null; + } + + @Override + public String getName() { + return "DevServicesOverrideConfigSource"; + } + + @Override + public int getOrdinal() { + // This is a dynamic override, so it needs a high priority to be able to override ephemeral port (0) with real values + return 410; // a bit more than system properties + } +} diff --git a/integration-tests/amazon-lambda/src/test/java/io/quarkus/it/amazon/lambda/AmazonLambdaEphemeralPortTestCase.java b/integration-tests/amazon-lambda/src/test/java/io/quarkus/it/amazon/lambda/AmazonLambdaEphemeralPortTestCase.java index 18b5999fdcab2..acdefad0883da 100644 --- a/integration-tests/amazon-lambda/src/test/java/io/quarkus/it/amazon/lambda/AmazonLambdaEphemeralPortTestCase.java +++ b/integration-tests/amazon-lambda/src/test/java/io/quarkus/it/amazon/lambda/AmazonLambdaEphemeralPortTestCase.java @@ -3,14 +3,12 @@ import static io.restassured.RestAssured.given; import static org.hamcrest.CoreMatchers.containsString; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import io.quarkus.it.amazon.lambda.profiles.EphemeralPortProfile; import io.quarkus.test.junit.QuarkusTest; import io.quarkus.test.junit.TestProfile; -@Disabled("Tracked by https://github.com/quarkusio/quarkus/issues/48784") @TestProfile(EphemeralPortProfile.class) @QuarkusTest public class AmazonLambdaEphemeralPortTestCase {