Skip to content

Commit

Permalink
Merge pull request #35877 from iocanel/wait-for-config-and-doc
Browse files Browse the repository at this point in the history
Introduce default config for init tasks
  • Loading branch information
iocanel authored Sep 13, 2023
2 parents db0ddb5 + 76842a2 commit 73439e7
Show file tree
Hide file tree
Showing 10 changed files with 178 additions and 13 deletions.
23 changes: 21 additions & 2 deletions docs/src/main/asciidoc/flyway.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -290,18 +290,37 @@ once and then start the actual application without Flyway. To support this use c
the generated manifests contain a Kubernetes initialization `Job` for Flyway.
The `Job` performs initialization and the actual `Pod`, will starts once the `Job` is successfully completed.

=== Disabling

The feature is enabled by default and can be globally disabled, using:

[source,properties]
----
quarkus.kubernetes.externalize-init=false
quarkus.kubernetes.init-task-defaults.enabled=false
----

or on OpenShift:

[source,properties]
----
quarkus.openshift.externalize-init=false
quarkus.openshift.init-task-defaults.enabled=false
----

=== Using a custom image that controls waiting for the Job

To change the `wait-for` image which by default is `groundnuty/k8s-wait-for:no-root-v1.7` you can use:

[source,properties]
----
quarkus.kubernetes.init-task-defaults.wait-for-image=my/wait-for-image:1.0
----

or on Openshift:


[source,properties]
----
quarkus.openshift.init-task-defaults.wait-for-image=my/wait-for-image:1.0
----

**Note**: In this context globally means `for all extensions that support init task externalization`.
23 changes: 21 additions & 2 deletions docs/src/main/asciidoc/liquibase-mongodb.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -159,18 +159,37 @@ once and then start the actual application without Liquibase. To support this us
the generated manifests contain a Kubernetes initialization `Job` for Liquibase.
The `Job` performs initialization and the actual `Pod`, will starts once the `Job` is successfully completed.

=== Disabling

The feature is enabled by default and can be globally disabled, using:

[source,properties]
----
quarkus.kubernetes.externalize-init=false
quarkus.kubernetes.init-task-defaults.enabled=false
----

or on OpenShift:

[source,properties]
----
quarkus.openshift.externalize-init=false
quarkus.openshift.init-task-defaults.enabled=false
----

=== Using a custom image that controls waiting for the Job

To change the `wait-for` image which by default is `groundnuty/k8s-wait-for:no-root-v1.7` you can use:

[source,properties]
----
quarkus.kubernetes.init-task-defaults.wait-for-image=my/wait-for-image:1.0
----

or on Openshift:


[source,properties]
----
quarkus.openshift.init-task-defaults.wait-for-image=my/wait-for-image:1.0
----

**Note**: In this context globally means `for all extensions that support init task externalization`.
Expand Down
25 changes: 23 additions & 2 deletions docs/src/main/asciidoc/liquibase.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -237,20 +237,41 @@ once and then start the actual application without Liquibase. To support this us
the generated manifests contain a Kubernetes initialization `Job` for Liquibase.
The `Job` performs initialization and the actual `Pod`, will starts once the `Job` is successfully completed.

=== Disabling

The feature is enabled by default and can be globally disabled, using:

[source,properties]
----
quarkus.kubernetes.externalize-init=false
quarkus.kubernetes.init-task-defaults.enabled=false
----

or on OpenShift:

[source,properties]
----
quarkus.openshift.externalize-init=false
quarkus.openshift.init-task-defaults.enabled=false
----

=== Using a custom image that controls waiting for the Job

To change the `wait-for` image which by default is `groundnuty/k8s-wait-for:no-root-v1.7` you can use:

[source,properties]
----
quarkus.kubernetes.init-task-defaults.wait-for-image=my/wait-for-image:1.0
----

or on Openshift:


[source,properties]
----
quarkus.openshift.init-task-defaults.wait-for-image=my/wait-for-image:1.0
----



**Note**: In this context globally means `for all extensions that support init task externalization`.

== Configuration Reference
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package io.quarkus.kubernetes.deployment;

import java.util.Optional;

import io.quarkus.runtime.annotations.ConfigGroup;
import io.quarkus.runtime.annotations.ConfigItem;

