From 0f8175813183e01a7fa00da736d25c6804c25e91 Mon Sep 17 00:00:00 2001 From: Chris Laprun Date: Fri, 13 Sep 2024 19:26:51 +0200 Subject: [PATCH] Emit build item when the effective ServiceAccount name is computed Signed-off-by: Chris Laprun --- .../kind/deployment/KindProcessor.java | 21 +- .../deployment/MinikubeProcessor.java | 23 +- .../kubernetes/spi/BaseTargetable.java | 16 + .../spi/KubernetesAnnotationBuildItem.java | 2 +- .../spi/KubernetesClusterRoleBuildItem.java | 15 +- ...netesEffectiveServiceAccountBuildItem.java | 53 +++ .../spi/KubernetesEnvBuildItem.java | 75 ++-- .../spi/KubernetesInitContainerBuildItem.java | 8 +- .../spi/KubernetesJobBuildItem.java | 5 +- .../spi/KubernetesLabelBuildItem.java | 2 +- .../spi/KubernetesNamespaceBuildItem.java | 11 +- .../KubernetesResourceMetadataBuildItem.java | 11 +- .../spi/KubernetesRoleBindingBuildItem.java | 14 +- .../spi/KubernetesRoleBuildItem.java | 15 +- .../io/quarkus/kubernetes/spi/Targetable.java | 29 ++ .../deployment/DevClusterHelper.java | 57 ++- .../deployment/KnativeProcessor.java | 136 +++---- .../deployment/KubernetesCommonHelper.java | 359 ++++++++---------- .../deployment/OpenshiftProcessor.java | 87 ++--- .../kubernetes/deployment/RBACUtil.java | 39 ++ .../VanillaKubernetesProcessor.java | 63 +-- 21 files changed, 539 insertions(+), 502 deletions(-) create mode 100644 extensions/kubernetes/spi/src/main/java/io/quarkus/kubernetes/spi/BaseTargetable.java create mode 100644 extensions/kubernetes/spi/src/main/java/io/quarkus/kubernetes/spi/KubernetesEffectiveServiceAccountBuildItem.java create mode 100644 extensions/kubernetes/spi/src/main/java/io/quarkus/kubernetes/spi/Targetable.java create mode 100644 extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/RBACUtil.java diff --git a/extensions/kubernetes/kind/deployment/src/main/java/io/quarkus/kind/deployment/KindProcessor.java b/extensions/kubernetes/kind/deployment/src/main/java/io/quarkus/kind/deployment/KindProcessor.java index c9c8dc765aecc0..b3b2ce748ddf23 100644 --- a/extensions/kubernetes/kind/deployment/src/main/java/io/quarkus/kind/deployment/KindProcessor.java +++ b/extensions/kubernetes/kind/deployment/src/main/java/io/quarkus/kind/deployment/KindProcessor.java @@ -38,6 +38,7 @@ import io.quarkus.kubernetes.spi.KubernetesClusterRoleBuildItem; import io.quarkus.kubernetes.spi.KubernetesCommandBuildItem; import io.quarkus.kubernetes.spi.KubernetesDeploymentTargetBuildItem; +import io.quarkus.kubernetes.spi.KubernetesEffectiveServiceAccountBuildItem; import io.quarkus.kubernetes.spi.KubernetesEnvBuildItem; import io.quarkus.kubernetes.spi.KubernetesHealthLivenessPathBuildItem; import io.quarkus.kubernetes.spi.KubernetesHealthReadinessPathBuildItem; @@ -73,9 +74,7 @@ public void checkKind(ApplicationInfoBuildItem applicationInfo, KubernetesConfig @BuildStep public void createAnnotations(KubernetesConfig config, BuildProducer annotations) { - config.getAnnotations().forEach((k, v) -> { - annotations.produce(new KubernetesAnnotationBuildItem(k, v, KIND)); - }); + config.getAnnotations().forEach((k, v) -> annotations.produce(new KubernetesAnnotationBuildItem(k, v, KIND))); } @BuildStep @@ -97,6 +96,17 @@ public List createConfigurators(KubernetesConfig config, return result; } + @BuildStep + public KubernetesEffectiveServiceAccountBuildItem computeEffectiveServiceAccounts(ApplicationInfoBuildItem applicationInfo, + KubernetesConfig config, List serviceAccountsFromExtensions, + BuildProducer decorators) { + final String name = ResourceNameUtil.getResourceName(config, applicationInfo); + return KubernetesCommonHelper.computeEffectiveServiceAccount(name, KIND, + config, serviceAccountsFromExtensions, + decorators); + } + + @SuppressWarnings("OptionalUsedAsFieldOrParameterType") @BuildStep public List createDecorators(ApplicationInfoBuildItem applicationInfo, OutputTargetBuildItem outputTarget, @@ -120,7 +130,7 @@ public List createDecorators(ApplicationInfoBuildItem applic Optional startupPath, List roles, List clusterRoles, - List serviceAccounts, + List serviceAccounts, List roleBindings, Optional customProjectRoot) { @@ -133,7 +143,8 @@ public List createDecorators(ApplicationInfoBuildItem applic } @BuildStep - public void postBuild(ContainerImageInfoBuildItem image, List builders, + public void postBuild(ContainerImageInfoBuildItem image, + @SuppressWarnings("unused") List builders, @SuppressWarnings("unused") BuildProducer artifactResults) { //We used to only perform the action below when using known builders that play nicely with kind (e.g. docker) //However, this excluded users that are just using external tools for building including the cli (e.g. quarkus image build docker). diff --git a/extensions/kubernetes/minikube/deployment/src/main/java/io/quarkus/minikube/deployment/MinikubeProcessor.java b/extensions/kubernetes/minikube/deployment/src/main/java/io/quarkus/minikube/deployment/MinikubeProcessor.java index a48124a93a8a68..051514efc49366 100644 --- a/extensions/kubernetes/minikube/deployment/src/main/java/io/quarkus/minikube/deployment/MinikubeProcessor.java +++ b/extensions/kubernetes/minikube/deployment/src/main/java/io/quarkus/minikube/deployment/MinikubeProcessor.java @@ -35,6 +35,7 @@ import io.quarkus.kubernetes.spi.KubernetesClusterRoleBuildItem; import io.quarkus.kubernetes.spi.KubernetesCommandBuildItem; import io.quarkus.kubernetes.spi.KubernetesDeploymentTargetBuildItem; +import io.quarkus.kubernetes.spi.KubernetesEffectiveServiceAccountBuildItem; import io.quarkus.kubernetes.spi.KubernetesEnvBuildItem; import io.quarkus.kubernetes.spi.KubernetesHealthLivenessPathBuildItem; import io.quarkus.kubernetes.spi.KubernetesHealthReadinessPathBuildItem; @@ -69,9 +70,7 @@ public void checkMinikube(ApplicationInfoBuildItem applicationInfo, KubernetesCo @BuildStep public void createAnnotations(KubernetesConfig config, BuildProducer annotations) { - config.getAnnotations().forEach((k, v) -> { - annotations.produce(new KubernetesAnnotationBuildItem(k, v, MINIKUBE)); - }); + config.getAnnotations().forEach((k, v) -> annotations.produce(new KubernetesAnnotationBuildItem(k, v, MINIKUBE))); } @BuildStep @@ -87,12 +86,22 @@ public void createLabels(KubernetesConfig config, BuildProducer createConfigurators(KubernetesConfig config, List ports) { List result = new ArrayList<>(); - KubernetesCommonHelper.combinePorts(ports, config).values().forEach(value -> { - result.add(new ConfiguratorBuildItem(new AddPortToKubernetesConfig(value))); - }); + KubernetesCommonHelper.combinePorts(ports, config).values() + .forEach(value -> result.add(new ConfiguratorBuildItem(new AddPortToKubernetesConfig(value)))); return result; } + @BuildStep + public KubernetesEffectiveServiceAccountBuildItem computeEffectiveServiceAccounts(ApplicationInfoBuildItem applicationInfo, + KubernetesConfig config, List serviceAccountsFromExtensions, + BuildProducer decorators) { + final String name = ResourceNameUtil.getResourceName(config, applicationInfo); + return KubernetesCommonHelper.computeEffectiveServiceAccount(name, MINIKUBE, + config, serviceAccountsFromExtensions, + decorators); + } + + @SuppressWarnings("OptionalUsedAsFieldOrParameterType") @BuildStep public List createDecorators(ApplicationInfoBuildItem applicationInfo, OutputTargetBuildItem outputTarget, @@ -116,7 +125,7 @@ public List createDecorators(ApplicationInfoBuildItem applic Optional startupPath, List roles, List clusterRoles, - List serviceAccounts, + List serviceAccounts, List roleBindings, Optional customProjectRoot) { diff --git a/extensions/kubernetes/spi/src/main/java/io/quarkus/kubernetes/spi/BaseTargetable.java b/extensions/kubernetes/spi/src/main/java/io/quarkus/kubernetes/spi/BaseTargetable.java new file mode 100644 index 00000000000000..340f0862509fbc --- /dev/null +++ b/extensions/kubernetes/spi/src/main/java/io/quarkus/kubernetes/spi/BaseTargetable.java @@ -0,0 +1,16 @@ +package io.quarkus.kubernetes.spi; + +import io.quarkus.builder.item.MultiBuildItem; + +class BaseTargetable extends MultiBuildItem implements Targetable { + private final String target; + + protected BaseTargetable(String target) { + this.target = target; + } + + @Override + public String getTarget() { + return target; + } +} diff --git a/extensions/kubernetes/spi/src/main/java/io/quarkus/kubernetes/spi/KubernetesAnnotationBuildItem.java b/extensions/kubernetes/spi/src/main/java/io/quarkus/kubernetes/spi/KubernetesAnnotationBuildItem.java index 54cc75d0a59b6b..9b666d55adcf02 100644 --- a/extensions/kubernetes/spi/src/main/java/io/quarkus/kubernetes/spi/KubernetesAnnotationBuildItem.java +++ b/extensions/kubernetes/spi/src/main/java/io/quarkus/kubernetes/spi/KubernetesAnnotationBuildItem.java @@ -2,7 +2,7 @@ import io.quarkus.builder.item.MultiBuildItem; -public final class KubernetesAnnotationBuildItem extends MultiBuildItem { +public final class KubernetesAnnotationBuildItem extends MultiBuildItem implements Targetable { private final String key; private final String value; diff --git a/extensions/kubernetes/spi/src/main/java/io/quarkus/kubernetes/spi/KubernetesClusterRoleBuildItem.java b/extensions/kubernetes/spi/src/main/java/io/quarkus/kubernetes/spi/KubernetesClusterRoleBuildItem.java index a1bf4655f95258..18124031634130 100644 --- a/extensions/kubernetes/spi/src/main/java/io/quarkus/kubernetes/spi/KubernetesClusterRoleBuildItem.java +++ b/extensions/kubernetes/spi/src/main/java/io/quarkus/kubernetes/spi/KubernetesClusterRoleBuildItem.java @@ -2,13 +2,11 @@ import java.util.List; -import io.quarkus.builder.item.MultiBuildItem; - /** * Produce this build item to request the Kubernetes extension to generate * a Kubernetes {@code ClusterRole} resource. */ -public final class KubernetesClusterRoleBuildItem extends MultiBuildItem { +public final class KubernetesClusterRoleBuildItem extends BaseTargetable { /** * Name of the generated {@code ClusterRole} resource. */ @@ -18,15 +16,10 @@ public final class KubernetesClusterRoleBuildItem extends MultiBuildItem { */ private final List rules; - /** - * The target manifest that should include this role. - */ - private final String target; - public KubernetesClusterRoleBuildItem(String name, List rules, String target) { + super(target); this.name = name; this.rules = rules; - this.target = target; } public String getName() { @@ -36,8 +29,4 @@ public String getName() { public List getRules() { return rules; } - - public String getTarget() { - return target; - } } diff --git a/extensions/kubernetes/spi/src/main/java/io/quarkus/kubernetes/spi/KubernetesEffectiveServiceAccountBuildItem.java b/extensions/kubernetes/spi/src/main/java/io/quarkus/kubernetes/spi/KubernetesEffectiveServiceAccountBuildItem.java new file mode 100644 index 00000000000000..dd504007ed9e7a --- /dev/null +++ b/extensions/kubernetes/spi/src/main/java/io/quarkus/kubernetes/spi/KubernetesEffectiveServiceAccountBuildItem.java @@ -0,0 +1,53 @@ +package io.quarkus.kubernetes.spi; + +/** + * This build item is produced once the effective service account used for the generated resources is computed. Useful for + * downstream + * extensions that need to know this information to wait until it is made available. + */ +public final class KubernetesEffectiveServiceAccountBuildItem extends BaseTargetable { + private final String serviceAccountName; + private final String namespace; + private final boolean wasSet; + + public KubernetesEffectiveServiceAccountBuildItem(String serviceAccountName, String namespace, boolean wasSet) { + this(serviceAccountName, namespace, wasSet, null); + } + + public KubernetesEffectiveServiceAccountBuildItem(String serviceAccountName, String namespace, boolean wasSet, + String target) { + super(target); + this.serviceAccountName = serviceAccountName; + this.namespace = namespace; + this.wasSet = wasSet; + } + + /** + * The effective service account name after all the build time configuration has taken effect + * + * @return the effective service account name as used in the generated manifests for the main application deployment + */ + public String getServiceAccountName() { + return serviceAccountName; + } + + /** + * The effective service account namespace + * + * @return the effective service account name as used in the generated manifests for the main application deployment + */ + public String getNamespace() { + return namespace; + } + + /** + * Determines whether the service account name is automatically derived from the application name as opposed to explicitly + * set by the user + * + * @return {@code true} if the service account is automatically derived from the application name, {@code false} if + * explicitly set by the user + */ + public boolean wasSet() { + return wasSet; + } +} diff --git a/extensions/kubernetes/spi/src/main/java/io/quarkus/kubernetes/spi/KubernetesEnvBuildItem.java b/extensions/kubernetes/spi/src/main/java/io/quarkus/kubernetes/spi/KubernetesEnvBuildItem.java index 6a2bf43671004b..0159d30cf2a5dd 100644 --- a/extensions/kubernetes/spi/src/main/java/io/quarkus/kubernetes/spi/KubernetesEnvBuildItem.java +++ b/extensions/kubernetes/spi/src/main/java/io/quarkus/kubernetes/spi/KubernetesEnvBuildItem.java @@ -1,11 +1,11 @@ package io.quarkus.kubernetes.spi; -import org.jboss.logging.Logger; +import java.util.Objects; -import io.quarkus.builder.item.MultiBuildItem; +import org.jboss.logging.Logger; -public final class KubernetesEnvBuildItem extends MultiBuildItem { +public final class KubernetesEnvBuildItem extends BaseTargetable { private static final Logger log = Logger.getLogger(KubernetesEnvBuildItem.class); public enum EnvType { @@ -27,22 +27,14 @@ public boolean mightConflictWith(EnvType type) { return true; } - switch (this) { - case field: - return type == var || type == keyFromConfigmap || type == keyFromSecret; - case var: - return type == field || type == keyFromConfigmap || type == keyFromSecret; - case secret: - return type == configmap; - case configmap: - return type == secret; - case keyFromConfigmap: - return type == field || type == var || type == keyFromSecret; - case keyFromSecret: - return type == field || type == var || type == keyFromConfigmap; - default: - return false; - } + return switch (this) { + case field -> type == var || type == keyFromConfigmap || type == keyFromSecret; + case var -> type == field || type == keyFromConfigmap || type == keyFromSecret; + case secret -> type == configmap; + case configmap -> type == secret; + case keyFromConfigmap -> type == field || type == var || type == keyFromSecret; + case keyFromSecret -> type == field || type == var || type == keyFromConfigmap; + }; } } @@ -52,7 +44,6 @@ public boolean mightConflictWith(EnvType type) { private final String secret; private final String field; private final EnvType type; - private final String target; private final boolean oldStyle; private final String prefix; @@ -81,6 +72,7 @@ public static KubernetesEnvBuildItem createFromConfigMapKey(String varName, Stri return create(varName, key, null, configmap, null, target, prefix, isOldStyle(oldStyle)); } + @SuppressWarnings("unused") public static KubernetesEnvBuildItem createFromSecretKey(String varName, String key, String secret, String target, String prefix, boolean... oldStyle) { return create(varName, key, secret, null, null, target, prefix, isOldStyle(oldStyle)); @@ -146,13 +138,13 @@ private static boolean isOldStyle(boolean[] oldStyle) { KubernetesEnvBuildItem(String name, String value, String configmap, String secret, String field, EnvType type, String target, String prefix, boolean oldStyle) { + super(target); this.name = name; this.value = value; this.configmap = configmap; this.secret = secret; this.field = field; this.type = type; - this.target = target; this.prefix = prefix; this.oldStyle = oldStyle; } @@ -185,36 +177,27 @@ public EnvType getType() { return type; } - public String getTarget() { - return target; - } - public String getPrefix() { return prefix; } + @SuppressWarnings("unused") public KubernetesEnvBuildItem newWithTarget(String newTarget) { return new KubernetesEnvBuildItem(this.name, this.value, this.configmap, this.secret, this.field, this.type, newTarget, this.prefix, this.oldStyle); } public String toString() { - switch (type) { - case var: - return String.format("'%s' env var with value '%s'", name, value); - case field: - return String.format("'%s' env var with value from field '%s'", name, field); - case secret: - return "all values from '" + secret + "' secret"; - case configmap: - return "all values from '" + configmap + "' configmap"; - case keyFromConfigmap: - return String.format("'%s' env var with value from '%s' key of '%s' configmap", name, value, configmap); - case keyFromSecret: - return String.format("'%s' env var with value from '%s' key of '%s' secret", name, value, secret); - default: - return "unknown type '" + type + "'"; - } + return switch (type) { + case var -> String.format("'%s' env var with value '%s'", name, value); + case field -> String.format("'%s' env var with value from field '%s'", name, field); + case secret -> "all values from '" + secret + "' secret"; + case configmap -> "all values from '" + configmap + "' configmap"; + case keyFromConfigmap -> + String.format("'%s' env var with value from '%s' key of '%s' configmap", name, value, configmap); + case keyFromSecret -> + String.format("'%s' env var with value from '%s' key of '%s' secret", name, value, secret); + }; } @Override @@ -228,15 +211,15 @@ public boolean equals(Object o) { if (!name.equals(that.name)) return false; - if (value != null ? !value.equals(that.value) : that.value != null) + if (!Objects.equals(value, that.value)) return false; - if (configmap != null ? !configmap.equals(that.configmap) : that.configmap != null) + if (!Objects.equals(configmap, that.configmap)) return false; - if (secret != null ? !secret.equals(that.secret) : that.secret != null) + if (!Objects.equals(secret, that.secret)) return false; - if (field != null ? !field.equals(that.field) : that.field != null) + if (!Objects.equals(field, that.field)) return false; - if (prefix != null ? !prefix.equals(that.prefix) : that.prefix != null) + if (!Objects.equals(prefix, that.prefix)) return false; return type == that.type; } diff --git a/extensions/kubernetes/spi/src/main/java/io/quarkus/kubernetes/spi/KubernetesInitContainerBuildItem.java b/extensions/kubernetes/spi/src/main/java/io/quarkus/kubernetes/spi/KubernetesInitContainerBuildItem.java index 5b9b026f1fe9e5..469facd99e490f 100644 --- a/extensions/kubernetes/spi/src/main/java/io/quarkus/kubernetes/spi/KubernetesInitContainerBuildItem.java +++ b/extensions/kubernetes/spi/src/main/java/io/quarkus/kubernetes/spi/KubernetesInitContainerBuildItem.java @@ -10,10 +10,10 @@ * A Built item for generating init containers. * The generated container will have the specified fields * and may optionally inherit env vars and volumes from the app container. - * + *

* Env vars specified through this build item, will take precedence over inherited ones. */ -public final class KubernetesInitContainerBuildItem extends MultiBuildItem { +public final class KubernetesInitContainerBuildItem extends MultiBuildItem implements Targetable { private final String name; private final String target; @@ -64,6 +64,7 @@ public String getImage() { return image; } + @SuppressWarnings("unused") public KubernetesInitContainerBuildItem withImage(String image) { return new KubernetesInitContainerBuildItem(name, target, image, command, arguments, envVars, sharedEnvironment, sharedFilesystem); @@ -91,6 +92,7 @@ public Map getEnvVars() { return envVars; } + @SuppressWarnings("unused") public KubernetesInitContainerBuildItem withEnvVars(Map envVars) { return new KubernetesInitContainerBuildItem(name, target, image, command, arguments, envVars, sharedEnvironment, sharedFilesystem); @@ -107,6 +109,7 @@ public boolean isSharedEnvironment() { return sharedEnvironment; } + @SuppressWarnings("unused") public KubernetesInitContainerBuildItem withSharedEnvironment(boolean sharedEnvironment) { return new KubernetesInitContainerBuildItem(name, target, image, command, arguments, envVars, sharedEnvironment, sharedFilesystem); @@ -125,6 +128,7 @@ public boolean isSharedFilesystem() { return sharedFilesystem; } + @SuppressWarnings("unused") public KubernetesInitContainerBuildItem withSharedFilesystem(boolean sharedFilesystem) { return new KubernetesInitContainerBuildItem(name, target, image, command, arguments, envVars, sharedEnvironment, sharedFilesystem); diff --git a/extensions/kubernetes/spi/src/main/java/io/quarkus/kubernetes/spi/KubernetesJobBuildItem.java b/extensions/kubernetes/spi/src/main/java/io/quarkus/kubernetes/spi/KubernetesJobBuildItem.java index 7b40a5e39ca139..c2b25b6e2506b9 100644 --- a/extensions/kubernetes/spi/src/main/java/io/quarkus/kubernetes/spi/KubernetesJobBuildItem.java +++ b/extensions/kubernetes/spi/src/main/java/io/quarkus/kubernetes/spi/KubernetesJobBuildItem.java @@ -10,10 +10,10 @@ * A Built item for generating init containers. * The generated container will have the specified fields * and may optionally inherit env vars and volumes from the app container. - * + *

* Env vars specified through this build item, will take precedence over inherited ones. */ -public final class KubernetesJobBuildItem extends MultiBuildItem { +public final class KubernetesJobBuildItem extends MultiBuildItem implements Targetable { private final String name; private final String target; @@ -63,6 +63,7 @@ public String getImage() { return image; } + @SuppressWarnings("unused") public KubernetesJobBuildItem withImage(String image) { return new KubernetesJobBuildItem(name, target, image, command, arguments, envVars, sharedEnvironment, sharedFilesystem); diff --git a/extensions/kubernetes/spi/src/main/java/io/quarkus/kubernetes/spi/KubernetesLabelBuildItem.java b/extensions/kubernetes/spi/src/main/java/io/quarkus/kubernetes/spi/KubernetesLabelBuildItem.java index d8d04fe583e8ed..ea4be01a6a9564 100644 --- a/extensions/kubernetes/spi/src/main/java/io/quarkus/kubernetes/spi/KubernetesLabelBuildItem.java +++ b/extensions/kubernetes/spi/src/main/java/io/quarkus/kubernetes/spi/KubernetesLabelBuildItem.java @@ -2,7 +2,7 @@ import io.quarkus.builder.item.MultiBuildItem; -public final class KubernetesLabelBuildItem extends MultiBuildItem { +public final class KubernetesLabelBuildItem extends MultiBuildItem implements Targetable { private final String key; private final String value; diff --git a/extensions/kubernetes/spi/src/main/java/io/quarkus/kubernetes/spi/KubernetesNamespaceBuildItem.java b/extensions/kubernetes/spi/src/main/java/io/quarkus/kubernetes/spi/KubernetesNamespaceBuildItem.java index c1b56ced080ced..742dda7b1956d5 100644 --- a/extensions/kubernetes/spi/src/main/java/io/quarkus/kubernetes/spi/KubernetesNamespaceBuildItem.java +++ b/extensions/kubernetes/spi/src/main/java/io/quarkus/kubernetes/spi/KubernetesNamespaceBuildItem.java @@ -1,21 +1,14 @@ package io.quarkus.kubernetes.spi; -import io.quarkus.builder.item.MultiBuildItem; +public final class KubernetesNamespaceBuildItem extends BaseTargetable { -public final class KubernetesNamespaceBuildItem extends MultiBuildItem { - - private final String target; // Kubernetes, Openshift, Knative private final String namespace; public KubernetesNamespaceBuildItem(String target, String namespace) { - this.target = target; + super(target); this.namespace = namespace; } - public String getTarget() { - return target; - } - public String getNamespace() { return namespace; } diff --git a/extensions/kubernetes/spi/src/main/java/io/quarkus/kubernetes/spi/KubernetesResourceMetadataBuildItem.java b/extensions/kubernetes/spi/src/main/java/io/quarkus/kubernetes/spi/KubernetesResourceMetadataBuildItem.java index d0eb2cd69a50e6..ba74afdd0bebce 100644 --- a/extensions/kubernetes/spi/src/main/java/io/quarkus/kubernetes/spi/KubernetesResourceMetadataBuildItem.java +++ b/extensions/kubernetes/spi/src/main/java/io/quarkus/kubernetes/spi/KubernetesResourceMetadataBuildItem.java @@ -1,28 +1,21 @@ package io.quarkus.kubernetes.spi; -import io.quarkus.builder.item.MultiBuildItem; +public final class KubernetesResourceMetadataBuildItem extends BaseTargetable { -public final class KubernetesResourceMetadataBuildItem extends MultiBuildItem { - - private final String target; private final String group; private final String version; private final String kind; private final String name; public KubernetesResourceMetadataBuildItem(String target, String group, String version, String kind, String name) { - this.target = target; + super(target); this.group = group; this.version = version; this.kind = kind; this.name = name; } - public String getTarget() { - return target; - } - public String getGroup() { return group; } diff --git a/extensions/kubernetes/spi/src/main/java/io/quarkus/kubernetes/spi/KubernetesRoleBindingBuildItem.java b/extensions/kubernetes/spi/src/main/java/io/quarkus/kubernetes/spi/KubernetesRoleBindingBuildItem.java index 0242d90192277b..7187c92d1da7a2 100644 --- a/extensions/kubernetes/spi/src/main/java/io/quarkus/kubernetes/spi/KubernetesRoleBindingBuildItem.java +++ b/extensions/kubernetes/spi/src/main/java/io/quarkus/kubernetes/spi/KubernetesRoleBindingBuildItem.java @@ -3,8 +3,6 @@ import java.util.Collections; import java.util.Map; -import io.quarkus.builder.item.MultiBuildItem; - /** * Produce this build item to request the Kubernetes extension to generate * a Kubernetes {@code RoleBinding} resource. The configuration here is limited; @@ -13,7 +11,7 @@ *

* Note that this can't be used to generate a {@code ClusterRoleBinding}. */ -public final class KubernetesRoleBindingBuildItem extends MultiBuildItem { +public final class KubernetesRoleBindingBuildItem extends BaseTargetable { /** * Name of the generated {@code RoleBinding} resource. * Can be {@code null}, in which case the resource name is autogenerated. @@ -23,10 +21,6 @@ public final class KubernetesRoleBindingBuildItem extends MultiBuildItem { * RoleRef configuration. */ private final RoleRef roleRef; - /** - * The target manifest that should include this role. - */ - private final String target; /** * The target subjects. */ @@ -53,8 +47,8 @@ public KubernetesRoleBindingBuildItem(String name, String role, boolean clusterW public KubernetesRoleBindingBuildItem(String name, String target, Map labels, RoleRef roleRef, Subject... subjects) { + super(target); this.name = name; - this.target = target; this.labels = labels; this.roleRef = roleRef; this.subjects = subjects; @@ -64,10 +58,6 @@ public String getName() { return this.name; } - public String getTarget() { - return target; - } - public Map getLabels() { return labels; } diff --git a/extensions/kubernetes/spi/src/main/java/io/quarkus/kubernetes/spi/KubernetesRoleBuildItem.java b/extensions/kubernetes/spi/src/main/java/io/quarkus/kubernetes/spi/KubernetesRoleBuildItem.java index c22c82410cc78c..2eb244f9ddf96a 100644 --- a/extensions/kubernetes/spi/src/main/java/io/quarkus/kubernetes/spi/KubernetesRoleBuildItem.java +++ b/extensions/kubernetes/spi/src/main/java/io/quarkus/kubernetes/spi/KubernetesRoleBuildItem.java @@ -2,15 +2,13 @@ import java.util.List; -import io.quarkus.builder.item.MultiBuildItem; - /** * Produce this build item to request the Kubernetes extension to generate * a Kubernetes {@code Role} resource. *

* Note that this can't be used to generate a {@code ClusterRole}. */ -public final class KubernetesRoleBuildItem extends MultiBuildItem { +public final class KubernetesRoleBuildItem extends BaseTargetable { /** * Name of the generated {@code Role} resource. */ @@ -24,11 +22,6 @@ public final class KubernetesRoleBuildItem extends MultiBuildItem { */ private final List rules; - /** - * The target manifest that should include this role. - */ - private final String target; - public KubernetesRoleBuildItem(String name, List rules) { this(name, rules, null); } @@ -38,10 +31,10 @@ public KubernetesRoleBuildItem(String name, List rules, String targe } public KubernetesRoleBuildItem(String name, String namespace, List rules, String target) { + super(target); this.name = name; this.namespace = namespace; this.rules = rules; - this.target = target; } public String getName() { @@ -55,8 +48,4 @@ public String getNamespace() { public List getRules() { return rules; } - - public String getTarget() { - return target; - } } diff --git a/extensions/kubernetes/spi/src/main/java/io/quarkus/kubernetes/spi/Targetable.java b/extensions/kubernetes/spi/src/main/java/io/quarkus/kubernetes/spi/Targetable.java new file mode 100644 index 00000000000000..30110eda67d77d --- /dev/null +++ b/extensions/kubernetes/spi/src/main/java/io/quarkus/kubernetes/spi/Targetable.java @@ -0,0 +1,29 @@ +package io.quarkus.kubernetes.spi; + +import java.util.Collection; +import java.util.stream.Stream; + +public interface Targetable { + String getTarget(); + + default boolean isActiveFor(String target, boolean strictTargetMatching) { + if (target == null) { + return true; + } + final var localTarget = getTarget(); + if (strictTargetMatching) { + return target.equals(localTarget); + } else { + return localTarget == null || localTarget.equals(target); + } + } + + static Stream filteredByTarget(Collection targetables, String target) { + return filteredByTarget(targetables, target, false); + } + + static Stream filteredByTarget(Collection targetables, String target, + boolean strictTargetMatching) { + return targetables.stream().filter(targetable -> targetable.isActiveFor(target, strictTargetMatching)); + } +} diff --git a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/DevClusterHelper.java b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/DevClusterHelper.java index 2895aa1642e3b2..7addbaa71373a9 100644 --- a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/DevClusterHelper.java +++ b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/DevClusterHelper.java @@ -19,7 +19,6 @@ import java.util.List; import java.util.Map; import java.util.Optional; -import java.util.stream.Collectors; import java.util.stream.Stream; import io.dekorate.kubernetes.annotation.ServiceType; @@ -41,6 +40,7 @@ import io.quarkus.kubernetes.spi.KubernetesAnnotationBuildItem; import io.quarkus.kubernetes.spi.KubernetesClusterRoleBuildItem; import io.quarkus.kubernetes.spi.KubernetesCommandBuildItem; +import io.quarkus.kubernetes.spi.KubernetesEffectiveServiceAccountBuildItem; import io.quarkus.kubernetes.spi.KubernetesEnvBuildItem; import io.quarkus.kubernetes.spi.KubernetesHealthLivenessPathBuildItem; import io.quarkus.kubernetes.spi.KubernetesHealthReadinessPathBuildItem; @@ -53,12 +53,13 @@ import io.quarkus.kubernetes.spi.KubernetesProbePortNameBuildItem; import io.quarkus.kubernetes.spi.KubernetesRoleBindingBuildItem; import io.quarkus.kubernetes.spi.KubernetesRoleBuildItem; -import io.quarkus.kubernetes.spi.KubernetesServiceAccountBuildItem; +import io.quarkus.kubernetes.spi.Targetable; public class DevClusterHelper { public static final String DEFAULT_HASH_ALGORITHM = "SHA-256"; + @SuppressWarnings("OptionalUsedAsFieldOrParameterType") public static List createDecorators(String clusterKind, String deploymentTarget, ApplicationInfoBuildItem applicationInfo, @@ -73,7 +74,7 @@ public static List createDecorators(String clusterKind, List annotations, List labels, List envs, - Optional baseImage, + @Deprecated Optional baseImage, Optional image, Optional command, List ports, @@ -83,41 +84,37 @@ public static List createDecorators(String clusterKind, Optional startupPath, List roles, List clusterRoles, - List serviceAccounts, + List serviceAccounts, List roleBindings, Optional customProjectRoot) { - List result = new ArrayList<>(); String name = ResourceNameUtil.getResourceName(config, applicationInfo); - Optional namespace = namespaces.stream() - .filter(n -> deploymentTarget.equals(n.getTarget())) + final var namespace = Targetable.filteredByTarget(namespaces, deploymentTarget, true) .findFirst(); Optional project = KubernetesCommonHelper.createProject(applicationInfo, customProjectRoot, outputTarget, packageConfig); Optional port = KubernetesCommonHelper.getPort(ports, config); - result.addAll(KubernetesCommonHelper.createDecorators(project, clusterKind, name, namespace, config, - metricsConfiguration, kubernetesClientConfiguration, - annotations, labels, image, command, - port, livenessPath, readinessPath, startupPath, roles, clusterRoles, serviceAccounts, roleBindings)); - - image.ifPresent(i -> { - result.add(new DecoratorBuildItem(clusterKind, new ApplyContainerImageDecorator(name, i.getImage()))); - }); - - Stream.concat(config.convertToBuildItems().stream(), - envs.stream().filter(e -> e.getTarget() == null || KUBERNETES.equals(e.getTarget()))).forEach(e -> { - result.add(new DecoratorBuildItem(clusterKind, - new AddEnvVarDecorator(ApplicationContainerDecorator.ANY, name, new EnvBuilder() - .withName(EnvConverter.convertName(e.getName())) - .withValue(e.getValue()) - .withSecret(e.getSecret()) - .withConfigmap(e.getConfigMap()) - .withField(e.getField()) - .withPrefix(e.getPrefix()) - .build()))); - }); + List result = new ArrayList<>( + KubernetesCommonHelper.createDecorators(project, clusterKind, name, namespace, config, + metricsConfiguration, kubernetesClientConfiguration, + annotations, labels, image, command, + port, livenessPath, readinessPath, startupPath, roles, clusterRoles, serviceAccounts, roleBindings)); + + image.ifPresent( + i -> result.add(new DecoratorBuildItem(clusterKind, new ApplyContainerImageDecorator(name, i.getImage())))); + + Stream.concat(config.convertToBuildItems().stream(), Targetable.filteredByTarget(envs, KUBERNETES)) + .forEach(e -> result.add(new DecoratorBuildItem(clusterKind, + new AddEnvVarDecorator(ApplicationContainerDecorator.ANY, name, new EnvBuilder() + .withName(EnvConverter.convertName(e.getName())) + .withValue(e.getValue()) + .withSecret(e.getSecret()) + .withConfigmap(e.getConfigMap()) + .withField(e.getField()) + .withPrefix(e.getPrefix()) + .build())))); result.add(new DecoratorBuildItem(clusterKind, new ApplyImagePullPolicyDecorator(name, "IfNotPresent"))); @@ -125,7 +122,7 @@ public static List createDecorators(String clusterKind, result.add(new DecoratorBuildItem(clusterKind, new ApplyServiceTypeDecorator(name, ServiceType.NodePort.name()))); List> nodeConfigPorts = config.getPorts().entrySet().stream() .filter(e -> e.getValue().nodePort.isPresent()) - .collect(Collectors.toList()); + .toList(); if (!nodeConfigPorts.isEmpty()) { for (Map.Entry entry : nodeConfigPorts) { result.add(new DecoratorBuildItem(KUBERNETES, @@ -158,7 +155,7 @@ public static List createDecorators(String clusterKind, // Handle init Containers result.addAll(KubernetesCommonHelper.createInitContainerDecorators(clusterKind, name, initContainers, result)); - result.addAll(KubernetesCommonHelper.createInitJobDecorators(clusterKind, name, jobs, result)); + result.addAll(KubernetesCommonHelper.createInitJobDecorators(clusterKind, jobs, result)); // Do not bind the Management port to the Service resource unless it's explicitly used by the user. if (managementPortIsEnabled() 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 80c8d1a99be4e1..24962f3c60a66e 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 @@ -1,9 +1,6 @@ package io.quarkus.kubernetes.deployment; -import static io.quarkus.kubernetes.deployment.Constants.KNATIVE; -import static io.quarkus.kubernetes.deployment.Constants.KNATIVE_SERVICE; -import static io.quarkus.kubernetes.deployment.Constants.KNATIVE_SERVICE_GROUP; -import static io.quarkus.kubernetes.deployment.Constants.KNATIVE_SERVICE_VERSION; +import static io.quarkus.kubernetes.deployment.Constants.*; import static io.quarkus.kubernetes.spi.KubernetesDeploymentTargetBuildItem.DEFAULT_PRIORITY; import java.util.ArrayList; @@ -11,7 +8,6 @@ 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; @@ -52,7 +48,6 @@ import io.dekorate.kubernetes.decorator.ApplicationContainerDecorator; import io.dekorate.kubernetes.decorator.ApplyImagePullPolicyDecorator; import io.dekorate.project.Project; -import io.quarkus.container.spi.BaseImageInfoBuildItem; import io.quarkus.container.spi.ContainerImageInfoBuildItem; import io.quarkus.container.spi.ContainerImageLabelBuildItem; import io.quarkus.deployment.annotations.BuildProducer; @@ -69,6 +64,7 @@ import io.quarkus.kubernetes.spi.KubernetesClusterRoleBuildItem; import io.quarkus.kubernetes.spi.KubernetesCommandBuildItem; import io.quarkus.kubernetes.spi.KubernetesDeploymentTargetBuildItem; +import io.quarkus.kubernetes.spi.KubernetesEffectiveServiceAccountBuildItem; import io.quarkus.kubernetes.spi.KubernetesEnvBuildItem; import io.quarkus.kubernetes.spi.KubernetesHealthLivenessPathBuildItem; import io.quarkus.kubernetes.spi.KubernetesHealthReadinessPathBuildItem; @@ -80,6 +76,7 @@ import io.quarkus.kubernetes.spi.KubernetesRoleBindingBuildItem; import io.quarkus.kubernetes.spi.KubernetesRoleBuildItem; import io.quarkus.kubernetes.spi.KubernetesServiceAccountBuildItem; +import io.quarkus.kubernetes.spi.Targetable; public class KnativeProcessor { @@ -109,9 +106,7 @@ public void checkKnative(ApplicationInfoBuildItem applicationInfo, KnativeConfig @BuildStep public void createAnnotations(KnativeConfig config, BuildProducer annotations) { - config.getAnnotations().forEach((k, v) -> { - annotations.produce(new KubernetesAnnotationBuildItem(k, v, KNATIVE)); - }); + config.getAnnotations().forEach((k, v) -> annotations.produce(new KubernetesAnnotationBuildItem(k, v, KNATIVE))); } @BuildStep @@ -125,9 +120,7 @@ public void createLabels(KnativeConfig config, BuildProducer namespace) { - config.getNamespace().ifPresent(n -> { - namespace.produce(new KubernetesNamespaceBuildItem(KNATIVE, n)); - }); + config.getNamespace().ifPresent(n -> namespace.produce(new KubernetesNamespaceBuildItem(KNATIVE, n))); } @BuildStep @@ -142,6 +135,17 @@ public List createConfigurators(KnativeConfig config, Lis return result; } + @BuildStep + public KubernetesEffectiveServiceAccountBuildItem computeEffectiveServiceAccounts(ApplicationInfoBuildItem applicationInfo, + KubernetesConfig config, List serviceAccountsFromExtensions, + BuildProducer decorators) { + final String name = ResourceNameUtil.getResourceName(config, applicationInfo); + return KubernetesCommonHelper.computeEffectiveServiceAccount(name, KNATIVE, + config, serviceAccountsFromExtensions, + decorators); + } + + @SuppressWarnings("OptionalUsedAsFieldOrParameterType") @BuildStep public List createDecorators(ApplicationInfoBuildItem applicationInfo, OutputTargetBuildItem outputTarget, @@ -153,7 +157,6 @@ public List createDecorators(ApplicationInfoBuildItem applic List annotations, List labels, List envs, - Optional baseImage, Optional image, Optional command, List ports, @@ -162,20 +165,19 @@ public List createDecorators(ApplicationInfoBuildItem applic Optional startupProbePath, List roles, List clusterRoles, - List serviceAccounts, + List serviceAccounts, List roleBindings, Optional customProjectRoot, List targets) { List result = new ArrayList<>(); - if (!targets.stream().filter(KubernetesDeploymentTargetBuildItem::isEnabled) - .anyMatch(t -> KNATIVE.equals(t.getName()))) { + if (targets.stream().filter(KubernetesDeploymentTargetBuildItem::isEnabled) + .noneMatch(t -> KNATIVE.equals(t.getName()))) { return result; } String name = ResourceNameUtil.getResourceName(config, applicationInfo); - Optional namespace = namespaces.stream() - .filter(n -> KNATIVE.equals(n.getTarget())) + final var namespace = Targetable.filteredByTarget(namespaces, KNATIVE, true) .findFirst(); Optional project = KubernetesCommonHelper.createProject(applicationInfo, customProjectRoot, outputTarget, @@ -187,25 +189,21 @@ public List createDecorators(ApplicationInfoBuildItem applic labels, image, command, port, livenessPath, readinessPath, startupProbePath, roles, clusterRoles, serviceAccounts, roleBindings)); - image.ifPresent(i -> { - result.add(new DecoratorBuildItem(KNATIVE, new ApplyContainerImageDecorator(name, i.getImage()))); - }); + image.ifPresent(i -> result.add(new DecoratorBuildItem(KNATIVE, new ApplyContainerImageDecorator(name, i.getImage())))); result.add(new DecoratorBuildItem(KNATIVE, new ApplyImagePullPolicyDecorator(name, config.getImagePullPolicy()))); config.getContainerName().ifPresent(containerName -> result .add(new DecoratorBuildItem(KNATIVE, new ChangeContainerNameDecorator(containerName)))); - Stream.concat(config.convertToBuildItems().stream(), - envs.stream().filter(e -> e.getTarget() == null || KNATIVE.equals(e.getTarget()))).forEach(e -> { - result.add(new DecoratorBuildItem(KNATIVE, - new AddEnvVarDecorator(ApplicationContainerDecorator.ANY, name, new EnvBuilder() - .withName(EnvConverter.convertName(e.getName())) - .withValue(e.getValue()) - .withSecret(e.getSecret()) - .withConfigmap(e.getConfigMap()) - .withField(e.getField()) - .build()))); - }); + Stream.concat(config.convertToBuildItems().stream(), Targetable.filteredByTarget(envs, KNATIVE)) + .forEach(e -> result.add(new DecoratorBuildItem(KNATIVE, + new AddEnvVarDecorator(ApplicationContainerDecorator.ANY, name, new EnvBuilder() + .withName(EnvConverter.convertName(e.getName())) + .withValue(e.getValue()) + .withSecret(e.getSecret()) + .withConfigmap(e.getConfigMap()) + .withField(e.getField()) + .build())))); if (config.clusterLocal) { if (labels.stream().filter(l -> KNATIVE.equals(l.getTarget())) @@ -269,15 +267,13 @@ public List createDecorators(ApplicationInfoBuildItem applic result.add(new DecoratorBuildItem(KNATIVE, new ApplyServiceTypeDecorator(name, config.getServiceType().name()))); //In Knative its expected that all http ports in probe are omitted (so we set them to null). - result.add(new DecoratorBuildItem(KNATIVE, new ApplyHttpGetActionPortDecorator(name, (Integer) null))); + result.add(new DecoratorBuildItem(KNATIVE, new ApplyHttpGetActionPortDecorator(name, null))); //Traffic Splitting - config.revisionName.ifPresent(r -> { - result.add(new DecoratorBuildItem(KNATIVE, new ApplyRevisionNameDecorator(name, r))); - }); + config.revisionName + .ifPresent(r -> result.add(new DecoratorBuildItem(KNATIVE, new ApplyRevisionNameDecorator(name, r)))); - config.traffic.forEach((k, v) -> { - TrafficConfig traffic = v; + config.traffic.forEach((k, traffic) -> { //Revision name is K unless we have the edge name of a revision named 'latest' which is not really the latest (in which case use null). boolean latestRevision = traffic.latestRevision.get(); String revisionName = !latestRevision && LATEST_REVISION.equals(k) ? null : k; @@ -288,15 +284,12 @@ 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)))); - }); - config.getSidecars().entrySet().forEach(e -> { - result.add(new DecoratorBuildItem(KNATIVE, new AddSidecarToRevisionDecorator(name, ContainerConverter.convert(e)))); - }); + result.addAll(createVolumeDecorators(config)); + result.addAll(createAppConfigVolumeAndEnvDecorators(name, config)); + config.getHostAliases().entrySet().forEach(e -> result.add(new DecoratorBuildItem(KNATIVE, + new AddHostAliasesToRevisionDecorator(name, HostAliasConverter.convert(e))))); + config.getSidecars().entrySet().forEach(e -> result + .add(new DecoratorBuildItem(KNATIVE, new AddSidecarToRevisionDecorator(name, ContainerConverter.convert(e))))); if (!roleBindings.isEmpty()) { result.add(new DecoratorBuildItem(new ApplyServiceAccountNameToRevisionSpecDecorator())); @@ -315,47 +308,34 @@ public List createDecorators(ApplicationInfoBuildItem applic return result; } - private static List createVolumeDecorators(Optional project, String name, - PlatformConfiguration config) { + private static List createVolumeDecorators(PlatformConfiguration config) { List result = new ArrayList<>(); - config.getSecretVolumes().entrySet().forEach(e -> { - result.add( - new DecoratorBuildItem(KNATIVE, new AddSecretVolumeToRevisionDecorator(SecretVolumeConverter.convert(e)))); - }); + config.getSecretVolumes().entrySet().forEach(e -> result.add( + new DecoratorBuildItem(KNATIVE, new AddSecretVolumeToRevisionDecorator(SecretVolumeConverter.convert(e))))); - config.getConfigMapVolumes().entrySet().forEach(e -> { - result.add(new DecoratorBuildItem(KNATIVE, - new AddConfigMapVolumeToRevisionDecorator(ConfigMapVolumeConverter.convert(e)))); - }); + config.getConfigMapVolumes().entrySet().forEach(e -> result.add(new DecoratorBuildItem(KNATIVE, + new AddConfigMapVolumeToRevisionDecorator(ConfigMapVolumeConverter.convert(e))))); - config.getEmptyDirVolumes().forEach(e -> { - result.add(new DecoratorBuildItem(KNATIVE, - new AddEmptyDirVolumeToRevisionDecorator(EmptyDirVolumeConverter.convert(e)))); - }); + config.getEmptyDirVolumes().forEach(e -> result.add(new DecoratorBuildItem(KNATIVE, + new AddEmptyDirVolumeToRevisionDecorator(EmptyDirVolumeConverter.convert(e))))); - config.getPvcVolumes().entrySet().forEach(e -> { - result.add(new DecoratorBuildItem(KNATIVE, new AddPvcVolumeToRevisionDecorator(PvcVolumeConverter.convert(e)))); - }); + config.getPvcVolumes().entrySet().forEach(e -> result + .add(new DecoratorBuildItem(KNATIVE, new AddPvcVolumeToRevisionDecorator(PvcVolumeConverter.convert(e))))); - config.getAwsElasticBlockStoreVolumes().entrySet().forEach(e -> { - result.add(new DecoratorBuildItem(KNATIVE, - new AddAwsElasticBlockStoreVolumeToRevisionDecorator(AwsElasticBlockStoreVolumeConverter.convert(e)))); - }); + config.getAwsElasticBlockStoreVolumes().entrySet().forEach(e -> result.add(new DecoratorBuildItem(KNATIVE, + new AddAwsElasticBlockStoreVolumeToRevisionDecorator(AwsElasticBlockStoreVolumeConverter.convert(e))))); - config.getAzureFileVolumes().entrySet().forEach(e -> { - result.add(new DecoratorBuildItem(KNATIVE, - new AddAzureFileVolumeToRevisionDecorator(AzureFileVolumeConverter.convert(e)))); - }); + config.getAzureFileVolumes().entrySet().forEach(e -> result.add(new DecoratorBuildItem(KNATIVE, + new AddAzureFileVolumeToRevisionDecorator(AzureFileVolumeConverter.convert(e))))); - config.getAzureDiskVolumes().entrySet().forEach(e -> { - result.add(new DecoratorBuildItem(KNATIVE, - new AddAzureDiskVolumeToRevisionDecorator(AzureDiskVolumeConverter.convert(e)))); - }); + config.getAzureDiskVolumes().entrySet().forEach(e -> result.add(new DecoratorBuildItem(KNATIVE, + new AddAzureDiskVolumeToRevisionDecorator(AzureDiskVolumeConverter.convert(e))))); return result; } - private static List createAppConfigVolumeAndEnvDecorators(Optional project, String name, + private static List createAppConfigVolumeAndEnvDecorators( + String name, PlatformConfiguration config) { List result = new ArrayList<>(); @@ -389,7 +369,7 @@ private static List createAppConfigVolumeAndEnvDecorators(Op result.add(new DecoratorBuildItem(KNATIVE, new AddEnvVarDecorator(ApplicationContainerDecorator.ANY, name, new EnvBuilder() .withName("SMALLRYE_CONFIG_LOCATIONS") - .withValue(paths.stream().collect(Collectors.joining(","))) + .withValue(String.join(",", paths)) .build()))); } return result; 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 97dc22ba2b3f52..2f9e23d683c10a 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 @@ -2,14 +2,7 @@ package io.quarkus.kubernetes.deployment; import static io.dekorate.kubernetes.decorator.AddServiceResourceDecorator.distinct; -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.KNATIVE; -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_QUARKUS_VERSION; -import static io.quarkus.kubernetes.deployment.Constants.QUARKUS_ANNOTATIONS_VCS_URL; -import static io.quarkus.kubernetes.deployment.Constants.SERVICE_ACCOUNT; +import static io.quarkus.kubernetes.deployment.Constants.*; import java.nio.file.Path; import java.time.ZoneOffset; @@ -82,9 +75,9 @@ import io.fabric8.kubernetes.api.model.ObjectMeta; import io.fabric8.kubernetes.api.model.PodSpecBuilder; import io.fabric8.kubernetes.api.model.rbac.PolicyRule; -import io.fabric8.kubernetes.api.model.rbac.PolicyRuleBuilder; import io.quarkus.builder.Version; import io.quarkus.container.spi.ContainerImageInfoBuildItem; +import io.quarkus.deployment.annotations.BuildProducer; import io.quarkus.deployment.builditem.ApplicationInfoBuildItem; import io.quarkus.deployment.metrics.MetricsCapabilityBuildItem; import io.quarkus.deployment.pkg.PackageConfig; @@ -95,6 +88,7 @@ import io.quarkus.kubernetes.spi.KubernetesAnnotationBuildItem; import io.quarkus.kubernetes.spi.KubernetesClusterRoleBuildItem; import io.quarkus.kubernetes.spi.KubernetesCommandBuildItem; +import io.quarkus.kubernetes.spi.KubernetesEffectiveServiceAccountBuildItem; import io.quarkus.kubernetes.spi.KubernetesHealthLivenessPathBuildItem; import io.quarkus.kubernetes.spi.KubernetesHealthReadinessPathBuildItem; import io.quarkus.kubernetes.spi.KubernetesHealthStartupPathBuildItem; @@ -110,7 +104,9 @@ import io.quarkus.kubernetes.spi.Property; import io.quarkus.kubernetes.spi.RoleRef; import io.quarkus.kubernetes.spi.Subject; +import io.quarkus.kubernetes.spi.Targetable; +@SuppressWarnings("OptionalUsedAsFieldOrParameterType") public class KubernetesCommonHelper { private static final Logger LOG = Logger.getLogger(KubernetesCommonHelper.class); private static final String ANY = null; @@ -118,7 +114,6 @@ public class KubernetesCommonHelper { private static final String[] PROMETHEUS_ANNOTATION_TARGETS = { "Service", "Deployment", "DeploymentConfig" }; private static final String DEFAULT_ROLE_NAME_VIEW = "view"; - private static final List LIST_WITH_EMPTY = List.of(""); private static final String SCHEME_HTTP = "HTTP"; private static final String SCHEME_HTTPS = "HTTPS"; @@ -165,7 +160,7 @@ public static Optional getPort(List ports, Kubern */ public static Optional getPort(List ports, PlatformConfiguration config, String targetPort) { return combinePorts(ports, config).values().stream() - .filter(distinct(p -> p.getName())) + .filter(distinct(Port::getName)) .filter(p -> p.getName().equals(targetPort)) .findFirst(); } @@ -215,7 +210,7 @@ public static Map combinePorts(List ports */ public static void printMessageAboutPortsThatCantChange(String target, List ports, PlatformConfiguration configuration) { - ports.stream().forEach(port -> { + ports.forEach(port -> { boolean enabled = port.isEnabled() || configuration.getPorts().containsKey(port.getName()); if (enabled) { String name = "quarkus." + target + ".ports." + port.getName() + ".container-port"; @@ -223,6 +218,7 @@ public static void printMessageAboutPortsThatCantChange(String target, List p.containerPort) .filter(OptionalInt::isPresent) .map(OptionalInt::getAsInt); + @SuppressWarnings({ "rawtypes", "unchecked" }) Property kubernetesPortProperty = new Property(name, Integer.class, value, null, false); PropertyUtil.printMessages(String.format("The container port %s", port.getName()), target, kubernetesPortProperty, @@ -249,36 +245,34 @@ public static List createDecorators(Optional projec Optional startupPath, List roles, List clusterRoles, - List serviceAccounts, + List serviceAccounts, List roleBindings) { List result = new ArrayList<>(); - result.addAll(createLabelDecorators(project, target, name, config, labels)); + result.addAll(createLabelDecorators(target, name, config, labels)); result.addAll(createAnnotationDecorators(project, target, name, config, metricsConfiguration, annotations, port)); - result.addAll(createPodDecorators(project, target, name, config)); - result.addAll(createContainerDecorators(project, target, name, namespace, config)); - result.addAll(createMountAndVolumeDecorators(project, target, name, config)); - result.addAll(createAppConfigVolumeAndEnvDecorators(project, target, name, config)); + result.addAll(createPodDecorators(target, name, config)); + result.addAll(createContainerDecorators(target, name, namespace, config)); + result.addAll(createMountAndVolumeDecorators(target, name, config)); + result.addAll(createAppConfigVolumeAndEnvDecorators(target, name, config)); - result.addAll(createCommandDecorator(project, target, name, config, command)); - result.addAll(createArgsDecorator(project, target, name, config, command)); + result.addAll(createCommandDecorator(target, name, config, command)); + result.addAll(createArgsDecorator(target, name, config, command)); // Handle Pull Secrets if (config.isGenerateImagePullSecret()) { - image.ifPresent(i -> { - i.getRegistry().ifPresent(registry -> { - if (i.getUsername().isPresent() && i.getPassword().isPresent()) { - String imagePullSecret = name + "-pull-secret"; - result.add(new DecoratorBuildItem(target, new AddImagePullSecretDecorator(name, imagePullSecret))); - result.add(new DecoratorBuildItem(target, new AddDockerConfigJsonSecretDecorator(imagePullSecret, - registry, i.username.get(), i.password.get()))); - } - }); - }); + image.ifPresent(i -> i.getRegistry().ifPresent(registry -> { + if (i.getUsername().isPresent() && i.getPassword().isPresent()) { + String imagePullSecret = name + "-pull-secret"; + result.add(new DecoratorBuildItem(target, new AddImagePullSecretDecorator(name, imagePullSecret))); + result.add(new DecoratorBuildItem(target, new AddDockerConfigJsonSecretDecorator(imagePullSecret, + registry, i.username.get(), i.password.get()))); + } + })); } // Handle Probes - if (!port.isEmpty()) { + if (port.isPresent()) { result.addAll(createProbeDecorators(name, target, config.getLivenessProbe(), config.getReadinessProbe(), config.getStartupProbe(), livenessProbePath, readinessProbePath, startupPath)); } @@ -294,7 +288,7 @@ private static Collection createRbacDecorators(String name, Optional kubernetesClientConfiguration, List rolesFromExtensions, List clusterRolesFromExtensions, - List serviceAccountsFromExtensions, + List effectiveServiceAccounts, List roleBindingsFromExtensions) { List result = new ArrayList<>(); boolean kubernetesClientRequiresRbacGeneration = kubernetesClientConfiguration @@ -316,24 +310,15 @@ private static Collection createRbacDecorators(String name, } // Add roles from extensions - for (KubernetesRoleBuildItem role : rolesFromExtensions) { - if (role.getTarget() == null || role.getTarget().equals(target)) { - result.add(new DecoratorBuildItem(target, new AddRoleResourceDecorator(name, + Targetable.filteredByTarget(rolesFromExtensions, target) + .map(role -> new DecoratorBuildItem(target, new AddRoleResourceDecorator(name, role.getName(), role.getNamespace(), Collections.emptyMap(), - role.getRules() - .stream() - .map(it -> new PolicyRuleBuilder() - .withApiGroups(it.getApiGroups()) - .withNonResourceURLs(it.getNonResourceURLs()) - .withResourceNames(it.getResourceNames()) - .withResources(it.getResources()) - .withVerbs(it.getVerbs()) - .build()) - .collect(Collectors.toList())))); - } - } + role.getRules().stream() + .map(RBACUtil::from) + .toList()))) + .forEach(result::add); // Add cluster roles from configuration for (Map.Entry clusterRoleFromConfig : config.getRbacConfig().clusterRoles.entrySet()) { @@ -347,56 +332,26 @@ private static Collection createRbacDecorators(String name, } // Add cluster roles from extensions - for (KubernetesClusterRoleBuildItem role : clusterRolesFromExtensions) { - if (role.getTarget() == null || role.getTarget().equals(target)) { - result.add(new DecoratorBuildItem(target, new AddClusterRoleResourceDecorator(name, + Targetable.filteredByTarget(clusterRolesFromExtensions, target) + .map(role -> new DecoratorBuildItem(target, new AddClusterRoleResourceDecorator(name, role.getName(), Collections.emptyMap(), - role.getRules() - .stream() - .map(it -> new PolicyRuleBuilder() - .withApiGroups(it.getApiGroups()) - .withNonResourceURLs(it.getNonResourceURLs()) - .withResourceNames(it.getResourceNames()) - .withResources(it.getResources()) - .withVerbs(it.getVerbs()) - .build()) - .collect(Collectors.toList())))); - } - } - - Optional effectiveServiceAccount = Optional.empty(); - String effectiveServiceAccountNamespace = null; - for (KubernetesServiceAccountBuildItem sa : serviceAccountsFromExtensions) { - String saName = Optional.ofNullable(sa.getName()).orElse(name); - result.add(new DecoratorBuildItem(target, new AddServiceAccountResourceDecorator(name, saName, - sa.getNamespace(), - sa.getLabels()))); - - if (sa.isUseAsDefault() || effectiveServiceAccount.isEmpty()) { - effectiveServiceAccount = Optional.of(saName); - effectiveServiceAccountNamespace = sa.getNamespace(); - } - } - - // Add service account from configuration - for (Map.Entry sa : config.getRbacConfig().serviceAccounts.entrySet()) { - String saName = sa.getValue().name.orElse(sa.getKey()); - result.add(new DecoratorBuildItem(target, new AddServiceAccountResourceDecorator(name, saName, - sa.getValue().namespace.orElse(null), - sa.getValue().labels))); - - if (sa.getValue().isUseAsDefault() || effectiveServiceAccount.isEmpty()) { - effectiveServiceAccount = Optional.of(saName); - effectiveServiceAccountNamespace = sa.getValue().namespace.orElse(null); - } + role.getRules().stream() + .map(RBACUtil::from) + .toList()))) + .forEach(result::add); + + // Retrieve SA for current target + final var potentialSAs = Targetable.filteredByTarget(effectiveServiceAccounts, target).toList(); + if (potentialSAs.isEmpty()) { + throw new RuntimeException("No effective service account found for application " + name); } - - // The user provided service account should always take precedence - if (config.getServiceAccount().isPresent()) { - effectiveServiceAccount = config.getServiceAccount(); - effectiveServiceAccountNamespace = null; + if (potentialSAs.size() > 1) { + throw new RuntimeException("More than one effective service account found for application " + name); } + final var effectiveServiceAccount = potentialSAs.get(0); + final var effectiveServiceAccountNamespace = effectiveServiceAccount.getNamespace(); + final var effectiveServiceAccountName = effectiveServiceAccount.getServiceAccountName(); // Prepare default configuration String defaultRoleName = null; @@ -412,15 +367,13 @@ private static Collection createRbacDecorators(String name, } // Add role bindings from extensions - for (KubernetesRoleBindingBuildItem rb : roleBindingsFromExtensions) { - if (rb.getTarget() == null || rb.getTarget().equals(target)) { - result.add(new DecoratorBuildItem(target, new AddRoleBindingResourceDecorator(name, + Targetable.filteredByTarget(roleBindingsFromExtensions, target) + .map(rb -> new DecoratorBuildItem(target, new AddRoleBindingResourceDecorator(name, Strings.isNotNullOrEmpty(rb.getName()) ? rb.getName() : name + "-" + rb.getRoleRef().getName(), rb.getLabels(), rb.getRoleRef(), - rb.getSubjects()))); - } - } + rb.getSubjects()))) + .forEach(result::add); // Add role bindings from configuration for (Map.Entry rb : config.getRbacConfig().roleBindings.entrySet()) { @@ -431,7 +384,7 @@ private static Collection createRbacDecorators(String name, if (roleBinding.subjects.isEmpty()) { requiresServiceAccount = true; subjects.add(new Subject(null, SERVICE_ACCOUNT, - effectiveServiceAccount.orElse(name), + effectiveServiceAccountName, effectiveServiceAccountNamespace)); } else { for (Map.Entry s : roleBinding.subjects.entrySet()) { @@ -493,7 +446,7 @@ private static Collection createRbacDecorators(String name, Collections.emptyMap(), new RoleRef(defaultRoleName, defaultClusterWide), new Subject(null, SERVICE_ACCOUNT, - effectiveServiceAccount.orElse(name), + effectiveServiceAccountName, effectiveServiceAccountNamespace)))); } else if (kubernetesClientRequiresRbacGeneration) { // the property `quarkus.kubernetes-client.generate-rbac` is enabled @@ -504,7 +457,7 @@ private static Collection createRbacDecorators(String name, Collections.emptyMap(), new RoleRef(DEFAULT_ROLE_NAME_VIEW, true), new Subject(null, SERVICE_ACCOUNT, - effectiveServiceAccount.orElse(name), + effectiveServiceAccountName, effectiveServiceAccountNamespace)))); } } @@ -513,22 +466,62 @@ private static Collection createRbacDecorators(String name, if (requiresServiceAccount) { // and generate the resource result.add(new DecoratorBuildItem(target, - new AddServiceAccountResourceDecorator(name, effectiveServiceAccount.orElse(name), + new AddServiceAccountResourceDecorator(name, effectiveServiceAccountName, effectiveServiceAccountNamespace, Collections.emptyMap()))); } // set service account in deployment resource if the user sets a service account, // or it's required for a dependant resource. - if (effectiveServiceAccount.isPresent() || requiresServiceAccount) { - result.add(new DecoratorBuildItem(target, - new ApplyServiceAccountNameDecorator(name, effectiveServiceAccount.orElse(name)))); + if (effectiveServiceAccount.wasSet() || requiresServiceAccount) { + result.add(new DecoratorBuildItem(target, new ApplyServiceAccountNameDecorator(name, effectiveServiceAccountName))); } return result; } - private static Collection createLabelDecorators(Optional project, String target, String name, + public static KubernetesEffectiveServiceAccountBuildItem computeEffectiveServiceAccount(String name, String target, + PlatformConfiguration config, List serviceAccountsFromExtensions, + BuildProducer decorators) { + Optional effectiveServiceAccount = Optional.empty(); + String effectiveServiceAccountNamespace = null; + for (KubernetesServiceAccountBuildItem sa : serviceAccountsFromExtensions) { + String saName = Optional.ofNullable(sa.getName()).orElse(name); + decorators.produce(new DecoratorBuildItem(target, new AddServiceAccountResourceDecorator(name, saName, + sa.getNamespace(), + sa.getLabels()))); + + if (sa.isUseAsDefault() || effectiveServiceAccount.isEmpty()) { + effectiveServiceAccount = Optional.of(saName); + effectiveServiceAccountNamespace = sa.getNamespace(); + } + } + + // Add service account from configuration + for (Map.Entry sa : config.getRbacConfig().serviceAccounts.entrySet()) { + String saName = sa.getValue().name.orElse(sa.getKey()); + decorators.produce(new DecoratorBuildItem(target, new AddServiceAccountResourceDecorator(name, saName, + sa.getValue().namespace.orElse(null), + sa.getValue().labels))); + + if (sa.getValue().isUseAsDefault() || effectiveServiceAccount.isEmpty()) { + effectiveServiceAccount = Optional.of(saName); + effectiveServiceAccountNamespace = sa.getValue().namespace.orElse(null); + } + } + + // The user provided service account should always take precedence + if (config.getServiceAccount().isPresent()) { + effectiveServiceAccount = config.getServiceAccount(); + effectiveServiceAccountNamespace = null; + } + + final var effectiveName = effectiveServiceAccount.orElse(name); + return new KubernetesEffectiveServiceAccountBuildItem(effectiveName, effectiveServiceAccountNamespace, + effectiveServiceAccount.isPresent(), target); + } + + private static Collection createLabelDecorators(String target, String name, PlatformConfiguration config, List labels) { List result = new ArrayList<>(); @@ -536,10 +529,8 @@ private static Collection createLabelDecorators(Optional { - result.add(new DecoratorBuildItem(l.getTarget(), - new AddLabelDecorator(name, l.getKey(), l.getValue()))); - }); + labels.forEach(l -> result.add(new DecoratorBuildItem(l.getTarget(), + new AddLabelDecorator(name, l.getKey(), l.getValue())))); if (!config.isAddVersionToLabelSelectors() || config.isIdempotent()) { result.add(new DecoratorBuildItem(target, new RemoveFromSelectorDecorator(name, Labels.VERSION))); @@ -568,7 +559,7 @@ private static Collection createLabelDecorators(Optional createCommandDecorator(Optional project, String target, String name, + private static List createCommandDecorator(String target, String name, PlatformConfiguration config, Optional command) { List result = new ArrayList<>(); if (config.getCommand().isPresent()) { @@ -593,7 +584,7 @@ private static List createCommandDecorator(Optional * @param config The {@link PlatformConfiguration} instance * @param command Optional command item from other extensions */ - private static List createArgsDecorator(Optional project, String target, String name, + private static List createArgsDecorator(String target, String name, PlatformConfiguration config, Optional command) { List result = new ArrayList<>(); @@ -602,7 +593,7 @@ private static List createArgsDecorator(Optional pr config.getArguments().ifPresent(args::addAll); if (!args.isEmpty()) { - result.add(new DecoratorBuildItem(target, new ApplyArgsDecorator(name, args.toArray(new String[args.size()])))); + result.add(new DecoratorBuildItem(target, new ApplyArgsDecorator(name, args.toArray(new String[0])))); } return result; @@ -617,21 +608,21 @@ public static List createInitContainerDecorators(String targ .map(d -> d.getDecorator(AddEnvVarDecorator.class)) .filter(Optional::isPresent) .map(Optional::get) - .collect(Collectors.toList()); + .toList(); List mountDecorators = decorators.stream() .filter(d -> d.getGroup() == null || d.getGroup().equals(target)) .map(d -> d.getDecorator(AddMountDecorator.class)) .filter(Optional::isPresent) .map(Optional::get) - .collect(Collectors.toList()); + .toList(); items.stream().filter(item -> item.getTarget() == null || item.getTarget().equals(target)).forEach(item -> { io.dekorate.kubernetes.config.ContainerBuilder containerBuilder = new io.dekorate.kubernetes.config.ContainerBuilder() .withName(item.getName()) .withImage(item.getImage()) - .withCommand(item.getCommand().toArray(new String[item.getCommand().size()])) - .withArguments(item.getArguments().toArray(new String[item.getArguments().size()])); + .withCommand(item.getCommand().toArray(new String[0])) + .withArguments(item.getArguments().toArray(new String[0])); if (item.isSharedEnvironment()) { for (final AddEnvVarDecorator delegate : envVarDecorators) { @@ -679,7 +670,7 @@ public void andThenVisit(ContainerBuilder builder) { return result; } - public static List createInitJobDecorators(String target, String name, + public static List createInitJobDecorators(String target, List items, List decorators) { List result = new ArrayList<>(); @@ -688,13 +679,12 @@ public static List createInitJobDecorators(String target, St .map(d -> d.getDecorator(AddEnvVarDecorator.class)) .filter(Optional::isPresent) .map(Optional::get) - .collect(Collectors.toList()); + .toList(); List> volumeDecorators = decorators.stream() .filter(d -> d.getGroup() == null || d.getGroup().equals(target)) .filter(d -> d.getDecorator() instanceof AddEmptyDirVolumeDecorator || d.getDecorator() instanceof AddSecretVolumeDecorator - || d.getDecorator() instanceof AddEmptyDirVolumeDecorator || d.getDecorator() instanceof AddAzureDiskVolumeDecorator || d.getDecorator() instanceof AddAzureFileVolumeDecorator || d.getDecorator() instanceof AddAwsElasticBlockStoreVolumeDecorator) @@ -706,21 +696,21 @@ public static List createInitJobDecorators(String target, St .map(d -> d.getDecorator(AddMountDecorator.class)) .filter(Optional::isPresent) .map(Optional::get) - .collect(Collectors.toList()); + .toList(); List imagePullSecretDecorators = decorators.stream() .filter(d -> d.getGroup() == null || d.getGroup().equals(target)) .map(d -> d.getDecorator(AddImagePullSecretDecorator.class)) .filter(Optional::isPresent) .map(Optional::get) - .collect(Collectors.toList()); + .toList(); List serviceAccountDecorators = decorators.stream() .filter(d -> d.getGroup() == null || d.getGroup().equals(target)) .map(d -> d.getDecorator(ApplyServiceAccountNameDecorator.class)) .filter(Optional::isPresent) .map(Optional::get) - .collect(Collectors.toList()); + .toList(); items.stream().filter(item -> item.getTarget() == null || item.getTarget().equals(target)).forEach(item -> { @@ -816,7 +806,7 @@ public void andThenVisit(ContainerBuilder builder, ObjectMeta meta) { * param namespace The optional namespace to apply to the resource * @param config The {@link PlatformConfiguration} instance */ - private static List createContainerDecorators(Optional project, String target, String name, + private static List createContainerDecorators(String target, String name, Optional namespace, PlatformConfiguration config) { List result = new ArrayList<>(); @@ -825,9 +815,8 @@ private static List createContainerDecorators(Optional { - result.add(new DecoratorBuildItem(target, new ApplyWorkingDirDecorator(name, w))); - }); + config.getWorkingDir() + .ifPresent(w -> result.add(new DecoratorBuildItem(target, new ApplyWorkingDirDecorator(name, w)))); return result; } @@ -839,40 +828,32 @@ private static List createContainerDecorators(Optional createPodDecorators(Optional project, String target, String name, + private static List createPodDecorators(String target, String name, PlatformConfiguration config) { List result = new ArrayList<>(); - config.getImagePullSecrets().ifPresent(l -> { - l.forEach(s -> result.add(new DecoratorBuildItem(target, new AddImagePullSecretDecorator(name, s)))); - }); + config.getImagePullSecrets().ifPresent( + l -> l.forEach(s -> result.add(new DecoratorBuildItem(target, new AddImagePullSecretDecorator(name, s))))); - config.getHostAliases().entrySet().forEach(e -> { - result.add(new DecoratorBuildItem(target, new AddHostAliasesDecorator(name, HostAliasConverter.convert(e)))); - }); + config.getHostAliases().entrySet().forEach(e -> result + .add(new DecoratorBuildItem(target, new AddHostAliasesDecorator(name, HostAliasConverter.convert(e))))); - config.getInitContainers().entrySet().forEach(e -> { - result.add(new DecoratorBuildItem(target, new AddInitContainerDecorator(name, ContainerConverter.convert(e)))); - }); + config.getInitContainers().entrySet().forEach(e -> result + .add(new DecoratorBuildItem(target, new AddInitContainerDecorator(name, ContainerConverter.convert(e))))); - config.getSidecars().entrySet().forEach(e -> { - result.add(new DecoratorBuildItem(target, new AddSidecarDecorator(name, ContainerConverter.convert(e)))); - }); + config.getSidecars().entrySet().forEach( + e -> result.add(new DecoratorBuildItem(target, new AddSidecarDecorator(name, ContainerConverter.convert(e))))); - config.getResources().limits.cpu.ifPresent(c -> { - result.add(new DecoratorBuildItem(target, new ApplyLimitsCpuDecorator(name, c))); - }); + config.getResources().limits.cpu + .ifPresent(c -> result.add(new DecoratorBuildItem(target, new ApplyLimitsCpuDecorator(name, c)))); - config.getResources().limits.memory.ifPresent(m -> { - result.add(new DecoratorBuildItem(target, new ApplyLimitsMemoryDecorator(name, m))); - }); + config.getResources().limits.memory + .ifPresent(m -> result.add(new DecoratorBuildItem(target, new ApplyLimitsMemoryDecorator(name, m)))); - config.getResources().requests.cpu.ifPresent(c -> { - result.add(new DecoratorBuildItem(target, new ApplyRequestsCpuDecorator(name, c))); - }); + config.getResources().requests.cpu + .ifPresent(c -> result.add(new DecoratorBuildItem(target, new ApplyRequestsCpuDecorator(name, c)))); - config.getResources().requests.memory.ifPresent(m -> { - result.add(new DecoratorBuildItem(target, new ApplyRequestsMemoryDecorator(name, m))); - }); + config.getResources().requests.memory + .ifPresent(m -> result.add(new DecoratorBuildItem(target, new ApplyRequestsMemoryDecorator(name, m)))); if (config.getSecurityContext().isAnyPropertySet()) { result.add(new DecoratorBuildItem(target, new ApplySecuritySettingsDecorator(name, config.getSecurityContext()))); @@ -881,7 +862,7 @@ private static List createPodDecorators(Optional pr return result; } - private static List createAppConfigVolumeAndEnvDecorators(Optional project, String target, + private static List createAppConfigVolumeAndEnvDecorators(String target, String name, PlatformConfiguration config) { @@ -916,49 +897,40 @@ private static List createAppConfigVolumeAndEnvDecorators(Op result.add(new DecoratorBuildItem(target, new AddEnvVarDecorator(ApplicationContainerDecorator.ANY, name, new EnvBuilder() .withName("SMALLRYE_CONFIG_LOCATIONS") - .withValue(paths.stream().collect(Collectors.joining(","))) + .withValue(String.join(",", paths)) .build()))); } return result; } - private static List createMountAndVolumeDecorators(Optional project, String target, + private static List createMountAndVolumeDecorators(String target, String name, PlatformConfiguration config) { List result = new ArrayList<>(); - config.getMounts().entrySet().forEach(e -> { - result.add(new DecoratorBuildItem(target, new AddMountDecorator(ANY, name, MountConverter.convert(e)))); - }); + config.getMounts().entrySet().forEach( + e -> result.add(new DecoratorBuildItem(target, new AddMountDecorator(ANY, name, MountConverter.convert(e))))); - config.getSecretVolumes().entrySet().forEach(e -> { - result.add(new DecoratorBuildItem(target, new AddSecretVolumeDecorator(SecretVolumeConverter.convert(e)))); - }); + config.getSecretVolumes().entrySet().forEach(e -> result + .add(new DecoratorBuildItem(target, new AddSecretVolumeDecorator(SecretVolumeConverter.convert(e))))); - config.getConfigMapVolumes().entrySet().forEach(e -> { - result.add(new DecoratorBuildItem(target, new AddConfigMapVolumeDecorator(ConfigMapVolumeConverter.convert(e)))); - }); + config.getConfigMapVolumes().entrySet().forEach(e -> result + .add(new DecoratorBuildItem(target, new AddConfigMapVolumeDecorator(ConfigMapVolumeConverter.convert(e))))); - config.getEmptyDirVolumes().forEach(e -> { - result.add(new DecoratorBuildItem(target, new AddEmptyDirVolumeDecorator(EmptyDirVolumeConverter.convert(e)))); - }); + config.getEmptyDirVolumes().forEach(e -> result + .add(new DecoratorBuildItem(target, new AddEmptyDirVolumeDecorator(EmptyDirVolumeConverter.convert(e))))); - config.getPvcVolumes().entrySet().forEach(e -> { - result.add(new DecoratorBuildItem(target, new AddPvcVolumeDecorator(PvcVolumeConverter.convert(e)))); - }); + config.getPvcVolumes().entrySet().forEach( + e -> result.add(new DecoratorBuildItem(target, new AddPvcVolumeDecorator(PvcVolumeConverter.convert(e))))); - config.getAwsElasticBlockStoreVolumes().entrySet().forEach(e -> { - result.add(new DecoratorBuildItem(target, - new AddAwsElasticBlockStoreVolumeDecorator(AwsElasticBlockStoreVolumeConverter.convert(e)))); - }); + config.getAwsElasticBlockStoreVolumes().entrySet().forEach(e -> result.add(new DecoratorBuildItem(target, + new AddAwsElasticBlockStoreVolumeDecorator(AwsElasticBlockStoreVolumeConverter.convert(e))))); - config.getAzureFileVolumes().entrySet().forEach(e -> { - result.add(new DecoratorBuildItem(target, new AddAzureFileVolumeDecorator(AzureFileVolumeConverter.convert(e)))); - }); + config.getAzureFileVolumes().entrySet().forEach(e -> result + .add(new DecoratorBuildItem(target, new AddAzureFileVolumeDecorator(AzureFileVolumeConverter.convert(e))))); - config.getAzureDiskVolumes().entrySet().forEach(e -> { - result.add(new DecoratorBuildItem(target, new AddAzureDiskVolumeDecorator(AzureDiskVolumeConverter.convert(e)))); - }); + config.getAzureDiskVolumes().entrySet().forEach(e -> result + .add(new DecoratorBuildItem(target, new AddAzureDiskVolumeDecorator(AzureDiskVolumeConverter.convert(e))))); return result; } @@ -969,10 +941,8 @@ private static List createAnnotationDecorators(Optional port) { List result = new ArrayList<>(); - annotations.forEach(a -> { - result.add(new DecoratorBuildItem(a.getTarget(), - new AddAnnotationDecorator(name, a.getKey(), a.getValue()))); - }); + annotations.forEach(a -> result.add(new DecoratorBuildItem(a.getTarget(), + new AddAnnotationDecorator(name, a.getKey(), a.getValue())))); ZonedDateTime now = ZonedDateTime.now(ZoneOffset.UTC); @@ -1052,7 +1022,7 @@ private static List createAnnotationDecorators(Optional createProbeDecorators(String name, Strin Optional readinessPath, Optional startupPath) { List result = new ArrayList<>(); - createLivenessProbe(name, target, livenessProbe, livenessPath).ifPresent(d -> result.add(d)); - createReadinessProbe(name, target, readinessProbe, readinessPath).ifPresent(d -> result.add(d)); + createLivenessProbe(name, target, livenessProbe, livenessPath).ifPresent(result::add); + createReadinessProbe(name, target, readinessProbe, readinessPath).ifPresent(result::add); if (!KNATIVE.equals(target)) { // see https://github.com/quarkusio/quarkus/issues/33944 - createStartupProbe(name, target, startupProbe, startupPath).ifPresent(d -> result.add(d)); + createStartupProbe(name, target, startupProbe, startupPath).ifPresent(result::add); } return result; } @@ -1185,16 +1155,7 @@ private static Map verifyPorts(List ku } private static List toPolicyRulesList(Map policyRules) { - return policyRules.values() - .stream() - .map(it -> new PolicyRuleBuilder() - .withApiGroups(it.apiGroups.orElse(LIST_WITH_EMPTY)) - .withNonResourceURLs(it.nonResourceUrls.orElse(null)) - .withResourceNames(it.resourceNames.orElse(null)) - .withResources(it.resources.orElse(null)) - .withVerbs(it.verbs.orElse(null)) - .build()) - .collect(Collectors.toList()); + return policyRules.values().stream().map(RBACUtil::from).toList(); } private static String parseVCSUri(VCSUriConfig config, ScmInfo scm) { 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 5759caf4f99472..acbb17fe6793df 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 @@ -1,15 +1,7 @@ package io.quarkus.kubernetes.deployment; -import static io.quarkus.kubernetes.deployment.Constants.DEFAULT_S2I_IMAGE_NAME; -import static io.quarkus.kubernetes.deployment.Constants.LIVENESS_PROBE; -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.OPENSHIFT_INTERNAL_REGISTRY_PROJECT; -import static io.quarkus.kubernetes.deployment.Constants.QUARKUS; -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.Constants.*; 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; @@ -63,6 +55,7 @@ import io.quarkus.kubernetes.spi.KubernetesClusterRoleBuildItem; import io.quarkus.kubernetes.spi.KubernetesCommandBuildItem; import io.quarkus.kubernetes.spi.KubernetesDeploymentTargetBuildItem; +import io.quarkus.kubernetes.spi.KubernetesEffectiveServiceAccountBuildItem; import io.quarkus.kubernetes.spi.KubernetesEnvBuildItem; import io.quarkus.kubernetes.spi.KubernetesHealthLivenessPathBuildItem; import io.quarkus.kubernetes.spi.KubernetesHealthReadinessPathBuildItem; @@ -77,13 +70,13 @@ import io.quarkus.kubernetes.spi.KubernetesRoleBindingBuildItem; import io.quarkus.kubernetes.spi.KubernetesRoleBuildItem; import io.quarkus.kubernetes.spi.KubernetesServiceAccountBuildItem; +import io.quarkus.kubernetes.spi.Targetable; public class OpenshiftProcessor { private static final int OPENSHIFT_PRIORITY = DEFAULT_PRIORITY; private static final String DOCKERIO_REGISTRY = "docker.io"; private static final String OPENSHIFT_V3_APP = "app"; - private static final String ANY = null; @BuildStep public void checkOpenshift(ApplicationInfoBuildItem applicationInfo, Capabilities capabilities, OpenshiftConfig config, @@ -110,10 +103,10 @@ public void populateInternalRegistry(OpenshiftConfig openshiftConfig, ContainerI BuildProducer containerImageRegistry, BuildProducer singleSegmentContainerImageRequest) { - if (!containerImageConfig.registry.isPresent() && !containerImageConfig.image.isPresent()) { + if (containerImageConfig.registry.isEmpty() && containerImageConfig.image.isEmpty()) { DeploymentResourceKind deploymentResourceKind = openshiftConfig.getDeploymentResourceKind(capabilities); if (deploymentResourceKind != DeploymentResourceKind.DeploymentConfig) { - if (openshiftConfig.isOpenshiftBuildEnabled(containerImageConfig, capabilities)) { + if (OpenshiftConfig.isOpenshiftBuildEnabled(containerImageConfig, capabilities)) { //Don't need fallback namespace, we use local lookup instead. singleSegmentContainerImageRequest.produce(new SingleSegmentContainerImageRequestBuildItem()); } else { @@ -125,9 +118,7 @@ public void populateInternalRegistry(OpenshiftConfig openshiftConfig, ContainerI @BuildStep public void createAnnotations(OpenshiftConfig config, BuildProducer annotations) { - config.getAnnotations().forEach((k, v) -> { - annotations.produce(new KubernetesAnnotationBuildItem(k, v, OPENSHIFT)); - }); + config.getAnnotations().forEach((k, v) -> annotations.produce(new KubernetesAnnotationBuildItem(k, v, OPENSHIFT))); } @BuildStep @@ -142,21 +133,19 @@ public void createLabels(OpenshiftConfig config, BuildProducer namespace) { - config.getNamespace().ifPresent(n -> { - namespace.produce(new KubernetesNamespaceBuildItem(OPENSHIFT, n)); - }); + config.getNamespace().ifPresent(n -> namespace.produce(new KubernetesNamespaceBuildItem(OPENSHIFT, n))); } + @SuppressWarnings("OptionalUsedAsFieldOrParameterType") @BuildStep - public List createConfigurators(ApplicationInfoBuildItem applicationInfo, + public List createConfigurators( OpenshiftConfig config, Capabilities capabilities, Optional image, List ports) { List result = new ArrayList<>(); - KubernetesCommonHelper.combinePorts(ports, config).values().forEach(value -> { - result.add(new ConfiguratorBuildItem(new AddPortToOpenshiftConfig(value))); - }); + KubernetesCommonHelper.combinePorts(ports, config).values() + .forEach(value -> result.add(new ConfiguratorBuildItem(new AddPortToOpenshiftConfig(value)))); result.add(new ConfiguratorBuildItem(new ApplyOpenshiftRouteConfigurator(config.route))); @@ -169,18 +158,27 @@ public List createConfigurators(ApplicationInfoBuildItem && !capabilities.isPresent(Capability.CONTAINER_IMAGE_OPENSHIFT)) { result.add(new ConfiguratorBuildItem(new DisableS2iConfigurator())); - image.flatMap(ContainerImageInfoBuildItem::getRegistry).ifPresent(r -> { - result.add(new ConfiguratorBuildItem(new ApplyImageRegistryConfigurator(r))); - }); + image.flatMap(ContainerImageInfoBuildItem::getRegistry) + .ifPresent(r -> result.add(new ConfiguratorBuildItem(new ApplyImageRegistryConfigurator(r)))); - image.map(ContainerImageInfoBuildItem::getGroup).ifPresent(g -> { - result.add(new ConfiguratorBuildItem(new ApplyImageGroupConfigurator(g))); - }); + image.map(ContainerImageInfoBuildItem::getGroup) + .ifPresent(g -> result.add(new ConfiguratorBuildItem(new ApplyImageGroupConfigurator(g)))); } return result; } + @BuildStep + public KubernetesEffectiveServiceAccountBuildItem computeEffectiveServiceAccounts(ApplicationInfoBuildItem applicationInfo, + KubernetesConfig config, List serviceAccountsFromExtensions, + BuildProducer decorators) { + final String name = ResourceNameUtil.getResourceName(config, applicationInfo); + return KubernetesCommonHelper.computeEffectiveServiceAccount(name, OPENSHIFT, + config, serviceAccountsFromExtensions, + decorators); + } + + @SuppressWarnings("OptionalUsedAsFieldOrParameterType") @BuildStep public List createDecorators(ApplicationInfoBuildItem applicationInfo, OutputTargetBuildItem outputTarget, @@ -207,25 +205,24 @@ public List createDecorators(ApplicationInfoBuildItem applic Optional startupPath, List roles, List clusterRoles, - List serviceAccounts, + List serviceAccounts, List roleBindings, Optional customProjectRoot, List targets) { List result = new ArrayList<>(); - if (!targets.stream().filter(KubernetesDeploymentTargetBuildItem::isEnabled) - .anyMatch(t -> OPENSHIFT.equals(t.getName()))) { + if (targets.stream().filter(KubernetesDeploymentTargetBuildItem::isEnabled) + .noneMatch(t -> OPENSHIFT.equals(t.getName()))) { return result; } String name = ResourceNameUtil.getResourceName(config, applicationInfo); + final var namespace = Targetable.filteredByTarget(namespaces, OPENSHIFT, true) + .findFirst(); Optional project = KubernetesCommonHelper.createProject(applicationInfo, customProjectRoot, outputTarget, packageConfig); Optional port = KubernetesCommonHelper.getPort(ports, config, config.route.targetPort); - Optional namespace = namespaces.stream() - .filter(n -> OPENSHIFT.equals(n.getTarget())) - .findFirst(); result.addAll(KubernetesCommonHelper.createDecorators(project, OPENSHIFT, name, namespace, config, metricsConfiguration, kubernetesClientConfiguration, annotations, labels, image, command, @@ -305,14 +302,12 @@ public List createDecorators(ApplicationInfoBuildItem applic result.add(new DecoratorBuildItem(OPENSHIFT, new AddLabelDecorator(name, OPENSHIFT_APP_RUNTIME, QUARKUS))); } - Stream.concat(config.convertToBuildItems().stream(), - envs.stream().filter(e -> e.getTarget() == null || OPENSHIFT.equals(e.getTarget()))).forEach(e -> { - result.add(new DecoratorBuildItem(OPENSHIFT, - new AddEnvVarDecorator(ApplicationContainerDecorator.ANY, name, - new EnvBuilder().withName(EnvConverter.convertName(e.getName())).withValue(e.getValue()) - .withSecret(e.getSecret()).withConfigmap(e.getConfigMap()).withField(e.getField()) - .build()))); - }); + Stream.concat(config.convertToBuildItems().stream(), Targetable.filteredByTarget(envs, OPENSHIFT)) + .forEach(e -> result.add(new DecoratorBuildItem(OPENSHIFT, + new AddEnvVarDecorator(ApplicationContainerDecorator.ANY, name, + new EnvBuilder().withName(EnvConverter.convertName(e.getName())).withValue(e.getValue()) + .withSecret(e.getSecret()).withConfigmap(e.getConfigMap()).withField(e.getField()) + .build())))); // Enalbe local lookup policy for all image streams result.add(new DecoratorBuildItem(OPENSHIFT, new EnableImageStreamLocalLookupPolicyDecorator())); @@ -324,7 +319,8 @@ public List createDecorators(ApplicationInfoBuildItem applic result.add(new DecoratorBuildItem(OPENSHIFT, new RemoveBuilderImageResourceDecorator(DEFAULT_S2I_IMAGE_NAME))); } - if (containerImageConfig.builder.isEmpty() || config.isOpenshiftBuildEnabled(containerImageConfig, capabilities)) { + if (containerImageConfig.builder.isEmpty() + || OpenshiftConfig.isOpenshiftBuildEnabled(containerImageConfig, capabilities)) { result.add(new DecoratorBuildItem(OPENSHIFT, new ApplyBuilderImageDecorator(name, builderImage))); ImageReference imageRef = ImageReference.parse(builderImage); boolean usesInternalRegistry = imageRef.getRegistry() @@ -367,7 +363,8 @@ public List createDecorators(ApplicationInfoBuildItem applic image.ifPresent(i -> { String registry = i.registry .or(() -> containerImageConfig.registry) - .orElse(fallbackRegistry.map(f -> f.getRegistry()).orElse(DOCKERIO_REGISTRY)); + .orElse(fallbackRegistry.map(FallbackContainerImageRegistryBuildItem::getRegistry) + .orElse(DOCKERIO_REGISTRY)); String repositoryWithRegistry = registry + "/" + i.getRepository(); ImageConfiguration imageConfiguration = new ImageConfigurationBuilder() .withName(name) @@ -395,7 +392,7 @@ public List createDecorators(ApplicationInfoBuildItem applic // Handle init Containers and Jobs result.addAll(KubernetesCommonHelper.createInitContainerDecorators(OPENSHIFT, name, initContainers, result)); - result.addAll(KubernetesCommonHelper.createInitJobDecorators(OPENSHIFT, name, jobs, result)); + result.addAll(KubernetesCommonHelper.createInitJobDecorators(OPENSHIFT, jobs, result)); // Do not bind the Management port to the Service resource unless it's explicitly used by the user. if (managementPortIsEnabled() diff --git a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/RBACUtil.java b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/RBACUtil.java new file mode 100644 index 00000000000000..0ceea595d9c014 --- /dev/null +++ b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/RBACUtil.java @@ -0,0 +1,39 @@ +package io.quarkus.kubernetes.deployment; + +import java.util.List; + +import io.fabric8.kubernetes.api.model.rbac.PolicyRuleBuilder; +import io.quarkus.kubernetes.spi.PolicyRule; + +public class RBACUtil { + private static final List LIST_WITH_EMPTY = List.of(""); + + private RBACUtil() { + } + + public static io.fabric8.kubernetes.api.model.rbac.PolicyRule from(PolicyRule rule) { + if (rule == null) { + return null; + } + return new PolicyRuleBuilder() + .withApiGroups(rule.getApiGroups()) + .withNonResourceURLs(rule.getNonResourceURLs()) + .withResourceNames(rule.getResourceNames()) + .withResources(rule.getResources()) + .withVerbs(rule.getVerbs()) + .build(); + } + + public static io.fabric8.kubernetes.api.model.rbac.PolicyRule from(PolicyRuleConfig policyRuleConfig) { + if (policyRuleConfig == null) { + return null; + } + return new PolicyRuleBuilder() + .withApiGroups(policyRuleConfig.apiGroups.orElse(LIST_WITH_EMPTY)) + .withNonResourceURLs(policyRuleConfig.nonResourceUrls.orElse(null)) + .withResourceNames(policyRuleConfig.resourceNames.orElse(null)) + .withResources(policyRuleConfig.resources.orElse(null)) + .withVerbs(policyRuleConfig.verbs.orElse(null)) + .build(); + } +} 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 3755c84d58126f..2a1cda278b14a3 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 @@ -15,7 +15,6 @@ import java.util.List; import java.util.Map; import java.util.Optional; -import java.util.stream.Collectors; import java.util.stream.Stream; import io.dekorate.kubernetes.annotation.ServiceType; @@ -53,6 +52,7 @@ import io.quarkus.kubernetes.spi.KubernetesClusterRoleBuildItem; import io.quarkus.kubernetes.spi.KubernetesCommandBuildItem; import io.quarkus.kubernetes.spi.KubernetesDeploymentTargetBuildItem; +import io.quarkus.kubernetes.spi.KubernetesEffectiveServiceAccountBuildItem; import io.quarkus.kubernetes.spi.KubernetesEnvBuildItem; import io.quarkus.kubernetes.spi.KubernetesHealthLivenessPathBuildItem; import io.quarkus.kubernetes.spi.KubernetesHealthReadinessPathBuildItem; @@ -67,7 +67,9 @@ import io.quarkus.kubernetes.spi.KubernetesRoleBindingBuildItem; import io.quarkus.kubernetes.spi.KubernetesRoleBuildItem; import io.quarkus.kubernetes.spi.KubernetesServiceAccountBuildItem; +import io.quarkus.kubernetes.spi.Targetable; +@SuppressWarnings("OptionalUsedAsFieldOrParameterType") public class VanillaKubernetesProcessor { @BuildStep @@ -98,9 +100,7 @@ public void checkVanillaKubernetes(ApplicationInfoBuildItem applicationInfo, Cap @BuildStep public void createAnnotations(KubernetesConfig config, BuildProducer annotations) { - config.annotations.forEach((k, v) -> { - annotations.produce(new KubernetesAnnotationBuildItem(k, v, KUBERNETES)); - }); + config.annotations.forEach((k, v) -> annotations.produce(new KubernetesAnnotationBuildItem(k, v, KUBERNETES))); } @BuildStep @@ -115,17 +115,14 @@ public void createLabels(KubernetesConfig config, BuildProducer namespace) { - config.getNamespace().ifPresent(n -> { - namespace.produce(new KubernetesNamespaceBuildItem(KUBERNETES, n)); - }); + config.getNamespace().ifPresent(n -> namespace.produce(new KubernetesNamespaceBuildItem(KUBERNETES, n))); } @BuildStep public List createConfigurators(KubernetesConfig config, List ports) { List result = new ArrayList<>(); - KubernetesCommonHelper.combinePorts(ports, config).values().forEach(value -> { - result.add(new ConfiguratorBuildItem(new AddPortToKubernetesConfig(value))); - }); + KubernetesCommonHelper.combinePorts(ports, config).values() + .forEach(value -> result.add(new ConfiguratorBuildItem(new AddPortToKubernetesConfig(value)))); if (config.ingress != null) { result.add(new ConfiguratorBuildItem(new ApplyKubernetesIngressConfigurator((config.ingress)))); } @@ -138,6 +135,16 @@ public List createConfigurators(KubernetesConfig config, return result; } + @BuildStep + public KubernetesEffectiveServiceAccountBuildItem computeEffectiveServiceAccounts(ApplicationInfoBuildItem applicationInfo, + KubernetesConfig config, List serviceAccountsFromExtensions, + BuildProducer decorators) { + final String name = ResourceNameUtil.getResourceName(config, applicationInfo); + return KubernetesCommonHelper.computeEffectiveServiceAccount(name, KUBERNETES, + config, serviceAccountsFromExtensions, + decorators); + } + @BuildStep public List createDecorators(ApplicationInfoBuildItem applicationInfo, OutputTargetBuildItem outputTarget, Capabilities capabilities, KubernetesConfig config, @@ -156,18 +163,17 @@ public List createDecorators(ApplicationInfoBuildItem applic Optional startupPath, List roles, List clusterRoles, - List serviceAccounts, + List serviceAccounts, List roleBindings, Optional customProjectRoot, List targets) { final List result = new ArrayList<>(); - if (!targets.stream().filter(KubernetesDeploymentTargetBuildItem::isEnabled) - .anyMatch(t -> KUBERNETES.equals(t.getName()))) { + if (targets.stream().filter(KubernetesDeploymentTargetBuildItem::isEnabled) + .noneMatch(t -> KUBERNETES.equals(t.getName()))) { return result; } final String name = ResourceNameUtil.getResourceName(config, applicationInfo); - Optional namespace = namespaces.stream() - .filter(n -> KUBERNETES.equals(n.getTarget())) + final var namespace = Targetable.filteredByTarget(namespaces, KUBERNETES, true) .findFirst(); Optional project = KubernetesCommonHelper.createProject(applicationInfo, customProjectRoot, outputTarget, @@ -231,22 +237,19 @@ public List createDecorators(ApplicationInfoBuildItem applic result.add(new DecoratorBuildItem(KUBERNETES, new ApplyReplicasToStatefulSetDecorator(name, config.getReplicas()))); } - image.ifPresent(i -> { - result.add(new DecoratorBuildItem(KUBERNETES, new ApplyContainerImageDecorator(name, i.getImage()))); - }); + image.ifPresent( + i -> result.add(new DecoratorBuildItem(KUBERNETES, new ApplyContainerImageDecorator(name, i.getImage())))); result.add(new DecoratorBuildItem(KUBERNETES, new ApplyImagePullPolicyDecorator(name, config.getImagePullPolicy()))); result.add(new DecoratorBuildItem(KUBERNETES, new AddSelectorToDeploymentDecorator(name))); - Stream.concat(config.convertToBuildItems().stream(), - envs.stream().filter(e -> e.getTarget() == null || KUBERNETES.equals(e.getTarget()))).forEach(e -> { - result.add(new DecoratorBuildItem(KUBERNETES, - new AddEnvVarDecorator(ApplicationContainerDecorator.ANY, name, - new EnvBuilder().withName(EnvConverter.convertName(e.getName())).withValue(e.getValue()) - .withSecret(e.getSecret()).withConfigmap(e.getConfigMap()).withField(e.getField()) - .withPrefix(e.getPrefix()) - .build()))); - }); + Stream.concat(config.convertToBuildItems().stream(), Targetable.filteredByTarget(envs, KUBERNETES)) + .forEach(e -> result.add(new DecoratorBuildItem(KUBERNETES, + new AddEnvVarDecorator(ApplicationContainerDecorator.ANY, name, + new EnvBuilder().withName(EnvConverter.convertName(e.getName())).withValue(e.getValue()) + .withSecret(e.getSecret()).withConfigmap(e.getConfigMap()).withField(e.getField()) + .withPrefix(e.getPrefix()) + .build())))); config.getContainerName().ifPresent(containerName -> result .add(new DecoratorBuildItem(KUBERNETES, new ChangeContainerNameDecorator(containerName)))); @@ -256,7 +259,7 @@ public List createDecorators(ApplicationInfoBuildItem applic if ((config.getServiceType() == ServiceType.NodePort)) { List> nodeConfigPorts = config.ports.entrySet().stream() .filter(e -> e.getValue().nodePort.isPresent()) - .collect(Collectors.toList()); + .toList(); if (!nodeConfigPorts.isEmpty()) { for (Map.Entry entry : nodeConfigPorts) { result.add(new DecoratorBuildItem(KUBERNETES, @@ -293,7 +296,7 @@ public List createDecorators(ApplicationInfoBuildItem applic // Handle init Containers and Jobs result.addAll(KubernetesCommonHelper.createInitContainerDecorators(KUBERNETES, name, initContainers, result)); - result.addAll(KubernetesCommonHelper.createInitJobDecorators(KUBERNETES, name, jobs, result)); + result.addAll(KubernetesCommonHelper.createInitJobDecorators(KUBERNETES, jobs, result)); // Do not bind the Management port to the Service resource unless it's explicitly used by the user. if (managementPortIsEnabled() @@ -330,7 +333,7 @@ void externalizeInitTasks( BuildProducer decorators) { final String name = ResourceNameUtil.getResourceName(config, applicationInfo); - if (config.externalizeInit) { + if (config.isExternalizeInit()) { InitTaskProcessor.process(KUBERNETES, name, image, initTasks, config.initTaskDefaults, config.initTasks, jobs, initContainers, env, roles, roleBindings, serviceAccount, decorators); }