Skip to content

Commit

Permalink
Add HTTPS port in generated containers by Kubernetes
Browse files Browse the repository at this point in the history
These changes will add an additional container port HTTPS in the generated manifests:

```yaml
containers:
        - image: ...
          imagePullPolicy: IfNotPresent
          name: kubernetes-kind
          ports:
            - containerPort: 8080
              name: http
              protocol: TCP
            - containerPort: 8443
              name: https
              protocol: TCP
```

By default, the Ingress and Route resources will use the "http" port. However, as part of these changes, I've added a new property to select between "https" or "http", or any other that user might have added as part of the configuration. Example:

```
quarkus.kubernetes.ingress.target-port=https
quarkus.openshift.route.target-port=https
```

Finally, note that the https container won't be added for the Knative resources because of the following Dekorate issue: dekorateio/dekorate#1119. 
Also, that the nodeport for Kind and Minikube resources will only be added for the http ports due to this Dekorate limitation: dekorateio/dekorate#1120.
Both issues should be addressed in Dekorate and then fixed in a later pull requested.

Fix #29999
  • Loading branch information
Sgitario committed Jan 11, 2023
1 parent c0ac21a commit 987572e
Show file tree
Hide file tree
Showing 31 changed files with 299 additions and 91 deletions.
1 change: 1 addition & 0 deletions docs/src/main/asciidoc/deploying-to-kubernetes.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -770,6 +770,7 @@ To secure the incoming connections, Kubernetes allows enabling https://kubernete
[source]
----
quarkus.kubernetes.ingress.expose=true
quarkus.kubernetes.ingress.target-port=https
## Ingress TLS configuration:
quarkus.kubernetes.ingress.tls.my-secret.enabled=true
----
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,9 +81,8 @@ public void createLabels(KubernetesConfig config, BuildProducer<KubernetesLabelB
public List<ConfiguratorBuildItem> createConfigurators(KubernetesConfig config,
List<KubernetesPortBuildItem> ports) {
List<ConfiguratorBuildItem> result = new ArrayList<>();
KubernetesCommonHelper.combinePorts(ports, config).entrySet().forEach(e -> {
result.add(new ConfiguratorBuildItem(new AddPortToKubernetesConfig(e.getValue())));
});
KubernetesCommonHelper.combinePorts(ports, config).values()
.forEach(value -> result.add(new ConfiguratorBuildItem(new AddPortToKubernetesConfig(value))));
return result;
}

Expand All @@ -106,9 +105,16 @@ public List<DecoratorBuildItem> createDecorators(ApplicationInfoBuildItem applic
List<KubernetesRoleBindingBuildItem> roleBindings,
Optional<CustomProjectRootBuildItem> customProjectRoot) {

return DevClusterHelper.createDecorators(KIND, applicationInfo, outputTarget, config, packageConfig,
List<DecoratorBuildItem> result = DevClusterHelper.createDecorators(KIND, applicationInfo, outputTarget, config,
packageConfig,
metricsConfiguration, annotations, labels, envs, baseImage, image, command, ports, livenessPath, readinessPath,
roles, roleBindings, customProjectRoot);

// TODO: Dekorate sets the same nodeport for all the ports: https://github.com/dekorateio/dekorate/issues/1120
// so we need to only keep the one for http
String name = ResourceNameUtil.getResourceName(config, applicationInfo);
result.add(new DecoratorBuildItem(KIND, new RemoveNodePortForNotHttpPorts(name)));
return result;
}

@BuildStep
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package io.quarkus.kind.deployment;

import io.dekorate.kind.decorator.ApplyPortToKindServiceDecorator;
import io.dekorate.kubernetes.decorator.Decorator;
import io.dekorate.kubernetes.decorator.NamedResourceDecorator;
import io.fabric8.kubernetes.api.model.ObjectMeta;
import io.fabric8.kubernetes.api.model.ServicePortFluent;

public class RemoveNodePortForNotHttpPorts extends NamedResourceDecorator<ServicePortFluent> {
public RemoveNodePortForNotHttpPorts(String name) {
super(name);
}

@Override
public void andThenVisit(ServicePortFluent servicePort, ObjectMeta objectMeta) {
if (servicePort.hasNodePort() && !servicePort.getName().equals("http")) {
servicePort.withNodePort(null);
}
}

@Override
public Class<? extends Decorator>[] after() {
return new Class[] { ApplyPortToKindServiceDecorator.class };
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import static io.quarkus.kubernetes.deployment.Constants.*;

import java.util.Optional;
import java.util.function.Predicate;

import org.jboss.logging.Logger;
Expand All @@ -18,13 +17,9 @@ public class AddNodePortDecorator extends NamedResourceDecorator<ServiceSpecFlue
private static final Logger log = Logger.getLogger(AddNodePortDecorator.class);

private final int nodePort;
private final Optional<String> matchingPortName;
private final String matchingPortName;

public AddNodePortDecorator(String name, int nodePort) {
this(name, nodePort, Optional.empty());
}

public AddNodePortDecorator(String name, int nodePort, Optional<String> matchingPortName) {
public AddNodePortDecorator(String name, int nodePort, String matchingPortName) {
super(name);
if (nodePort < MIN_NODE_PORT_VALUE || nodePort > MAX_NODE_PORT_VALUE) {
log.info("Using a port outside of the " + MIN_NODE_PORT_VALUE + "-" + MAX_NODE_PORT_VALUE
Expand All @@ -37,18 +32,12 @@ public AddNodePortDecorator(String name, int nodePort, Optional<String> matching
@SuppressWarnings("unchecked")
@Override
public void andThenVisit(ServiceSpecFluent service, ObjectMeta resourceMeta) {
ServiceSpecFluent.PortsNested<?> editPort;
if (matchingPortName.isPresent()) {
editPort = service.editMatchingPort(new Predicate<ServicePortBuilder>() {
@Override
public boolean test(ServicePortBuilder servicePortBuilder) {
return (servicePortBuilder.hasName())
&& (servicePortBuilder.getName().equals(matchingPortName.get()));
}
});
} else {
editPort = service.editFirstPort();
}
ServiceSpecFluent.PortsNested<?> editPort = service.editMatchingPort(new Predicate<ServicePortBuilder>() {
@Override
public boolean test(ServicePortBuilder servicePortBuilder) {
return servicePortBuilder.hasName() && servicePortBuilder.getName().equals(matchingPortName);
}
});
editPort.withNodePort(nodePort);
editPort.endPort();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package io.quarkus.kubernetes.deployment;

import java.util.Optional;

import io.dekorate.kubernetes.config.IngressRule;
import io.dekorate.kubernetes.config.Port;
import io.dekorate.kubernetes.decorator.AddIngressRuleDecorator;

/**
* TODO: Workaround for https://github.com/dekorateio/dekorate/issues/1123 where all the ports are being configured as rules.
*/
public class ApplyIngressRuleDecorator extends AddIngressRuleDecorator {
public ApplyIngressRuleDecorator(String name, Optional<Port> defaultHostPort, IngressRule rule) {
super(name, defaultHostPort, rule);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,7 @@ public void visit(OpenshiftConfigFluent config) {
routeBuilder.withHost(routeConfig.host.get());
}

if (routeConfig.targetPort.isPresent()) {
routeBuilder.withTargetPort(routeConfig.targetPort.get());
}

routeBuilder.withTargetPort(routeConfig.targetPort);
routeBuilder.endRoute();
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
package io.quarkus.kubernetes.deployment;

import static io.quarkus.kubernetes.deployment.Constants.DEFAULT_HTTP_PORT;
import static io.quarkus.kubernetes.deployment.Constants.HTTP_PORT;
import static io.quarkus.kubernetes.deployment.Constants.KUBERNETES;
import static io.quarkus.kubernetes.deployment.Constants.MAX_NODE_PORT_VALUE;
import static io.quarkus.kubernetes.deployment.Constants.MAX_PORT_NUMBER;
Expand All @@ -21,6 +20,7 @@

import io.dekorate.kubernetes.annotation.ServiceType;
import io.dekorate.kubernetes.config.EnvBuilder;
import io.dekorate.kubernetes.config.Port;
import io.dekorate.kubernetes.decorator.AddEnvVarDecorator;
import io.dekorate.kubernetes.decorator.ApplicationContainerDecorator;
import io.dekorate.kubernetes.decorator.ApplyImagePullPolicyDecorator;
Expand Down Expand Up @@ -71,10 +71,11 @@ public static List<DecoratorBuildItem> createDecorators(String clusterKind,

Optional<Project> project = KubernetesCommonHelper.createProject(applicationInfo, customProjectRoot, outputTarget,
packageConfig);
Optional<Port> port = KubernetesCommonHelper.getPort(ports, config);
result.addAll(KubernetesCommonHelper.createDecorators(project, clusterKind, name, config,
metricsConfiguration,
annotations, labels, command,
ports, livenessPath, readinessPath, roles, roleBindings));
port, livenessPath, readinessPath, roles, roleBindings));

image.ifPresent(i -> {
result.add(new DecoratorBuildItem(clusterKind, new ApplyContainerImageDecorator(name, i.getImage())));
Expand Down Expand Up @@ -102,17 +103,19 @@ public static List<DecoratorBuildItem> createDecorators(String clusterKind,
if (!nodeConfigPorts.isEmpty()) {
for (Map.Entry<String, PortConfig> entry : nodeConfigPorts) {
result.add(new DecoratorBuildItem(KUBERNETES,
new AddNodePortDecorator(name, entry.getValue().nodePort.getAsInt(), Optional.of(entry.getKey()))));
new AddNodePortDecorator(name, entry.getValue().nodePort.getAsInt(), entry.getKey())));
}
} else {
result.add(new DecoratorBuildItem(clusterKind, new AddNodePortDecorator(name, config.getNodePort()
.orElseGet(() -> getStablePortNumberInRange(name, MIN_NODE_PORT_VALUE, MAX_NODE_PORT_VALUE)))));
result.add(new DecoratorBuildItem(clusterKind,
new AddNodePortDecorator(name,
config.getNodePort().orElseGet(
() -> getStablePortNumberInRange(name, MIN_NODE_PORT_VALUE, MAX_NODE_PORT_VALUE)),
config.ingress.targetPort)));
}

//Probe port handling
Integer port = ports.stream().filter(p -> HTTP_PORT.equals(p.getName())).map(KubernetesPortBuildItem::getPort)
.findFirst().orElse(DEFAULT_HTTP_PORT);
result.add(new DecoratorBuildItem(clusterKind, new ApplyHttpGetActionPortDecorator(name, name, port)));
Integer portNumber = port.map(Port::getContainerPort).orElse(DEFAULT_HTTP_PORT);
result.add(new DecoratorBuildItem(clusterKind, new ApplyHttpGetActionPortDecorator(name, name, portNumber)));

return result;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,13 @@ public class IngressConfig {
@ConfigItem
Optional<String> ingressClassName;

/**
* The default target named port. If not provided, it will be deducted from the Service resource ports.
* Options are: "http" and "https".
*/
@ConfigItem(defaultValue = "http")
String targetPort;

/**
* Custom annotations to add to exposition (route or ingress) resources
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import io.dekorate.knative.decorator.ApplyServiceAccountToRevisionSpecDecorator;
import io.dekorate.knative.decorator.ApplyTrafficDecorator;
import io.dekorate.kubernetes.config.EnvBuilder;
import io.dekorate.kubernetes.config.Port;
import io.dekorate.kubernetes.decorator.AddConfigMapDataDecorator;
import io.dekorate.kubernetes.decorator.AddConfigMapResourceProvidingDecorator;
import io.dekorate.kubernetes.decorator.AddEnvVarDecorator;
Expand Down Expand Up @@ -116,9 +117,11 @@ public void createLabels(KnativeConfig config, BuildProducer<KubernetesLabelBuil
@BuildStep
public List<ConfiguratorBuildItem> createConfigurators(KnativeConfig config, List<KubernetesPortBuildItem> ports) {
List<ConfiguratorBuildItem> result = new ArrayList<>();
KubernetesCommonHelper.combinePorts(ports, config).values().forEach(value -> {
result.add(new ConfiguratorBuildItem(new AddPortToKnativeConfig(value)));
});
KubernetesCommonHelper.combinePorts(ports, config).values()
// TODO: At the moment, Knative only supports http ports because this issue in Dekorate:
// https://github.com/dekorateio/dekorate/issues/1119
.stream().filter(p -> p.getName().equals("http"))
.forEach(value -> result.add(new ConfiguratorBuildItem(new AddPortToKnativeConfig(value))));
return result;
}

Expand Down Expand Up @@ -151,8 +154,9 @@ public List<DecoratorBuildItem> createDecorators(ApplicationInfoBuildItem applic
String name = ResourceNameUtil.getResourceName(config, applicationInfo);
Optional<Project> project = KubernetesCommonHelper.createProject(applicationInfo, customProjectRoot, outputTarget,
packageConfig);
Optional<Port> port = KubernetesCommonHelper.getPort(ports, config, "http");
result.addAll(KubernetesCommonHelper.createDecorators(project, KNATIVE, name, config, metricsConfiguration, annotations,
labels, command, ports, livenessPath, readinessPath, roles, roleBindings));
labels, command, port, livenessPath, readinessPath, roles, roleBindings));

image.ifPresent(i -> {
result.add(new DecoratorBuildItem(KNATIVE, new ApplyContainerImageDecorator(name, i.getImage())));
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@

package io.quarkus.kubernetes.deployment;

import static io.dekorate.kubernetes.decorator.AddServiceResourceDecorator.distinct;
import static io.quarkus.kubernetes.deployment.Constants.QUARKUS_ANNOTATIONS_BUILD_TIMESTAMP;
import static io.quarkus.kubernetes.deployment.Constants.QUARKUS_ANNOTATIONS_COMMIT_ID;
import static io.quarkus.kubernetes.deployment.Constants.QUARKUS_ANNOTATIONS_VCS_URL;
Expand Down Expand Up @@ -112,6 +113,23 @@ public static Optional<Project> createProject(ApplicationInfoBuildItem app,
}
}

/**
* Creates the configurator build items.
*/
public static Optional<Port> getPort(List<KubernetesPortBuildItem> ports, KubernetesConfig config) {
return getPort(ports, config, config.ingress.targetPort);
}

/**
* Creates the configurator build items.
*/
public static Optional<Port> getPort(List<KubernetesPortBuildItem> ports, PlatformConfiguration config, String targetPort) {
return combinePorts(ports, config).values().stream()
.filter(distinct(p -> p.getName()))
.filter(p -> p.getName().equals(targetPort))
.findFirst();
}

/**
* Creates the configurator build items.
*/
Expand Down Expand Up @@ -153,15 +171,15 @@ public static List<DecoratorBuildItem> createDecorators(Optional<Project> projec
List<KubernetesAnnotationBuildItem> annotations,
List<KubernetesLabelBuildItem> labels,
Optional<KubernetesCommandBuildItem> command,
List<KubernetesPortBuildItem> ports,
Optional<Port> port,
Optional<KubernetesHealthLivenessPathBuildItem> livenessProbePath,
Optional<KubernetesHealthReadinessPathBuildItem> readinessProbePath,
List<KubernetesRoleBuildItem> roles,
List<KubernetesRoleBindingBuildItem> roleBindings) {
List<DecoratorBuildItem> result = new ArrayList<>();

result.addAll(createLabelDecorators(project, target, name, config, labels));
result.addAll(createAnnotationDecorators(project, target, name, config, metricsConfiguration, annotations, ports));
result.addAll(createAnnotationDecorators(project, target, name, config, metricsConfiguration, annotations, port));
result.addAll(createPodDecorators(project, target, name, config));
result.addAll(createContainerDecorators(project, target, name, config));
result.addAll(createMountAndVolumeDecorators(project, target, name, config));
Expand All @@ -171,7 +189,7 @@ public static List<DecoratorBuildItem> createDecorators(Optional<Project> projec
result.addAll(createArgsDecorator(project, target, name, config, command));

//Handle Probes
if (!ports.isEmpty()) {
if (!port.isEmpty()) {
result.addAll(createProbeDecorators(name, target, config.getLivenessProbe(), config.getReadinessProbe(),
livenessProbePath, readinessProbePath));
}
Expand Down Expand Up @@ -428,7 +446,7 @@ private static List<DecoratorBuildItem> createAnnotationDecorators(Optional<Proj
PlatformConfiguration config,
Optional<MetricsCapabilityBuildItem> metricsConfiguration,
List<KubernetesAnnotationBuildItem> annotations,
List<KubernetesPortBuildItem> ports) {
Optional<Port> port) {
List<DecoratorBuildItem> result = new ArrayList<>();

annotations.forEach(a -> {
Expand Down Expand Up @@ -471,14 +489,14 @@ private static List<DecoratorBuildItem> createAnnotationDecorators(Optional<Proj
metricsConfiguration.ifPresent(m -> {
String path = m.metricsEndpoint();
String prefix = config.getPrometheusConfig().prefix;
if (!ports.isEmpty() && path != null) {
if (port.isPresent() && path != null) {
result.add(new DecoratorBuildItem(target, new AddAnnotationDecorator(name,
config.getPrometheusConfig().scrape.orElse(prefix + "/scrape"), "true",
PROMETHEUS_ANNOTATION_TARGETS)));
result.add(new DecoratorBuildItem(target, new AddAnnotationDecorator(name,
config.getPrometheusConfig().path.orElse(prefix + "/path"), path, PROMETHEUS_ANNOTATION_TARGETS)));
result.add(new DecoratorBuildItem(target, new AddAnnotationDecorator(name,
config.getPrometheusConfig().port.orElse(prefix + "/port"), "" + ports.get(0).getPort(),
config.getPrometheusConfig().port.orElse(prefix + "/port"), "" + port.get().getContainerPort(),
PROMETHEUS_ANNOTATION_TARGETS)));
result.add(new DecoratorBuildItem(target, new AddAnnotationDecorator(name,
config.getPrometheusConfig().scheme.orElse(prefix + "/scheme"), "http",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@

import static io.quarkus.kubernetes.deployment.Constants.DEFAULT_HTTP_PORT;
import static io.quarkus.kubernetes.deployment.Constants.DEFAULT_S2I_IMAGE_NAME;
import static io.quarkus.kubernetes.deployment.Constants.HTTP_PORT;
import static io.quarkus.kubernetes.deployment.Constants.OPENSHIFT;
import static io.quarkus.kubernetes.deployment.Constants.OPENSHIFT_APP_RUNTIME;
import static io.quarkus.kubernetes.deployment.Constants.QUARKUS;
Expand All @@ -21,6 +20,7 @@
import io.dekorate.kubernetes.config.EnvBuilder;
import io.dekorate.kubernetes.config.ImageConfiguration;
import io.dekorate.kubernetes.config.ImageConfigurationBuilder;
import io.dekorate.kubernetes.config.Port;
import io.dekorate.kubernetes.decorator.AddAnnotationDecorator;
import io.dekorate.kubernetes.decorator.AddEnvVarDecorator;
import io.dekorate.kubernetes.decorator.AddLabelDecorator;
Expand Down Expand Up @@ -191,10 +191,11 @@ public List<DecoratorBuildItem> createDecorators(ApplicationInfoBuildItem applic

Optional<Project> project = KubernetesCommonHelper.createProject(applicationInfo, customProjectRoot, outputTarget,
packageConfig);
Optional<Port> port = KubernetesCommonHelper.getPort(ports, config, config.route.targetPort);
result.addAll(KubernetesCommonHelper.createDecorators(project, OPENSHIFT, name, config,
metricsConfiguration,
annotations, labels, command,
ports, livenessPath, readinessPath, roles, roleBindings));
port, livenessPath, readinessPath, roles, roleBindings));

if (config.flavor == v3) {
//Openshift 3.x doesn't recognize 'app.kubernetes.io/name', it uses 'app' instead.
Expand Down Expand Up @@ -289,13 +290,13 @@ public List<DecoratorBuildItem> createDecorators(ApplicationInfoBuildItem applic
// Service handling
result.add(new DecoratorBuildItem(OPENSHIFT, new ApplyServiceTypeDecorator(name, config.getServiceType().name())));
if ((config.getServiceType() == ServiceType.NodePort) && config.nodePort.isPresent()) {
result.add(new DecoratorBuildItem(OPENSHIFT, new AddNodePortDecorator(name, config.nodePort.getAsInt())));
result.add(new DecoratorBuildItem(OPENSHIFT,
new AddNodePortDecorator(name, config.nodePort.getAsInt(), config.route.targetPort)));
}

// Probe port handling
Integer port = ports.stream().filter(p -> HTTP_PORT.equals(p.getName())).map(KubernetesPortBuildItem::getPort)
.findFirst().orElse(DEFAULT_HTTP_PORT);
result.add(new DecoratorBuildItem(OPENSHIFT, new ApplyHttpGetActionPortDecorator(name, name, port)));
Integer portNumber = port.map(Port::getContainerPort).orElse(DEFAULT_HTTP_PORT);
result.add(new DecoratorBuildItem(OPENSHIFT, new ApplyHttpGetActionPortDecorator(name, name, portNumber)));

// Handle non-openshift builds
if (deploymentKind == DeploymentResourceKind.DeploymentConfig
Expand Down
Loading

0 comments on commit 987572e

Please sign in to comment.