Expand All @@ -11,9 +13,17 @@ public class InitTaskConfig {
@ConfigItem(defaultValue = "true")
public boolean enabled;

/**
* The init task image to use by the init-container.
*/
@Deprecated
@ConfigItem
public Optional<String> image;

/**
* The init task image to use by the init-container.
*/
@ConfigItem(defaultValue = "groundnuty/k8s-wait-for:no-root-v1.7")
public String image;
public String waitForImage;

}
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,13 @@
public class InitTaskProcessor {

private static final String INIT_CONTAINER_WAITER_NAME = "init";
private static final String INIT_CONTAINER_WAITER_DEFAULT_IMAGE = "groundnuty/k8s-wait-for:no-root-v1.7";

static void process(
String target, // kubernetes, openshift, etc.
String name,
ContainerImageInfoBuildItem image,
List<InitTaskBuildItem> initTasks,
InitTaskConfig initTaskDefaults,
Map<String, InitTaskConfig> initTasksConfig,
BuildProducer<KubernetesJobBuildItem> jobs,
BuildProducer<KubernetesInitContainerBuildItem> initContainers,
Expand All @@ -40,7 +40,7 @@ static void process(

boolean generateRoleForJobs = false;
for (InitTaskBuildItem task : initTasks) {
InitTaskConfig config = initTasksConfig.get(task.getName());
InitTaskConfig config = initTasksConfig.getOrDefault(task.getName(), initTaskDefaults);
if (config == null || config.enabled) {
generateRoleForJobs = true;
jobs.produce(KubernetesJobBuildItem.create(image.getImage())
Expand All @@ -60,8 +60,8 @@ static void process(
.build())));
});

initContainers.produce(KubernetesInitContainerBuildItem.create(INIT_CONTAINER_WAITER_NAME,
config == null ? INIT_CONTAINER_WAITER_DEFAULT_IMAGE : config.image)
String waitForImage = config.image.orElse(config.waitForImage);
initContainers.produce(KubernetesInitContainerBuildItem.create(INIT_CONTAINER_WAITER_NAME, waitForImage)
.withTarget(target)
.withArguments(List.of("job", task.getName())));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -381,6 +381,15 @@ public enum DeploymentResourceKind {
@ConfigItem
Map<String, InitTaskConfig> initTasks;

/**
* Default Init tasks configuration.
*
* The init tasks are automatically generated by extensions like Flyway to perform the database migration before staring
* up the application.
*/
@ConfigItem
InitTaskConfig initTaskDefaults;

/**
* Switch used to control whether non-idempotent fields are included in generated kubernetes resources to improve
* git-ops compatibility
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -598,6 +598,15 @@ public EnvVarsConfig getEnv() {
@ConfigItem
Map<String, InitTaskConfig> initTasks;

/**
* Default Init tasks configuration.
*
* The init tasks are automatically generated by extensions like Flyway to perform the database migration before staring
* up the application.
*/
@ConfigItem
InitTaskConfig initTaskDefaults;

/**
* Switch used to control whether non-idempotent fields are included in generated kubernetes resources to improve
* git-ops compatibility
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -406,7 +406,7 @@ void externalizeInitTasks(
BuildProducer<DecoratorBuildItem> decorators) {
final String name = ResourceNameUtil.getResourceName(config, applicationInfo);
if (config.externalizeInit) {
InitTaskProcessor.process(OPENSHIFT, name, image, initTasks, config.initTasks,
InitTaskProcessor.process(OPENSHIFT, name, image, initTasks, config.initTaskDefaults, config.initTasks,
jobs, initContainers, env, roles, roleBindings, serviceAccount, decorators);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -319,7 +319,7 @@ void externalizeInitTasks(
BuildProducer<DecoratorBuildItem> decorators) {
final String name = ResourceNameUtil.getResourceName(config, applicationInfo);
if (config.externalizeInit) {
InitTaskProcessor.process(KUBERNETES, name, image, initTasks, config.initTasks,
InitTaskProcessor.process(KUBERNETES, name, image, initTasks, config.initTaskDefaults, config.initTasks,
jobs, initContainers, env, roles, roleBindings, serviceAccount, decorators);
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
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.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 KubernetesInitTaskWithCustomWaitForImageTest {

private static final String NAME = "kubernetes-custom-wait-for";
private static final String WAIT_FOR_IMAGE = "my/awesome-wait-for-image";

@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.init-task-defaults.wait-for-image", WAIT_FOR_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(WAIT_FOR_IMAGE);
});

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

0 comments on commit 73439e7

Please sign in to comment.