Skip to content

Commit

Permalink
Merge pull request #36115 from iocanel/fix-36089-init-task-serviceacc…
Browse files Browse the repository at this point in the history
…ount

User provided ServiceAccount always takes precedence over extensions
  • Loading branch information
iocanel authored Sep 25, 2023
2 parents f7da1d7 + 5c92840 commit 383c126
Show file tree
Hide file tree
Showing 4 changed files with 159 additions and 81 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -354,8 +354,7 @@ private static Collection<DecoratorBuildItem> createRbacDecorators(String name,
}
}

// Add service account from extensions: use the one provided by the user always
Optional<String> effectiveServiceAccount = config.getServiceAccount();
Optional<String> effectiveServiceAccount = Optional.empty();
String effectiveServiceAccountNamespace = null;
for (KubernetesServiceAccountBuildItem sa : serviceAccountsFromExtensions) {
String saName = Optional.ofNullable(sa.getName()).orElse(name);
Expand All @@ -382,6 +381,12 @@ private static Collection<DecoratorBuildItem> createRbacDecorators(String name,
}
}

// The user provided service account should always take precedence
if (config.getServiceAccount().isPresent()) {
effectiveServiceAccount = config.getServiceAccount();
effectiveServiceAccountNamespace = null;
}

// Prepare default configuration
String defaultRoleName = null;
boolean defaultClusterWide = false;
Expand Down Expand Up @@ -699,6 +704,13 @@ public static List<DecoratorBuildItem> createInitJobDecorators(String target, St
.map(Optional::get)
.collect(Collectors.toList());

List<ApplyServiceAccountNameDecorator> 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());

items.stream().filter(item -> item.getTarget() == null || item.getTarget().equals(target)).forEach(item -> {

for (final AddImagePullSecretDecorator delegate : imagePullSecretDecorators) {
Expand All @@ -710,6 +722,15 @@ public void andThenVisit(PodSpecBuilder builder, ObjectMeta meta) {
}));
}

for (final ApplyServiceAccountNameDecorator delegate : serviceAccountDecorators) {
result.add(new DecoratorBuildItem(target, new NamedResourceDecorator<PodSpecBuilder>("Job", item.getName()) {
@Override
public void andThenVisit(PodSpecBuilder builder, ObjectMeta meta) {
delegate.andThenVisit(builder, meta);
}
}));
}

