Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix setting service account in Kubernetes/Openshift extensions #32934

Merged
merged 1 commit into from
Apr 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -304,17 +304,17 @@ private static Collection<DecoratorBuildItem> createRbacDecorators(String name,
}

// Add service account from extensions: use the one provided by the user always
String defaultServiceAccount = null;
String defaultServiceAccountNamespace = null;
Optional<String> 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();
}
}

Expand All @@ -325,9 +325,9 @@ private static Collection<DecoratorBuildItem> 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);
}
}

Expand Down Expand Up @@ -364,8 +364,8 @@ private static Collection<DecoratorBuildItem> 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<String, SubjectConfig> s : roleBinding.subjects.entrySet()) {
String subjectName = s.getValue().name.orElse(s.getKey());
Expand Down Expand Up @@ -426,8 +426,8 @@ private static Collection<DecoratorBuildItem> 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
Expand All @@ -437,24 +437,25 @@ private static Collection<DecoratorBuildItem> 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;
Expand Down Expand Up @@ -1048,12 +1049,4 @@ private static List<PolicyRule> toPolicyRulesList(Map<String, PolicyRuleConfig>
.build())
.collect(Collectors.toList());
}

private static String defaultIfEmpty(String str, String defaultStr) {
if (str == null || str.length() == 0) {
return defaultStr;
}

return str;
}
}
Original file line number Diff line number Diff line change
@@ -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<HasMetadata> 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> serviceAccount = getServiceAccountByName(kubernetesList, SERVICE_ACCOUNT);
assertThat(serviceAccount.isPresent()).isFalse();
}

private Optional<Deployment> getDeploymentByName(List<HasMetadata> kubernetesList, String name) {
return getResourceByName(kubernetesList, Deployment.class, name);
}

private Optional<ServiceAccount> getServiceAccountByName(List<HasMetadata> kubernetesList, String saName) {
return getResourceByName(kubernetesList, ServiceAccount.class, saName);
}

private <T extends HasMetadata> Optional<T> getResourceByName(List<HasMetadata> kubernetesList, Class<T> clazz,
String name) {
return kubernetesList.stream()
.filter(r -> r.getMetadata().getName().equals(name))
.filter(clazz::isInstance)
.map(clazz::cast)
.findFirst();
}
}