Skip to content

Commit 383c126

Browse files
authored
Merge pull request quarkusio#36115 from iocanel/fix-36089-init-task-serviceaccount
User provided ServiceAccount always takes precedence over extensions
2 parents f7da1d7 + 5c92840 commit 383c126

File tree

4 files changed

+159
-81
lines changed

4 files changed

+159
-81
lines changed

extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/KubernetesCommonHelper.java

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -354,8 +354,7 @@ private static Collection<DecoratorBuildItem> createRbacDecorators(String name,
354354
}
355355
}
356356

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

384+
// The user provided service account should always take precedence
385+
if (config.getServiceAccount().isPresent()) {
386+
effectiveServiceAccount = config.getServiceAccount();
387+
effectiveServiceAccountNamespace = null;
388+
}
389+
385390
// Prepare default configuration
386391
String defaultRoleName = null;
387392
boolean defaultClusterWide = false;
@@ -699,6 +704,13 @@ public static List<DecoratorBuildItem> createInitJobDecorators(String target, St
699704
.map(Optional::get)
700705
.collect(Collectors.toList());
701706

707+
List<ApplyServiceAccountNameDecorator> serviceAccountDecorators = decorators.stream()
708+
.filter(d -> d.getGroup() == null || d.getGroup().equals(target))
709+
.map(d -> d.getDecorator(ApplyServiceAccountNameDecorator.class))
710+
.filter(Optional::isPresent)
711+
.map(Optional::get)
712+
.collect(Collectors.toList());
713+
702714
items.stream().filter(item -> item.getTarget() == null || item.getTarget().equals(target)).forEach(item -> {
703715

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

725+
for (final ApplyServiceAccountNameDecorator delegate : serviceAccountDecorators) {
726+
result.add(new DecoratorBuildItem(target, new NamedResourceDecorator<PodSpecBuilder>("Job", item.getName()) {
727+
@Override
728+
public void andThenVisit(PodSpecBuilder builder, ObjectMeta meta) {
729+
delegate.andThenVisit(builder, meta);
730+
}
731+
}));
732+
}
733+
713734
result.add(new DecoratorBuildItem(target, new NamedResourceDecorator<ContainerBuilder>("Job", item.getName()) {
714735
@Override
715736
public void andThenVisit(ContainerBuilder builder, ObjectMeta meta) {
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
package io.quarkus.it.kubernetes;
2+
3+
import static org.assertj.core.api.Assertions.assertThat;
4+
import static org.junit.jupiter.api.Assertions.assertTrue;
5+
6+
import java.io.IOException;
7+
import java.nio.file.Path;
8+
import java.util.List;
9+
import java.util.Optional;
10+
11+
import io.fabric8.kubernetes.api.model.HasMetadata;
12+
import io.fabric8.kubernetes.api.model.apps.Deployment;
13+
import io.fabric8.kubernetes.api.model.batch.v1.Job;
14+
import io.fabric8.kubernetes.api.model.rbac.RoleBinding;
15+
16+
public class KubernetesWithFlywayInitBase {
17+
18+
public void assertGeneratedResources(Path kubernetesDir, String name, String imagePullSecret, String serviceAccount)
19+
throws IOException {
20+
assertThat(kubernetesDir)
21+
.isDirectoryContaining(p -> p.getFileName().endsWith("kubernetes.json"))
22+
.isDirectoryContaining(p -> p.getFileName().endsWith("kubernetes.yml"));
23+
List<HasMetadata> kubernetesList = DeserializationUtil.deserializeAsList(kubernetesDir.resolve("kubernetes.yml"));
24+
25+
Optional<Deployment> deployment = kubernetesList.stream()
26+
.filter(d -> "Deployment".equals(d.getKind())
27+
&& name.equals(d.getMetadata().getName()))
28+
.map(d -> (Deployment) d).findAny();
29+
30+
assertTrue(deployment.isPresent());
31+
assertThat(deployment).satisfies(j -> j.isPresent());
32+
assertThat(deployment.get()).satisfies(d -> {
33+
assertThat(d.getMetadata()).satisfies(m -> {
34+
assertThat(m.getName()).isEqualTo(name);
35+
});
36+
37+
assertThat(d.getSpec()).satisfies(deploymentSpec -> {
38+
assertThat(deploymentSpec.getTemplate()).satisfies(t -> {
39+
assertThat(t.getSpec()).satisfies(podSpec -> {
40+
assertThat(podSpec.getImagePullSecrets()).singleElement()
41+
.satisfies(s -> assertThat(s.getName()).isEqualTo(imagePullSecret));
42+
assertThat(podSpec.getServiceAccountName()).isEqualTo(serviceAccount);
43+
assertThat(podSpec.getInitContainers()).singleElement().satisfies(container -> {
44+
assertThat(container.getName()).isEqualTo("init");
45+
assertThat(container.getImage()).isEqualTo("groundnuty/k8s-wait-for:no-root-v1.7");
46+
});
47+
48+
});
49+
});
50+
});
51+
});
52+
53+
Optional<Job> job = kubernetesList.stream()
54+
.filter(j -> "Job".equals(j.getKind()) && (name + "-flyway-init").equals(j.getMetadata().getName()))
55+
.map(j -> (Job) j)
56+
.findAny();
57+
assertTrue(job.isPresent());
58+
59+
assertThat(job.get()).satisfies(j -> {
60+
assertThat(j.getSpec()).satisfies(jobSpec -> {
61+
assertThat(jobSpec.getCompletionMode()).isEqualTo("NonIndexed");
62+
assertThat(jobSpec.getTemplate()).satisfies(t -> {
63+
assertThat(t.getSpec()).satisfies(podSpec -> {
64+
assertThat(podSpec.getImagePullSecrets()).singleElement()
65+
.satisfies(s -> assertThat(s.getName()).isEqualTo(imagePullSecret));
66+
assertThat(podSpec.getServiceAccountName()).isEqualTo(serviceAccount);
67+
assertThat(podSpec.getRestartPolicy()).isEqualTo("OnFailure");
68+
assertThat(podSpec.getContainers()).singleElement().satisfies(container -> {
69+
assertThat(container.getName()).isEqualTo(name + "-flyway-init");
70+
assertThat(container.getEnv()).filteredOn(env -> "QUARKUS_FLYWAY_ENABLED".equals(env.getName()))
71+
.singleElement().satisfies(env -> {
72+
assertThat(env.getValue()).isEqualTo("true");
73+
});
74+
assertThat(container.getEnv())
75+
.filteredOn(env -> "QUARKUS_INIT_AND_EXIT".equals(env.getName())).singleElement()
76+
.satisfies(env -> {
77+
assertThat(env.getValue()).isEqualTo("true");
78+
});
79+
});
80+
});
81+
});
82+
});
83+
});
84+
85+
Optional<RoleBinding> roleBinding = kubernetesList.stream().filter(
86+
r -> r instanceof RoleBinding && (name + "-view-jobs").equals(r.getMetadata().getName()))
87+
.map(r -> (RoleBinding) r).findFirst();
88+
assertTrue(roleBinding.isPresent());
89+
}
90+
}
Lines changed: 2 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,21 @@
11
package io.quarkus.it.kubernetes;
22

3-
import static org.assertj.core.api.Assertions.assertThat;
4-
import static org.junit.jupiter.api.Assertions.assertTrue;
5-
63
import java.io.IOException;
74
import java.nio.file.Path;
85
import java.util.Arrays;
9-
import java.util.List;
10-
import java.util.Optional;
116

127
import org.jboss.shrinkwrap.api.ShrinkWrap;
138
import org.jboss.shrinkwrap.api.spec.JavaArchive;
149
import org.junit.jupiter.api.Test;
1510
import org.junit.jupiter.api.extension.RegisterExtension;
1611

17-
import io.fabric8.kubernetes.api.model.HasMetadata;
18-
import io.fabric8.kubernetes.api.model.apps.Deployment;
19-
import io.fabric8.kubernetes.api.model.batch.v1.Job;
20-
import io.fabric8.kubernetes.api.model.rbac.RoleBinding;
2112
import io.quarkus.bootstrap.model.AppArtifact;
2213
import io.quarkus.builder.Version;
2314
import io.quarkus.test.ProdBuildResults;
2415
import io.quarkus.test.ProdModeTestResults;
2516
import io.quarkus.test.QuarkusProdModeTest;
2617

27-
public class KubernetesWithFlywayInitTest {
18+
public class KubernetesWithFlywayInitTest extends KubernetesWithFlywayInitBase {
2819

2920
private static final String NAME = "kubernetes-with-flyway";
3021
private static final String IMAGE_PULL_SECRET = "my-pull-secret";
@@ -46,74 +37,6 @@ public class KubernetesWithFlywayInitTest {
4637
@Test
4738
public void assertGeneratedResources() throws IOException {
4839
final Path kubernetesDir = prodModeTestResults.getBuildDir().resolve("kubernetes");
49-
assertThat(kubernetesDir)
50-
.isDirectoryContaining(p -> p.getFileName().endsWith("kubernetes.json"))
51-
.isDirectoryContaining(p -> p.getFileName().endsWith("kubernetes.yml"));
52-
List<HasMetadata> kubernetesList = DeserializationUtil
53-
.deserializeAsList(kubernetesDir.resolve("kubernetes.yml"));
54-
55-
Optional<Deployment> deployment = kubernetesList.stream()
56-
.filter(d -> "Deployment".equals(d.getKind())
57-
&& NAME.equals(d.getMetadata().getName()))
58-
.map(d -> (Deployment) d).findAny();
59-
60-
assertTrue(deployment.isPresent());
61-
assertThat(deployment).satisfies(j -> j.isPresent());
62-
assertThat(deployment.get()).satisfies(d -> {
63-
assertThat(d.getMetadata()).satisfies(m -> {
64-
assertThat(m.getName()).isEqualTo(NAME);
65-
});
66-
67-
assertThat(d.getSpec()).satisfies(deploymentSpec -> {
68-
assertThat(deploymentSpec.getTemplate()).satisfies(t -> {
69-
assertThat(t.getSpec()).satisfies(podSpec -> {
70-
assertThat(podSpec.getImagePullSecrets()).singleElement()
71-
.satisfies(s -> assertThat(s.getName()).isEqualTo(IMAGE_PULL_SECRET));
72-
assertThat(podSpec.getServiceAccountName()).isEqualTo(NAME);
73-
assertThat(podSpec.getInitContainers()).singleElement().satisfies(container -> {
74-
assertThat(container.getName()).isEqualTo("init");
75-
assertThat(container.getImage()).isEqualTo("groundnuty/k8s-wait-for:no-root-v1.7");
76-
});
77-
78-
});
79-
});
80-
});
81-
});
82-
83-
Optional<Job> job = kubernetesList.stream()
84-
.filter(j -> "Job".equals(j.getKind()) && (NAME + "-flyway-init").equals(j.getMetadata().getName()))
85-
.map(j -> (Job) j)
86-
.findAny();
87-
assertTrue(job.isPresent());
88-
89-
assertThat(job.get()).satisfies(j -> {
90-
assertThat(j.getSpec()).satisfies(jobSpec -> {
91-
assertThat(jobSpec.getCompletionMode()).isEqualTo("NonIndexed");
92-
assertThat(jobSpec.getTemplate()).satisfies(t -> {
93-
assertThat(t.getSpec()).satisfies(podSpec -> {
94-
assertThat(podSpec.getImagePullSecrets()).singleElement()
95-
.satisfies(s -> assertThat(s.getName()).isEqualTo(IMAGE_PULL_SECRET));
96-
assertThat(podSpec.getRestartPolicy()).isEqualTo("OnFailure");
97-
assertThat(podSpec.getContainers()).singleElement().satisfies(container -> {
98-
assertThat(container.getName()).isEqualTo(NAME + "-flyway-init");
99-
assertThat(container.getEnv()).filteredOn(env -> "QUARKUS_FLYWAY_ENABLED".equals(env.getName()))
100-
.singleElement().satisfies(env -> {
101-
assertThat(env.getValue()).isEqualTo("true");
102-
});
103-
assertThat(container.getEnv())
104-
.filteredOn(env -> "QUARKUS_INIT_AND_EXIT".equals(env.getName())).singleElement()
105-
.satisfies(env -> {
106-
assertThat(env.getValue()).isEqualTo("true");
107-
});
108-
});
109-
});
110-
});
111-
});
112-
});
113-
114-
Optional<RoleBinding> roleBinding = kubernetesList.stream().filter(
115-
r -> r instanceof RoleBinding && (NAME + "-view-jobs").equals(r.getMetadata().getName()))
116-
.map(r -> (RoleBinding) r).findFirst();
117-
assertTrue(roleBinding.isPresent());
40+
assertGeneratedResources(kubernetesDir, NAME, IMAGE_PULL_SECRET, NAME);
11841
}
11942
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package io.quarkus.it.kubernetes;
2+
3+
import java.io.IOException;
4+
import java.nio.file.Path;
5+
import java.util.Arrays;
6+
7+
import org.jboss.shrinkwrap.api.ShrinkWrap;
8+
import org.jboss.shrinkwrap.api.spec.JavaArchive;
9+
import org.junit.jupiter.api.Test;
10+
import org.junit.jupiter.api.extension.RegisterExtension;
11+
12+
import io.quarkus.bootstrap.model.AppArtifact;
13+
import io.quarkus.builder.Version;
14+
import io.quarkus.test.ProdBuildResults;
15+
import io.quarkus.test.ProdModeTestResults;
16+
import io.quarkus.test.QuarkusProdModeTest;
17+
18+
public class KubernetesWithFlywayIntAndCustomServiceAccountTest extends KubernetesWithFlywayInitBase {
19+
20+
private static final String NAME = "kubernetes-with-flyway";
21+
private static final String IMAGE_PULL_SECRET = "my-pull-secret";
22+
private static final String SERVICE_ACCOUNT = "my-service-account";
23+
24+
@RegisterExtension
25+
static final QuarkusProdModeTest config = new QuarkusProdModeTest()
26+
.setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class).addClasses(GreetingResource.class))
27+
.setApplicationName(NAME)
28+
.setApplicationVersion("0.1-SNAPSHOT")
29+
.setLogFileName("k8s.log")
30+
.overrideConfigKey("quarkus.kubernetes.image-pull-secrets", IMAGE_PULL_SECRET)
31+
.overrideConfigKey("quarkus.kubernetes.service-account", SERVICE_ACCOUNT)
32+
.setForcedDependencies(Arrays.asList(
33+
new AppArtifact("io.quarkus", "quarkus-kubernetes", Version.getVersion()),
34+
new AppArtifact("io.quarkus", "quarkus-flyway", Version.getVersion())));
35+
36+
@ProdBuildResults
37+
private ProdModeTestResults prodModeTestResults;
38+
39+
@Test
40+
public void assertGeneratedResources() throws IOException {
41+
final Path kubernetesDir = prodModeTestResults.getBuildDir().resolve("kubernetes");
42+
assertGeneratedResources(kubernetesDir, NAME, IMAGE_PULL_SECRET, SERVICE_ACCOUNT);
43+
}
44+
}

0 commit comments

Comments
 (0)