result.add(new DecoratorBuildItem(target, new NamedResourceDecorator<ContainerBuilder>("Job", item.getName()) {
@Override
public void andThenVisit(ContainerBuilder builder, ObjectMeta meta) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
package io.quarkus.it.kubernetes;

import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertTrue;

import java.io.IOException;
import java.nio.file.Path;
import java.util.List;
import java.util.Optional;

import io.fabric8.kubernetes.api.model.HasMetadata;
import io.fabric8.kubernetes.api.model.apps.Deployment;
import io.fabric8.kubernetes.api.model.batch.v1.Job;
import io.fabric8.kubernetes.api.model.rbac.RoleBinding;

public class KubernetesWithFlywayInitBase {

public void assertGeneratedResources(Path kubernetesDir, String name, String imagePullSecret, String serviceAccount)
throws IOException {
assertThat(kubernetesDir)
.isDirectoryContaining(p -> p.getFileName().endsWith("kubernetes.json"))
.isDirectoryContaining(p -> p.getFileName().endsWith("kubernetes.yml"));
List<HasMetadata> kubernetesList = DeserializationUtil.deserializeAsList(kubernetesDir.resolve("kubernetes.yml"));

Optional<Deployment> deployment = kubernetesList.stream()
.filter(d -> "Deployment".equals(d.getKind())
&& name.equals(d.getMetadata().getName()))
.map(d -> (Deployment) d).findAny();

assertTrue(deployment.isPresent());
assertThat(deployment).satisfies(j -> j.isPresent());
assertThat(deployment.get()).satisfies(d -> {
assertThat(d.getMetadata()).satisfies(m -> {
assertThat(m.getName()).isEqualTo(name);
});

assertThat(d.getSpec()).satisfies(deploymentSpec -> {
assertThat(deploymentSpec.getTemplate()).satisfies(t -> {
assertThat(t.getSpec()).satisfies(podSpec -> {
assertThat(podSpec.getImagePullSecrets()).singleElement()
.satisfies(s -> assertThat(s.getName()).isEqualTo(imagePullSecret));
assertThat(podSpec.getServiceAccountName()).isEqualTo(serviceAccount);
assertThat(podSpec.getInitContainers()).singleElement().satisfies(container -> {
assertThat(container.getName()).isEqualTo("init");
assertThat(container.getImage()).isEqualTo("groundnuty/k8s-wait-for:no-root-v1.7");
});

});
});
});
});

Optional<Job> job = kubernetesList.stream()
.filter(j -> "Job".equals(j.getKind()) && (name + "-flyway-init").equals(j.getMetadata().getName()))
.map(j -> (Job) j)
.findAny();
assertTrue(job.isPresent());

assertThat(job.get()).satisfies(j -> {
assertThat(j.getSpec()).satisfies(jobSpec -> {
assertThat(jobSpec.getCompletionMode()).isEqualTo("NonIndexed");
assertThat(jobSpec.getTemplate()).satisfies(t -> {
assertThat(t.getSpec()).satisfies(podSpec -> {
assertThat(podSpec.getImagePullSecrets()).singleElement()
.satisfies(s -> assertThat(s.getName()).isEqualTo(imagePullSecret));
assertThat(podSpec.getServiceAccountName()).isEqualTo(serviceAccount);
assertThat(podSpec.getRestartPolicy()).isEqualTo("OnFailure");
assertThat(podSpec.getContainers()).singleElement().satisfies(container -> {
assertThat(container.getName()).isEqualTo(name + "-flyway-init");
assertThat(container.getEnv()).filteredOn(env -> "QUARKUS_FLYWAY_ENABLED".equals(env.getName()))
.singleElement().satisfies(env -> {
assertThat(env.getValue()).isEqualTo("true");
});
assertThat(container.getEnv())
.filteredOn(env -> "QUARKUS_INIT_AND_EXIT".equals(env.getName())).singleElement()
.satisfies(env -> {
assertThat(env.getValue()).isEqualTo("true");
});
});
});
});
});
});

Optional<RoleBinding> roleBinding = kubernetesList.stream().filter(
r -> r instanceof RoleBinding && (name + "-view-jobs").equals(r.getMetadata().getName()))
.map(r -> (RoleBinding) r).findFirst();
assertTrue(roleBinding.isPresent());
}
}
Original file line number Diff line number Diff line change
@@ -1,30 +1,21 @@
package io.quarkus.it.kubernetes;

import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertTrue;

import java.io.IOException;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;

import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.spec.JavaArchive;
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.apps.Deployment;
import io.fabric8.kubernetes.api.model.batch.v1.Job;
import io.fabric8.kubernetes.api.model.rbac.RoleBinding;
import io.quarkus.bootstrap.model.AppArtifact;
import io.quarkus.builder.Version;
import io.quarkus.test.ProdBuildResults;
import io.quarkus.test.ProdModeTestResults;
import io.quarkus.test.QuarkusProdModeTest;

public class KubernetesWithFlywayInitTest {
public class KubernetesWithFlywayInitTest extends KubernetesWithFlywayInitBase {

private static final String NAME = "kubernetes-with-flyway";
private static final String IMAGE_PULL_SECRET = "my-pull-secret";
Expand All @@ -46,74 +37,6 @@ public class KubernetesWithFlywayInitTest {
@Test
public void assertGeneratedResources() throws IOException {
final Path kubernetesDir = prodModeTestResults.getBuildDir().resolve("kubernetes");
assertThat(kubernetesDir)
.isDirectoryContaining(p -> p.getFileName().endsWith("kubernetes.json"))
.isDirectoryContaining(p -> p.getFileName().endsWith("kubernetes.yml"));
List<HasMetadata> kubernetesList = DeserializationUtil
.deserializeAsList(kubernetesDir.resolve("kubernetes.yml"));

Optional<Deployment> deployment = kubernetesList.stream()
.filter(d -> "Deployment".equals(d.getKind())
&& NAME.equals(d.getMetadata().getName()))
.map(d -> (Deployment) d).findAny();

assertTrue(deployment.isPresent());
assertThat(deployment).satisfies(j -> j.isPresent());
assertThat(deployment.get()).satisfies(d -> {
assertThat(d.getMetadata()).satisfies(m -> {
assertThat(m.getName()).isEqualTo(NAME);
});

assertThat(d.getSpec()).satisfies(deploymentSpec -> {
assertThat(deploymentSpec.getTemplate()).satisfies(t -> {
assertThat(t.getSpec()).satisfies(podSpec -> {
assertThat(podSpec.getImagePullSecrets()).singleElement()
.satisfies(s -> assertThat(s.getName()).isEqualTo(IMAGE_PULL_SECRET));
assertThat(podSpec.getServiceAccountName()).isEqualTo(NAME);
assertThat(podSpec.getInitContainers()).singleElement().satisfies(container -> {
assertThat(container.getName()).isEqualTo("init");
assertThat(container.getImage()).isEqualTo("groundnuty/k8s-wait-for:no-root-v1.7");
});

});
});
});
});

Optional<Job> job = kubernetesList.stream()
.filter(j -> "Job".equals(j.getKind()) && (NAME + "-flyway-init").equals(j.getMetadata().getName()))
.map(j -> (Job) j)
.findAny();
assertTrue(job.isPresent());

assertThat(job.get()).satisfies(j -> {
assertThat(j.getSpec()).satisfies(jobSpec -> {
assertThat(jobSpec.getCompletionMode()).isEqualTo("NonIndexed");
assertThat(jobSpec.getTemplate()).satisfies(t -> {
assertThat(t.getSpec()).satisfies(podSpec -> {
assertThat(podSpec.getImagePullSecrets()).singleElement()
.satisfies(s -> assertThat(s.getName()).isEqualTo(IMAGE_PULL_SECRET));
assertThat(podSpec.getRestartPolicy()).isEqualTo("OnFailure");
assertThat(podSpec.getContainers()).singleElement().satisfies(container -> {
assertThat(container.getName()).isEqualTo(NAME + "-flyway-init");
assertThat(container.getEnv()).filteredOn(env -> "QUARKUS_FLYWAY_ENABLED".equals(env.getName()))
.singleElement().satisfies(env -> {
assertThat(env.getValue()).isEqualTo("true");
});
assertThat(container.getEnv())
.filteredOn(env -> "QUARKUS_INIT_AND_EXIT".equals(env.getName())).singleElement()
.satisfies(env -> {
assertThat(env.getValue()).isEqualTo("true");
});
});
});
});
});
});

Optional<RoleBinding> roleBinding = kubernetesList.stream().filter(
r -> r instanceof RoleBinding && (NAME + "-view-jobs").equals(r.getMetadata().getName()))
.map(r -> (RoleBinding) r).findFirst();
assertTrue(roleBinding.isPresent());
assertGeneratedResources(kubernetesDir, NAME, IMAGE_PULL_SECRET, NAME);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package io.quarkus.it.kubernetes;

import java.io.IOException;
import java.nio.file.Path;
import java.util.Arrays;

import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.spec.JavaArchive;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;

import io.quarkus.bootstrap.model.AppArtifact;
import io.quarkus.builder.Version;
import io.quarkus.test.ProdBuildResults;
import io.quarkus.test.ProdModeTestResults;
import io.quarkus.test.QuarkusProdModeTest;

public class KubernetesWithFlywayIntAndCustomServiceAccountTest extends KubernetesWithFlywayInitBase {

private static final String NAME = "kubernetes-with-flyway";
private static final String IMAGE_PULL_SECRET = "my-pull-secret";
private static final String SERVICE_ACCOUNT = "my-service-account";

@RegisterExtension
static final QuarkusProdModeTest config = new QuarkusProdModeTest()
.setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class).addClasses(GreetingResource.class))
.setApplicationName(NAME)
.setApplicationVersion("0.1-SNAPSHOT")
.setLogFileName("k8s.log")
.overrideConfigKey("quarkus.kubernetes.image-pull-secrets", IMAGE_PULL_SECRET)
.overrideConfigKey("quarkus.kubernetes.service-account", SERVICE_ACCOUNT)
.setForcedDependencies(Arrays.asList(
new AppArtifact("io.quarkus", "quarkus-kubernetes", Version.getVersion()),
new AppArtifact("io.quarkus", "quarkus-flyway", Version.getVersion())));

@ProdBuildResults
private ProdModeTestResults prodModeTestResults;

@Test
public void assertGeneratedResources() throws IOException {
final Path kubernetesDir = prodModeTestResults.getBuildDir().resolve("kubernetes");
assertGeneratedResources(kubernetesDir, NAME, IMAGE_PULL_SECRET, SERVICE_ACCOUNT);
}
}

0 comments on commit 383c126

Please sign in to comment.