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 81b3aecda5e40..591ae9947c4a3 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 @@ -304,17 +304,17 @@ private static Collection createRbacDecorators(String name, } // Add service account from extensions: use the one provided by the user always - String defaultServiceAccount = null; - String defaultServiceAccountNamespace = null; + Optional effectiveServiceAccount = config.getServiceAccount(); + String effectiveServiceAccountNamespace = null; for (KubernetesServiceAccountBuildItem sa : serviceAccountsFromExtensions) { - String saName = defaultIfEmpty(sa.getName(), name); + String saName = Optional.ofNullable(sa.getName()).orElse(name); result.add(new DecoratorBuildItem(target, new AddServiceAccountResourceDecorator(name, saName, sa.getNamespace(), sa.getLabels()))); - if (sa.isUseAsDefault() || defaultServiceAccount == null) { - defaultServiceAccount = saName; - defaultServiceAccountNamespace = sa.getNamespace(); + if (sa.isUseAsDefault() || effectiveServiceAccount.isEmpty()) { + effectiveServiceAccount = Optional.of(saName); + effectiveServiceAccountNamespace = sa.getNamespace(); } } @@ -325,9 +325,9 @@ private static Collection createRbacDecorators(String name, sa.getValue().namespace.orElse(null), sa.getValue().labels))); - if (sa.getValue().isUseAsDefault() || defaultServiceAccount == null) { - defaultServiceAccount = saName; - defaultServiceAccountNamespace = sa.getValue().namespace.orElse(null); + if (sa.getValue().isUseAsDefault() || effectiveServiceAccount.isEmpty()) { + effectiveServiceAccount = Optional.of(saName); + effectiveServiceAccountNamespace = sa.getValue().namespace.orElse(null); } } @@ -364,8 +364,8 @@ private static Collection createRbacDecorators(String name, if (roleBinding.subjects.isEmpty()) { requiresServiceAccount = true; subjects.add(new Subject(null, SERVICE_ACCOUNT, - defaultIfEmpty(defaultServiceAccount, config.getServiceAccount().orElse(name)), - defaultServiceAccountNamespace)); + effectiveServiceAccount.orElse(name), + effectiveServiceAccountNamespace)); } else { for (Map.Entry s : roleBinding.subjects.entrySet()) { String subjectName = s.getValue().name.orElse(s.getKey()); @@ -426,8 +426,8 @@ private static Collection createRbacDecorators(String name, Collections.emptyMap(), new RoleRef(defaultRoleName, defaultClusterWide), new Subject(null, SERVICE_ACCOUNT, - defaultIfEmpty(defaultServiceAccount, config.getServiceAccount().orElse(name)), - defaultServiceAccountNamespace)))); + effectiveServiceAccount.orElse(name), + effectiveServiceAccountNamespace)))); } else if (kubernetesClientRequiresRbacGeneration) { // the property `quarkus.kubernetes-client.generate-rbac` is enabled // and the kubernetes-client extension is present @@ -437,24 +437,25 @@ private static Collection createRbacDecorators(String name, Collections.emptyMap(), new RoleRef(DEFAULT_ROLE_NAME_VIEW, true), new Subject(null, SERVICE_ACCOUNT, - defaultIfEmpty(defaultServiceAccount, config.getServiceAccount().orElse(name)), - defaultServiceAccountNamespace)))); + effectiveServiceAccount.orElse(name), + effectiveServiceAccountNamespace)))); } } // generate service account if none is set, and it's required by other resources - if (defaultServiceAccount == null && requiresServiceAccount) { - // use the application name - defaultServiceAccount = config.getServiceAccount().orElse(name); + if (requiresServiceAccount) { // and generate the resource result.add(new DecoratorBuildItem(target, - new AddServiceAccountResourceDecorator(name, defaultServiceAccount, defaultServiceAccountNamespace, + new AddServiceAccountResourceDecorator(name, effectiveServiceAccount.orElse(name), + effectiveServiceAccountNamespace, Collections.emptyMap()))); } - // set service account in deployment resource - if (defaultServiceAccount != null) { - result.add(new DecoratorBuildItem(target, new ApplyServiceAccountNameDecorator(name, defaultServiceAccount))); + // 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)))); } return result; @@ -1048,12 +1049,4 @@ private static List toPolicyRulesList(Map .build()) .collect(Collectors.toList()); } - - private static String defaultIfEmpty(String str, String defaultStr) { - if (str == null || str.length() == 0) { - return defaultStr; - } - - return str; - } } diff --git a/integration-tests/kubernetes/quarkus-standard-way/src/test/java/io/quarkus/it/kubernetes/KubernetesWithServiceAccountTest.java b/integration-tests/kubernetes/quarkus-standard-way/src/test/java/io/quarkus/it/kubernetes/KubernetesWithServiceAccountTest.java new file mode 100644 index 0000000000000..0935145c0c275 --- /dev/null +++ b/integration-tests/kubernetes/quarkus-standard-way/src/test/java/io/quarkus/it/kubernetes/KubernetesWithServiceAccountTest.java @@ -0,0 +1,64 @@ +package io.quarkus.it.kubernetes; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.List; +import java.util.Optional; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.fabric8.kubernetes.api.model.HasMetadata; +import io.fabric8.kubernetes.api.model.ServiceAccount; +import io.fabric8.kubernetes.api.model.apps.Deployment; +import io.quarkus.test.ProdBuildResults; +import io.quarkus.test.ProdModeTestResults; +import io.quarkus.test.QuarkusProdModeTest; + +public class KubernetesWithServiceAccountTest { + + private static final String APP_NAME = "kubernetes-with-service-account"; + private static final String SERVICE_ACCOUNT = "my-service-account"; + + @RegisterExtension + static final QuarkusProdModeTest config = new QuarkusProdModeTest() + .withApplicationRoot((jar) -> jar.addClasses(GreetingResource.class)) + .setApplicationName(APP_NAME) + .setApplicationVersion("0.1-SNAPSHOT") + .overrideConfigKey("quarkus.kubernetes.service-account", SERVICE_ACCOUNT); + + @ProdBuildResults + private ProdModeTestResults prodModeTestResults; + + @Test + public void assertGeneratedResources() throws IOException { + final Path kubernetesDir = prodModeTestResults.getBuildDir().resolve("kubernetes"); + List kubernetesList = DeserializationUtil + .deserializeAsList(kubernetesDir.resolve("kubernetes.yml")); + + Deployment deployment = getDeploymentByName(kubernetesList, APP_NAME).get(); + assertThat(deployment.getSpec().getTemplate().getSpec().getServiceAccountName()).isEqualTo(SERVICE_ACCOUNT); + + Optional serviceAccount = getServiceAccountByName(kubernetesList, SERVICE_ACCOUNT); + assertThat(serviceAccount.isPresent()).isFalse(); + } + + private Optional getDeploymentByName(List kubernetesList, String name) { + return getResourceByName(kubernetesList, Deployment.class, name); + } + + private Optional getServiceAccountByName(List kubernetesList, String saName) { + return getResourceByName(kubernetesList, ServiceAccount.class, saName); + } + + private Optional getResourceByName(List kubernetesList, Class clazz, + String name) { + return kubernetesList.stream() + .filter(r -> r.getMetadata().getName().equals(name)) + .filter(clazz::isInstance) + .map(clazz::cast) + .findFirst(); + } +}