Skip to content

Commit

Permalink
Merge pull request #32934 from Sgitario/32933
Browse files Browse the repository at this point in the history
Fix setting service account in Kubernetes/Openshift extensions
  • Loading branch information
Sgitario authored Apr 27, 2023
2 parents 0baea80 + bd2b94b commit 2cf9355
Show file tree
Hide file tree
Showing 2 changed files with 87 additions and 30 deletions.
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();
}
}

0 comments on commit 2cf9355

Please sign in to comment.