From 1da5a0bfde1fd1eebda4395112a394abec8b87a6 Mon Sep 17 00:00:00 2001 From: Jose Date: Thu, 8 Jun 2023 13:06:51 +0200 Subject: [PATCH] Print messages about ports that can't change at runtime for K8s Related to https://github.com/quarkusio/quarkus/issues/33307, task 3. Fix https://github.com/quarkusio/quarkus/issues/32882 --- .../deployment/KubernetesCommonHelper.java | 29 ++++--- .../deployment/OpenshiftProcessor.java | 4 + .../deployment/RuntimePropertiesUtil.java | 84 +++++++++++++++++++ .../VanillaKubernetesProcessor.java | 5 +- 4 files changed, 110 insertions(+), 12 deletions(-) create mode 100644 extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/RuntimePropertiesUtil.java diff --git a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/KubernetesCommonHelper.java b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/KubernetesCommonHelper.java index dadabe7723868..aa827427b2566 100644 --- a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/KubernetesCommonHelper.java +++ b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/KubernetesCommonHelper.java @@ -25,8 +25,6 @@ import java.util.Set; import java.util.stream.Collectors; -import org.eclipse.microprofile.config.ConfigProvider; - import io.dekorate.kubernetes.config.Annotation; import io.dekorate.kubernetes.config.ConfigMapVolumeBuilder; import io.dekorate.kubernetes.config.EnvBuilder; @@ -103,7 +101,6 @@ import io.quarkus.kubernetes.spi.Subject; public class KubernetesCommonHelper { - private static final String ANY = null; private static final String OUTPUT_ARTIFACT_FORMAT = "%s%s.jar"; private static final String[] PROMETHEUS_ANNOTATION_TARGETS = { "Service", @@ -187,13 +184,14 @@ public static Map combinePorts(List ports : buildItemPort.getPath()) .build(); - // Special handling for ports with name "https". We look up the container port from the Quarkus configuration. - if ("https".equals(name) && combinedPort.getContainerPort() == null) { - int containerPort = ConfigProvider.getConfig() - .getOptionalValue("quarkus.http.ssl-port", Integer.class) - .orElse(8443); - - combinedPort = new PortBuilder(combinedPort).withContainerPort(containerPort).build(); + // Special handling for ports with mapped configuration. We look up the container port from the Quarkus configuration. + if (combinedPort.getContainerPort() == null) { + Integer containerPort = RuntimePropertiesUtil.getPortNumberFromRuntime(name); + if (containerPort != null) { + combinedPort = new PortBuilder(combinedPort) + .withContainerPort(containerPort) + .build(); + } } allPorts.put(name, combinedPort); @@ -201,6 +199,17 @@ public static Map combinePorts(List ports return allPorts; } + /** + * Creates the configurator build items. + */ + public static void printMessageAboutPortsThatCantChange(String target, List ports, + PlatformConfiguration config) { + Collection allPorts = combinePorts(ports, config).values(); + for (Port port : allPorts) { + RuntimePropertiesUtil.printTraceIfRuntimePropertyIsSet(target, port); + } + } + /** * Creates the common decorator build items. */ diff --git a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/OpenshiftProcessor.java b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/OpenshiftProcessor.java index ab3f4f5a3fb7d..386ef5302527a 100644 --- a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/OpenshiftProcessor.java +++ b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/OpenshiftProcessor.java @@ -9,6 +9,7 @@ import static io.quarkus.kubernetes.deployment.Constants.READINESS_PROBE; import static io.quarkus.kubernetes.deployment.Constants.ROUTE; import static io.quarkus.kubernetes.deployment.Constants.STARTUP_PROBE; +import static io.quarkus.kubernetes.deployment.KubernetesCommonHelper.printMessageAboutPortsThatCantChange; import static io.quarkus.kubernetes.deployment.KubernetesConfigUtil.MANAGEMENT_PORT_NAME; import static io.quarkus.kubernetes.deployment.KubernetesConfigUtil.managementPortIsEnabled; import static io.quarkus.kubernetes.deployment.OpenshiftConfig.OpenshiftFlavor.v3; @@ -375,6 +376,9 @@ public List createDecorators(ApplicationInfoBuildItem applic || !config.route.targetPort.equals(MANAGEMENT_PORT_NAME))) { result.add(new DecoratorBuildItem(OPENSHIFT, new RemovePortFromServiceDecorator(name, MANAGEMENT_PORT_NAME))); } + + printMessageAboutPortsThatCantChange(OPENSHIFT, ports, config); + return result; } diff --git a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/RuntimePropertiesUtil.java b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/RuntimePropertiesUtil.java new file mode 100644 index 0000000000000..a559a2492e76d --- /dev/null +++ b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/RuntimePropertiesUtil.java @@ -0,0 +1,84 @@ +package io.quarkus.kubernetes.deployment; + +import java.util.Map; +import java.util.Optional; + +import org.eclipse.microprofile.config.ConfigProvider; +import org.jboss.logging.Logger; + +import io.dekorate.kubernetes.config.Port; + +/** + * Utility to handling runtime properties. + */ +public final class RuntimePropertiesUtil { + + private static final Logger LOG = Logger.getLogger(KubernetesDeployer.class); + + private static final String CONTAINER_PORT = "the container port"; + /** + * List of ports that are linked to a runtime property. + */ + private static final Map PORTS = Map.of( + "http", new RuntimeProperty<>("quarkus.http.port", Integer.class, 8080, CONTAINER_PORT), + "https", new RuntimeProperty<>("quarkus.http.ssl-port", Integer.class, 8443, CONTAINER_PORT), + "management", new RuntimeProperty<>("quarkus.management.port", Integer.class, 9000, CONTAINER_PORT)); + + private RuntimePropertiesUtil() { + + } + + /** + * This method will return the port number that is configured from the runtime property. If the runtime property does not + * return any value, it will return the default value that is defined in the {@link RuntimePropertiesUtil#PORTS} map. + */ + public static Integer getPortNumberFromRuntime(String name) { + RuntimeProperty runtimeProperty = PORTS.get(name); + if (runtimeProperty != null) { + return (Integer) runtimeProperty.getValue().orElse(runtimeProperty.defaultValue); + } + + return null; + } + + /** + * This method will trace an informative message to let users know that runtime properties that are used in the generated + * resources by Kubernetes can't change again at runtime. + * + * For example, for users that set a runtime property "quarkus.http.port=9000" at build time, Kubernetes will use this value + * in the generated resources. Then, when running the application in Kubernetes, if users try to modify again the + * runtime property "quarkus.http.port" to a different value, this won't work because the generated resources already took + * the 9000 value. + * + * Note that this message won't be printed if the users didn't provide the runtime property at build time. + */ + public static void printTraceIfRuntimePropertyIsSet(String target, Port port) { + RuntimeProperty runtimeProperty = PORTS.get(port.getName()); + if (runtimeProperty != null) { + var runtimePropertyValue = runtimeProperty.getValue(); + if (runtimePropertyValue.isPresent()) { + LOG.info(String.format("The '%s' manifests are generated with %s '%s' having value '%d'. " + + "The app and manifests will get out of sync if the property '%s' is changed at runtime.", + target, runtimeProperty.usage, port.getName(), port.getContainerPort(), runtimeProperty.propertyName)); + } + } + } + + private static class RuntimeProperty { + private final String propertyName; + private final Class clazzOfValue; + private final T defaultValue; + private final String usage; + + public RuntimeProperty(String propertyName, Class clazzOfValue, T defaultValue, String usage) { + this.propertyName = propertyName; + this.clazzOfValue = clazzOfValue; + this.defaultValue = defaultValue; + this.usage = usage; + } + + public Optional getValue() { + return ConfigProvider.getConfig().getOptionalValue(propertyName, clazzOfValue); + } + } +} diff --git a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/VanillaKubernetesProcessor.java b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/VanillaKubernetesProcessor.java index fa1f8e9e38dc6..74e197ac406f0 100644 --- a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/VanillaKubernetesProcessor.java +++ b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/VanillaKubernetesProcessor.java @@ -8,6 +8,7 @@ import static io.quarkus.kubernetes.deployment.Constants.LIVENESS_PROBE; import static io.quarkus.kubernetes.deployment.Constants.READINESS_PROBE; import static io.quarkus.kubernetes.deployment.Constants.STARTUP_PROBE; +import static io.quarkus.kubernetes.deployment.KubernetesCommonHelper.printMessageAboutPortsThatCantChange; import static io.quarkus.kubernetes.deployment.KubernetesConfigUtil.MANAGEMENT_PORT_NAME; import static io.quarkus.kubernetes.deployment.KubernetesConfigUtil.managementPortIsEnabled; import static io.quarkus.kubernetes.spi.KubernetesDeploymentTargetBuildItem.VANILLA_KUBERNETES_PRIORITY; @@ -298,7 +299,7 @@ public List createDecorators(ApplicationInfoBuildItem applic .withMaxUnavailable(config.rollingUpdate.maxUnavailable) .build()))); } - + printMessageAboutPortsThatCantChange(KUBERNETES, ports, config); return result; } @@ -320,4 +321,4 @@ void externalizeInitTasks( jobs, initContainers, env, roles, roleBindings, decorators); } } -} \ No newline at end of file +}