Skip to content

Commit

Permalink
Allow overriding generated init-container of extensions from user config
Browse files Browse the repository at this point in the history
  • Loading branch information
Sgitario committed May 16, 2023
1 parent 6d7bc7f commit a3c49bf
Show file tree
Hide file tree
Showing 5 changed files with 190 additions and 6 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package io.quarkus.kubernetes.deployment;

import io.dekorate.kubernetes.config.Container;
import io.dekorate.kubernetes.decorator.NamedResourceDecorator;
import io.fabric8.kubernetes.api.model.ContainerBuilder;
import io.fabric8.kubernetes.api.model.ObjectMeta;
import io.fabric8.kubernetes.api.model.PodSpecBuilder;

public class ApplyInitContainerDecorator extends NamedResourceDecorator<PodSpecBuilder> {

private final Container container;

public ApplyInitContainerDecorator(String deployment, Container container) {
super(deployment);
this.container = container;
}

@Override
public void andThenVisit(PodSpecBuilder podSpec, ObjectMeta resourceMeta) {
var resource = ContainerAdapter.adapt(container);
if (podSpec.hasMatchingInitContainer(this::existsContainerByName)) {
update(podSpec, resource);
} else {
add(podSpec, resource);
}
}

private void add(PodSpecBuilder podSpec, io.fabric8.kubernetes.api.model.Container resource) {
podSpec.addToInitContainers(resource);
}

private void update(PodSpecBuilder podSpec, io.fabric8.kubernetes.api.model.Container resource) {
var matching = podSpec.editMatchingInitContainer(this::existsContainerByName);

if (resource.getImage() != null) {
matching.withImage(resource.getImage());
}

if (resource.getWorkingDir() != null) {
matching.withWorkingDir(resource.getWorkingDir());
}

if (resource.getCommand() != null && !resource.getCommand().isEmpty()) {
matching.withCommand(resource.getCommand());
}

if (resource.getArgs() != null && !resource.getArgs().isEmpty()) {
matching.withArgs(resource.getArgs());
}

if (resource.getReadinessProbe() != null) {
matching.withReadinessProbe(resource.getReadinessProbe());
}

if (resource.getLivenessProbe() != null) {
matching.withLivenessProbe(resource.getLivenessProbe());
}

matching.addAllToEnv(resource.getEnv());
if (resource.getPorts() != null && !resource.getPorts().isEmpty()) {
matching.withPorts(resource.getPorts());
}

matching.endInitContainer();
}

private boolean existsContainerByName(ContainerBuilder containerBuilder) {
return containerBuilder.getName().equals(container.getName());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public Class<? extends Decorator>[] after() {
ApplyImagePullPolicyDecorator.class, ApplyWorkingDirDecorator.class, ApplyCommandDecorator.class,
ApplyArgsDecorator.class, ApplyServiceAccountNamedDecorator.class, AddReadinessProbeDecorator.class,
AddLivenessProbeDecorator.class, ApplyApplicationContainerDecorator.class, AddSidecarDecorator.class,
AddInitContainerDecorator.class };
ApplyInitContainerDecorator.class };
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@

import io.dekorate.kubernetes.config.Annotation;
import io.dekorate.kubernetes.config.ConfigMapVolumeBuilder;
import io.dekorate.kubernetes.config.Container;
import io.dekorate.kubernetes.config.EnvBuilder;
import io.dekorate.kubernetes.config.MountBuilder;
import io.dekorate.kubernetes.config.Port;
Expand All @@ -40,7 +41,6 @@
import io.dekorate.kubernetes.decorator.AddEnvVarDecorator;
import io.dekorate.kubernetes.decorator.AddHostAliasesDecorator;
import io.dekorate.kubernetes.decorator.AddImagePullSecretDecorator;
import io.dekorate.kubernetes.decorator.AddInitContainerDecorator;
import io.dekorate.kubernetes.decorator.AddLabelDecorator;
import io.dekorate.kubernetes.decorator.AddLivenessProbeDecorator;
import io.dekorate.kubernetes.decorator.AddMetadataToTemplateDecorator;
Expand Down Expand Up @@ -602,7 +602,7 @@ public void andThenVisit(ContainerBuilder builder) {
}

result.add(new DecoratorBuildItem(target,
new AddInitContainerDecorator(name, containerBuilder
new ApplyInitContainerDecorator(name, containerBuilder
.addAllToEnvVars(item.getEnvVars().entrySet().stream().map(e -> new EnvBuilder()
.withName(e.getKey())
.withValue(e.getValue())
Expand Down Expand Up @@ -750,7 +750,8 @@ private static List<DecoratorBuildItem> createPodDecorators(Optional<Project> pr
});

config.getInitContainers().entrySet().forEach(e -> {
result.add(new DecoratorBuildItem(target, new AddInitContainerDecorator(name, ContainerConverter.convert(e))));
Container container = ContainerConverter.convert(e);
result.add(new DecoratorBuildItem(target, new ApplyInitContainerDecorator(name, container)));
});

config.getSidecars().entrySet().forEach(e -> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
import io.dekorate.kubernetes.config.KubernetesConfigBuilder;
import io.dekorate.kubernetes.configurator.ApplyDeployToApplicationConfiguration;
import io.dekorate.kubernetes.decorator.AddIngressDecorator;
import io.dekorate.kubernetes.decorator.AddInitContainerDecorator;
import io.dekorate.kubernetes.decorator.AddServiceResourceDecorator;
import io.dekorate.kubernetes.decorator.ApplyHeadlessDecorator;
import io.dekorate.kubernetes.decorator.ApplyImageDecorator;
Expand Down Expand Up @@ -108,7 +107,7 @@ protected void addDecorators(String group, KubernetesConfig config) {
super.addDecorators(group, config);

for (Container container : config.getInitContainers()) {
resourceRegistry.decorate(group, new AddInitContainerDecorator(config.getName(), container));
resourceRegistry.decorate(group, new ApplyInitContainerDecorator(config.getName(), container));
}

if (config.getPorts().length > 0) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
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 KubernetesWithFlywayInitOverrideWaiterImageTest {

private static final String NAME = "kubernetes-with-flyway-init-override-waiter-image";
private static final String OVERRIDDEN_WAITER_IMAGE = "overridden:latest";

@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.flyway.migrate-at-start", "true")
.overrideConfigKey("quarkus.kubernetes.init-containers.\"init\".image", OVERRIDDEN_WAITER_IMAGE)
.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");
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.getInitContainers()).singleElement().satisfies(container -> {
assertThat(container.getName()).isEqualTo("init");
assertThat(container.getImage()).isEqualTo(OVERRIDDEN_WAITER_IMAGE);
assertThat(container.getArgs()).containsExactly("job", NAME + "-flyway-init");
});

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

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.getTemplate()).satisfies(t -> {
assertThat(t.getSpec()).satisfies(podSpec -> {
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());
}
}

0 comments on commit a3c49bf

Please sign in to comment.