This is an automated email from the ASF dual-hosted git repository.
claudio4j pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git
The following commit(s) were added to refs/heads/main by this push:
new d20a5c519deb CAMEL-22644 camel-jbang-kubernetes: add support to
generate a CronJob instead a Deployment (#20006)
d20a5c519deb is described below
commit d20a5c519deb7f3501dc7f36c59887f6a9763a2b
Author: Claudio Miranda <[email protected]>
AuthorDate: Fri Nov 21 14:01:31 2025 +0000
CAMEL-22644 camel-jbang-kubernetes: add support to generate a CronJob
instead a Deployment (#20006)
* camel-jbang export: Fix duplicate duplicate properties starting with
camel.main
* CAMEL-22644 camel-jbang-kubernetes: add support to generate a CronJob
instead a Deployment
https://issues.apache.org/jira/browse/CAMEL-22644
Removed quarkus.management.port from quarkus-kubernetes-pom.tmpl since the
property is added to application.properties
Labels moved from container trait to label trait
docker-java.properties sets api-version as docker container requires a
newer docker client version, while camel doesn't update testcontainer
---
.../modules/ROOT/pages/camel-jbang-kubernetes.adoc | 56 +++++++++
.../dsl/jbang/core/commands/ExportBaseCommand.java | 12 +-
.../templates/quarkus-kubernetes-pom.tmpl | 8 --
.../camel/dsl/jbang/core/commands/ExportTest.java | 19 ++-
.../core/commands/kubernetes/KubernetesExport.java | 64 ++++++-----
.../core/commands/kubernetes/KubernetesRun.java | 16 ++-
.../commands/kubernetes/traits/ContainerTrait.java | 3 -
.../commands/kubernetes/traits/CronJobTrait.java | 118 +++++++++++++++++++
.../kubernetes/traits/DeploymentTrait.java | 5 +-
.../commands/kubernetes/traits/LabelTrait.java | 12 ++
.../commands/kubernetes/traits/TraitCatalog.java | 1 +
.../commands/kubernetes/traits/TraitContext.java | 7 ++
.../commands/kubernetes/traits/model/CronJob.java | 127 +++++++++++++++++++++
.../commands/kubernetes/traits/model/Traits.java | 16 ++-
.../commands/kubernetes/KubernetesExportTest.java | 63 +++++++++-
.../commands/kubernetes/KubernetesRunTest.java | 42 +++++++
.../src/test/resources/docker-java.properties | 17 +++
17 files changed, 527 insertions(+), 59 deletions(-)
diff --git a/docs/user-manual/modules/ROOT/pages/camel-jbang-kubernetes.adoc
b/docs/user-manual/modules/ROOT/pages/camel-jbang-kubernetes.adoc
index f8197ca903df..af0f7b6b555a 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-jbang-kubernetes.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-jbang-kubernetes.adoc
@@ -329,6 +329,62 @@ spec:
<3> Custom container name
<4> Custom container port exposed
+=== Cronjob
+
+It's possible to run the workload as a
https://kubernetes.io/docs/concepts/workloads/controllers/cron-jobs/[CronJob],
it allows to set some parameters as `schedule`, `timezone`,
`startingDeadlineSeconds`, `activeDeadlineSeconds`, `backoffLimit`, and
`durationMaxIdleSeconds`. When the CronJob trait is used, it doesn't contain
the container health probes, since the Cronjob doesn't use them to start or
check the pods.
+
+To generate the CronJob manifest, these are required trait parameters:
`enabled=true` and `schedule`.
+
+.Example
+
+[source,bash]
+----
+camel kubernetes export Sample.java \
+ --trait=cronjob.enabled=true \
+ --trait=cronjob.schedule="* * * * 2" \
+ --trait=cronjob.timezone="Europe/Lisbon" \
+ --trait=cronjob.startingDeadlineSeconds=3 \
+ --trait=cronjob.activeDeadlineSeconds=7 \
+ --trait=cronjob.backoffLimit=4 \
+ --trait=cronjob.durationMaxIdleSeconds=2
+----
+
+The CronJob configuration parameters:
+
+[cols="2m,1m,5a"]
+|===
+|Property | Type | Description
+
+| cronjob.enabled
+| boolean
+| To generate the CronJob manifest instead of Deployment. (default=false)
+
+| cronjob.schedule
+| string
+| To set the schedule to run the workload following the crontab format.
+
+| cronjob.timezone
+| string
+| To set the timezone to run the workload.
+
+| cronjob.startingDeadlineSeconds
+| integer
+| To set the `StartingDeadlineSeconds` field of the CronJob.
+
+| cronjob.activeDeadlineSeconds
+| integer
+| To set the `ActiveDeadlineSeconds` field of the CronJob.
+
+| cronjob.backoffLimit
+| integer
+| To set the `BackoffLimit` field of the CronJob.
+
+| cronjob.durationMaxIdleSeconds
+| integer
+| To set the `camel.main.duration-max-idle-seconds` property for how long time
in seconds Camel can be idle before automatic terminating the JVM. (default 1)
+
+|===
+
=== Labels and annotations
You may need to add labels or annotations to the generated Kubernetes
resources.
diff --git
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ExportBaseCommand.java
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ExportBaseCommand.java
index 840e8d717adf..0f84ef2cd492 100644
---
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ExportBaseCommand.java
+++
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ExportBaseCommand.java
@@ -66,6 +66,7 @@ import
org.apache.camel.tooling.maven.MavenResolutionException;
import org.apache.camel.util.CamelCaseOrderedProperties;
import org.apache.camel.util.FileUtil;
import org.apache.camel.util.IOHelper;
+import org.apache.camel.util.ObjectHelper;
import org.apache.camel.util.StringHelper;
import picocli.CommandLine;
@@ -821,7 +822,6 @@ public abstract class ExportBaseCommand extends
CamelCommand {
customize.apply(profileProps);
}
- Path appPropsPath = targetDir.resolve("application.properties");
StringBuilder content = new StringBuilder();
for (Map.Entry<Object, Object> entry : profileProps.entrySet()) {
String k = entry.getKey().toString();
@@ -852,17 +852,19 @@ public abstract class ExportBaseCommand extends
CamelCommand {
// User properties
Properties userProps = new CamelCaseOrderedProperties();
- prepareUserProperties(userProps);
+ userProps.putAll(propertiesMap(this.applicationProperties));
for (Map.Entry<Object, Object> entryUserProp : userProps.entrySet()) {
String uK = entryUserProp.getKey().toString();
String uV = entryUserProp.getValue().toString();
String line = applicationPropertyLine(uK, uV);
- if (line != null && !line.isBlank()) {
+ // properties from the profile are already included so skip them
+ if (profileProps.get(uK) == null && ObjectHelper.isNotEmpty(line))
{
content.append(line).append("\n");
}
}
// write all the properties
+ Path appPropsPath = targetDir.resolve("application.properties");
Files.writeString(appPropsPath, content.toString(),
StandardCharsets.UTF_8);
}
@@ -870,10 +872,6 @@ public abstract class ExportBaseCommand extends
CamelCommand {
// NOOP
}
- protected void prepareUserProperties(Properties properties) {
- properties.putAll(propertiesMap(this.applicationProperties));
- }
-
protected Map<String, String> propertiesMap(String[]... propertySources) {
Map<String, String> result = new LinkedHashMap<>();
if (propertySources != null) {
diff --git
a/dsl/camel-jbang/camel-jbang-core/src/main/resources/templates/quarkus-kubernetes-pom.tmpl
b/dsl/camel-jbang/camel-jbang-core/src/main/resources/templates/quarkus-kubernetes-pom.tmpl
index 01b45c5bc3c4..ca543e2b5625 100644
---
a/dsl/camel-jbang/camel-jbang-core/src/main/resources/templates/quarkus-kubernetes-pom.tmpl
+++
b/dsl/camel-jbang/camel-jbang-core/src/main/resources/templates/quarkus-kubernetes-pom.tmpl
@@ -18,7 +18,6 @@
<quarkus.platform.group-id>{{ .QuarkusGroupId
}}</quarkus.platform.group-id>
<quarkus.platform.artifact-id>{{ .QuarkusArtifactId
}}</quarkus.platform.artifact-id>
<quarkus.platform.version>{{ .QuarkusVersion
}}</quarkus.platform.version>
- <quarkus.management.port>{{ .QuarkusManagementPort
}}</quarkus.management.port>
{{ .BuildProperties }}
<skipITs>true</skipITs>
<surefire-plugin.version>3.5.4</surefire-plugin.version>
@@ -136,13 +135,6 @@
</build>
</image>
</images>
- <enricher>
- <config>
- <jkube-healthcheck-quarkus>
- <port>${quarkus.management.port}</port>
- </jkube-healthcheck-quarkus>
- </config>
- </enricher>
</configuration>
<executions>
<execution>
diff --git
a/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/ExportTest.java
b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/ExportTest.java
index 8bf84d7bfb87..074b0cb5a08b 100644
---
a/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/ExportTest.java
+++
b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/ExportTest.java
@@ -26,6 +26,7 @@ import java.nio.file.Paths;
import java.util.Arrays;
import java.util.List;
import java.util.Properties;
+import java.util.Scanner;
import java.util.stream.Stream;
import org.apache.camel.dsl.jbang.core.common.CamelJBangConstants;
@@ -591,7 +592,8 @@ class ExportTest {
// We need a real file as we want to test the generated content
Export command = createCommand(rt, new String[] {
"src/test/resources/route.yaml" },
"--gav=examples:route:1.0.0", "--dir=" + workingDir, "--quiet",
- "--property", "hello=world");
+ // there was a bug where properties starting with camel.main
were duplicated in application.properties
+ "--property", "hello=world", "--property",
"camel.main.foo=bar");
int exit = command.doCall();
Assertions.assertEquals(0, exit);
@@ -600,10 +602,23 @@ class ExportTest {
// Application properties
File appProperties = new File(workingDir + "/src/main/resources",
"application.properties");
- Assertions.assertTrue(appProperties.exists(), "Missing application
properties");
+ Assertions.assertTrue(appProperties.exists(), "Missing
application.properties");
Properties appProps = new Properties();
appProps.load(new FileInputStream(appProperties));
Assertions.assertEquals("world", appProps.getProperty("hello"));
+ Assertions.assertEquals("bar", appProps.getProperty("camel.main.foo"));
+ int nrCamelProps = 0;
+ // read the file as text to read the properties as clean text
+ // as using the Properties class won't allow duplicated properties
+ try (Scanner scanner = new Scanner(new File(workingDir +
"/src/main/resources/application.properties"))) {
+ while (scanner.hasNextLine()) {
+ String line = scanner.nextLine();
+ if ("camel.main.foo=bar".equals(line)) {
+ nrCamelProps++;
+ }
+ }
+ }
+ Assertions.assertEquals(1, nrCamelProps, "Duplicated property
camel.main.foo in application.properties");
}
@ParameterizedTest
diff --git
a/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/main/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/KubernetesExport.java
b/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/main/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/KubernetesExport.java
index 390f6348a9f3..fbd8648526e7 100644
---
a/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/main/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/KubernetesExport.java
+++
b/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/main/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/KubernetesExport.java
@@ -378,7 +378,15 @@ public class KubernetesExport extends Export {
var managementPort = httpManagementPort(settingsPath);
buildProperties.add("jkube.version=%s".formatted(jkubeVersion));
- setContainerHealthPaths(managementPort);
+ boolean cronJobEnabled = traitsSpec.getCronjob() != null &&
traitsSpec.getCronjob().getEnabled();
+ if (cronJobEnabled) {
+ // set this property to allow the JVM to finish quickly once there
are no more exchange messages
+ // important for cronjobs so that the jvm can end quickly
+ addToApplicationProperties(
+ "camel.main.duration-max-idle-seconds=" +
traitsSpec.getCronjob().getDurationMaxIdleSeconds());
+ } else {
+ setContainerHealthPaths(managementPort);
+ }
// Run export
int exit = super.doExport();
@@ -515,41 +523,34 @@ public class KubernetesExport extends Export {
// jkube reads quarkus properties to set the container health
probes path
buildProperties.add("jkube.enricher.jkube-healthcheck-quarkus.port=" +
probePort);
buildProperties.add("quarkus.smallrye-health.root-path=/observe/health");
- List<String> newProps = new ArrayList<>();
- newProps.add("quarkus.management.port=" + probePort);
- if (applicationProperties == null) {
- applicationProperties = newProps.toArray(new
String[newProps.size()]);
- } else {
- newProps.addAll(Arrays.asList(applicationProperties));
- applicationProperties = newProps.toArray(new
String[newProps.size()]);
- }
+ addToApplicationProperties("quarkus.management.port=" + probePort);
} else if (RuntimeType.springBoot == runtime) {
- List<String> newProps = new ArrayList<>();
+ //
addDependencies("org.springframework.boot:spring-boot-starter-actuator");
// jkube reads spring-boot properties to set the kubernetes
container health probes path
// in this case, jkube reads from the application.properties and
not from the build properties in pom.xml
- newProps.add("management.endpoints.web.base-path=/observe");
- newProps.add("management.server.port=" + probePort);
- // jkube uses the old property to enable the readiness/liveness
probes
- // TODO: rename this property once
https://github.com/eclipse-jkube/jkube/issues/3690 is fixed
- newProps.add("management.health.probes.enabled=true");
- if (applicationProperties == null) {
- applicationProperties = newProps.toArray(new
String[newProps.size()]);
- } else {
- newProps.addAll(Arrays.asList(applicationProperties));
- applicationProperties = newProps.toArray(new
String[newProps.size()]);
- }
+
addToApplicationProperties("management.endpoints.web.base-path=/observe",
+ "management.server.port=" + probePort,
+ // jkube uses the old property to enable the
readiness/liveness probes
+ // TODO: rename this property once
https://github.com/eclipse-jkube/jkube/issues/3690 is fixed
+ "management.health.probes.enabled=true");
} else if (RuntimeType.main == runtime) {
- List<String> newProps = new ArrayList<>();
- newProps.add("camel.management.port=" + probePort);
- if (applicationProperties == null) {
- applicationProperties = newProps.toArray(new
String[newProps.size()]);
- } else {
- newProps.addAll(Arrays.asList(applicationProperties));
- applicationProperties = newProps.toArray(new
String[newProps.size()]);
- }
+ addToApplicationProperties("camel.management.port=" + probePort);
}
}
+ // helper method to add parameters to the applicationProperties
+ // it takes care to resize the string array
+ private void addToApplicationProperties(String... lines) {
+ List<String> newProps = new ArrayList<>();
+ for (String line : lines) {
+ newProps.add(line);
+ }
+ if (applicationProperties != null) {
+ newProps.addAll(Arrays.asList(applicationProperties));
+ }
+ applicationProperties = newProps.toArray(new String[newProps.size()]);
+ }
+
private String extractImageGroup(String image) {
String[] parts = image.split("/");
if (parts.length == 3) {
@@ -598,7 +599,6 @@ public class KubernetesExport extends Export {
if (image != null) {
return StringHelper.afterLast(image, ":");
}
-
return super.getVersion();
}
@@ -606,6 +606,10 @@ public class KubernetesExport extends Export {
this.applicationProperties = props;
}
+ void setObserve(boolean observe) {
+ this.observe = observe;
+ }
+
/**
* Configurer used to customize internal options for the Export command.
*/
diff --git
a/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/main/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/KubernetesRun.java
b/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/main/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/KubernetesRun.java
index bfd32b2a3417..9a632cd409cd 100644
---
a/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/main/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/KubernetesRun.java
+++
b/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/main/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/KubernetesRun.java
@@ -291,6 +291,7 @@ public class KubernetesRun extends KubernetesBaseCommand {
private KubernetesPodLogs reusablePodLogs;
private Printer quietPrinter;
+ private Traits computedTraits;
public KubernetesRun(CamelJBangMain main) {
this(main, null);
@@ -312,6 +313,7 @@ public class KubernetesRun extends KubernetesBaseCommand {
public Integer doCall() throws Exception {
String projectName = getProjectName();
+ computedTraits = TraitHelper.parseTraits(traits);
Path baseDir = Path.of(".");
if (files.size() == 1) {
@@ -348,6 +350,11 @@ public class KubernetesRun extends KubernetesBaseCommand {
String workingDir = getIndexedWorkingDir(projectName);
KubernetesExport export = configureExport(workingDir, baseDir);
+ boolean cronEnabled = computedTraits.getCronjob() != null &&
computedTraits.getCronjob().getEnabled();
+ if (cronEnabled) {
+ // disable observability-services as CronJob doesn't use the
container probes
+ export.setObserve(false);
+ }
int exit = export.export();
if (exit != 0) {
printer().printErr("Project export failed!");
@@ -355,8 +362,7 @@ public class KubernetesRun extends KubernetesBaseCommand {
}
if (output != null) {
- Traits ptraits = TraitHelper.parseTraits(traits);
- boolean ksvcEnabled = ptraits.getKnativeService() != null &&
ptraits.getKnativeService().getEnabled();
+ boolean ksvcEnabled = computedTraits.getKnativeService() != null
&& computedTraits.getKnativeService().getEnabled();
exit = buildProjectOutput(workingDir);
if (exit != 0) {
@@ -658,8 +664,7 @@ public class KubernetesRun extends KubernetesBaseCommand {
// suppress maven transfer progress
args.add("-ntp");
- Traits ptraits = TraitHelper.parseTraits(traits);
- if (ptraits.getKnativeService() != null &&
ptraits.getKnativeService().getEnabled()) {
+ if (computedTraits.getKnativeService() != null &&
computedTraits.getKnativeService().getEnabled()) {
// by default jkube creates a Deployment manifest and it doesn't
support knative controller yet.
// however when knative-service is enabled the knative-service
trait generates a src/main/jkube/service.yml
// and there is no need for the regular Deployment as the knative
Service manifest, once deployed
@@ -727,8 +732,7 @@ public class KubernetesRun extends KubernetesBaseCommand {
boolean isOpenshift = ClusterType.OPENSHIFT.isEqualTo(clusterType);
var prefix = isOpenshift ? "oc" : "k8s";
- Traits ptraits = TraitHelper.parseTraits(traits);
- if (ptraits.getKnativeService() != null &&
ptraits.getKnativeService().getEnabled()) {
+ if (computedTraits.getKnativeService() != null &&
computedTraits.getKnativeService().getEnabled()) {
// by default jkube creates a Deployment manifest and it doesn't
support knative controller yet.
// however when knative-service is enabled the knative-service
trait generates a src/main/jkube/service.yml
// and there is no need for the regular Deployment as the knative
Service manifest, once deployed
diff --git
a/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/main/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/traits/ContainerTrait.java
b/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/main/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/traits/ContainerTrait.java
index 03dfe897268d..275722ab8644 100644
---
a/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/main/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/traits/ContainerTrait.java
+++
b/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/main/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/traits/ContainerTrait.java
@@ -111,9 +111,6 @@ public class ContainerTrait extends BaseTrait {
context.doWithCronJobs(j -> j.editOrNewSpec()
.editOrNewJobTemplate()
- .editOrNewMetadata()
- .addToLabels(KUBERNETES_LABEL_NAME, context.getName())
- .endMetadata()
.editOrNewSpec()
.editOrNewTemplate()
.editOrNewSpec()
diff --git
a/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/main/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/traits/CronJobTrait.java
b/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/main/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/traits/CronJobTrait.java
new file mode 100644
index 000000000000..2ddfc8bd270b
--- /dev/null
+++
b/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/main/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/traits/CronJobTrait.java
@@ -0,0 +1,118 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.camel.dsl.jbang.core.commands.kubernetes.traits;
+
+import java.util.List;
+import java.util.Optional;
+
+import io.fabric8.kubernetes.api.model.batch.v1.CronJobBuilder;
+import
org.apache.camel.dsl.jbang.core.commands.kubernetes.traits.model.Container;
+import
org.apache.camel.dsl.jbang.core.commands.kubernetes.traits.model.CronJob;
+import org.apache.camel.dsl.jbang.core.commands.kubernetes.traits.model.Traits;
+import org.apache.camel.util.ObjectHelper;
+
+public class CronJobTrait extends BaseTrait {
+
+ public CronJobTrait() {
+ super("cronjob", 900);
+ }
+
+ @Override
+ public boolean configure(Traits traitConfig, TraitContext context) {
+ return traitConfig.getCronjob() != null
+ &&
Optional.ofNullable(traitConfig.getCronjob().getEnabled()).orElse(false)
+ &&
ObjectHelper.isNotEmpty(traitConfig.getCronjob().getSchedule());
+ }
+
+ @Override
+ public void apply(Traits traitConfig, TraitContext context) {
+ CronJob cronjobTrait =
Optional.ofNullable(traitConfig.getCronjob()).orElseGet(CronJob::new);
+ CronJobBuilder cronjobBuilder = new CronJobBuilder()
+ .withNewMetadata()
+ .withName(context.getName())
+ .endMetadata()
+ .withNewSpec()
+ .endSpec();
+
+ Container containerTrait =
Optional.ofNullable(traitConfig.getContainer()).orElseGet(Container::new);
+ // sets the image pull secret
+
Optional.ofNullable(containerTrait.getImagePullSecrets()).orElseGet(List::of)
+ .forEach(sec -> cronjobBuilder.editOrNewSpec()
+ .editOrNewJobTemplate()
+ .editOrNewSpec()
+ .editOrNewTemplate()
+ .editOrNewSpec()
+ .addNewImagePullSecret(sec)
+ .endSpec()
+ .endTemplate()
+ .endSpec()
+ .endJobTemplate()
+ .endSpec());
+
+ // sets the service account
+ if (context.getServiceAccount() != null) {
+ cronjobBuilder.editOrNewSpec()
+ .editOrNewJobTemplate()
+ .editOrNewSpec()
+ .editOrNewTemplate()
+ .editOrNewSpec()
+ .withServiceAccountName(context.getServiceAccount())
+ .endSpec()
+ .endTemplate()
+ .endSpec()
+ .endJobTemplate()
+ .endSpec();
+ }
+
+ // sets the schedule and restart-policy
+ cronjobBuilder.editOrNewSpec()
+ // set the timezone
+ .withSchedule(traitConfig.getCronjob().getSchedule())
+ .editOrNewJobTemplate()
+ .editOrNewSpec()
+ .editOrNewTemplate()
+ .editOrNewSpec()
+ // set the restartPolicy
+ .withRestartPolicy("Never")
+ .endSpec()
+ .endTemplate()
+ .endSpec()
+ .endJobTemplate()
+ .endSpec();
+
+ // sets the timezone
+ if (ObjectHelper.isNotEmpty(cronjobTrait.getTimezone())) {
+
cronjobBuilder.editOrNewSpec().withTimeZone(cronjobTrait.getTimezone()).endSpec();
+ }
+ // sets the ActiveDeadlineSeconds
+ if (cronjobTrait.getActiveDeadlineSeconds() != null &&
cronjobTrait.getActiveDeadlineSeconds() > 0) {
+
cronjobBuilder.editOrNewSpec().editOrNewJobTemplate().editOrNewSpec()
+
.withActiveDeadlineSeconds(cronjobTrait.getActiveDeadlineSeconds()).endSpec().endJobTemplate().endSpec();
+ }
+ // sets the BackoffLimit
+ if (cronjobTrait.getBackoffLimit() != null &&
cronjobTrait.getBackoffLimit() > 0) {
+
cronjobBuilder.editOrNewSpec().editOrNewJobTemplate().editOrNewSpec()
+
.withBackoffLimit(cronjobTrait.getBackoffLimit()).endSpec().endJobTemplate().endSpec();
+ }
+ // sets the StartingDeadlineSeconds
+ if (cronjobTrait.getStartingDeadlineSeconds() != null &&
cronjobTrait.getStartingDeadlineSeconds() > 0) {
+
cronjobBuilder.editOrNewSpec().withStartingDeadlineSeconds(cronjobTrait.getStartingDeadlineSeconds()).endSpec();
+ }
+ context.add(cronjobBuilder);
+ }
+}
diff --git
a/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/main/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/traits/DeploymentTrait.java
b/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/main/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/traits/DeploymentTrait.java
index 35fa071e68d3..bbafcccb62a0 100644
---
a/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/main/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/traits/DeploymentTrait.java
+++
b/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/main/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/traits/DeploymentTrait.java
@@ -34,12 +34,15 @@ public class DeploymentTrait extends BaseTrait {
@Override
public boolean configure(Traits traitConfig, TraitContext context) {
+ // disable the deployment trait if cronjob is enabled
+ boolean cronjobDisabled = traitConfig.getCronjob() == null
+ ||
!Optional.ofNullable(traitConfig.getCronjob().getEnabled()).orElse(false);
// disable the deployment trait if knative-service is enabled
boolean knEnabled = false;
if (traitConfig.getKnativeService() != null) {
knEnabled =
Optional.ofNullable(traitConfig.getKnativeService().getEnabled()).orElse(false);
}
- return !knEnabled;
+ return cronjobDisabled && !knEnabled;
}
@Override
diff --git
a/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/main/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/traits/LabelTrait.java
b/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/main/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/traits/LabelTrait.java
index 8bebbdf97d6a..8ab08380e953 100644
---
a/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/main/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/traits/LabelTrait.java
+++
b/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/main/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/traits/LabelTrait.java
@@ -42,5 +42,17 @@ public class LabelTrait extends BaseTrait {
.addToLabels(KUBERNETES_LABEL_NAME, context.getName())
.addToLabels(context.getLabels())
.endMetadata());
+ context.doWithCronJobs(
+ c -> c.editOrNewMetadata()
+ .addToLabels(KUBERNETES_LABEL_NAME, context.getName())
+ .addToLabels(context.getLabels())
+ .endMetadata()
+ .editOrNewSpec()
+ .editOrNewJobTemplate()
+ .editOrNewMetadata()
+ .addToLabels(KUBERNETES_LABEL_NAME, context.getName())
+ .endMetadata()
+ .endJobTemplate()
+ .endSpec());
}
}
diff --git
a/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/main/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/traits/TraitCatalog.java
b/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/main/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/traits/TraitCatalog.java
index 0a491f818aa8..cb67f33a2ce1 100644
---
a/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/main/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/traits/TraitCatalog.java
+++
b/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/main/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/traits/TraitCatalog.java
@@ -39,6 +39,7 @@ public class TraitCatalog {
public TraitCatalog() {
register(new DeploymentTrait());
+ register(new CronJobTrait());
register(new KnativeTrait());
register(new KnativeServiceTrait());
register(new ServiceTrait());
diff --git
a/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/main/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/traits/TraitContext.java
b/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/main/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/traits/TraitContext.java
index 866af1b34f5f..84f7fabc48d1 100644
---
a/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/main/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/traits/TraitContext.java
+++
b/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/main/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/traits/TraitContext.java
@@ -135,6 +135,13 @@ public class TraitContext {
.findFirst();
}
+ public Optional<CronJobBuilder> getCronJob() {
+ return resourceRegistry.stream()
+ .filter(it ->
it.getClass().isAssignableFrom(CronJobBuilder.class))
+ .map(it -> (CronJobBuilder) it)
+ .findFirst();
+ }
+
public Optional<IngressBuilder> getIngress() {
return resourceRegistry.stream()
.filter(it ->
it.getClass().isAssignableFrom(IngressBuilder.class))
diff --git
a/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/main/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/traits/model/CronJob.java
b/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/main/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/traits/model/CronJob.java
new file mode 100644
index 000000000000..e91f9a6975d6
--- /dev/null
+++
b/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/main/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/traits/model/CronJob.java
@@ -0,0 +1,127 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.camel.dsl.jbang.core.commands.kubernetes.traits.model;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.annotation.JsonPropertyDescription;
+import com.fasterxml.jackson.annotation.JsonPropertyOrder;
+import com.fasterxml.jackson.annotation.JsonSetter;
+import com.fasterxml.jackson.annotation.Nulls;
+
+@JsonInclude(JsonInclude.Include.NON_NULL)
+@JsonPropertyOrder({
+ "enabled", "schedule", "timezone", "startingDeadlineSeconds",
"activeDeadlineSeconds", "backoffLimit",
+ "durationMaxIdleSeconds" })
+public class CronJob {
+
+ @JsonProperty("enabled")
+ @JsonPropertyDescription("Can be used to enable or disable a trait.")
+ @JsonSetter(nulls = Nulls.SKIP)
+ private Boolean enabled = Boolean.FALSE;
+
+ @JsonProperty("schedule")
+ @JsonPropertyDescription("The schedule in Cron format.")
+ @JsonSetter(nulls = Nulls.SKIP)
+ private String schedule;
+
+ @JsonProperty("timezone")
+ @JsonPropertyDescription("The time zone name for the given schedule.")
+ @JsonSetter(nulls = Nulls.SKIP)
+ private String timezone;
+
+ @JsonProperty("startingDeadlineSeconds")
+ @JsonPropertyDescription("Optional deadline in seconds for starting the
job if it misses scheduled time for any reason.")
+ @JsonSetter(nulls = Nulls.SKIP)
+ private Long startingDeadlineSeconds;
+
+ @JsonProperty("activeDeadlineSeconds")
+ @JsonPropertyDescription("Specifies the duration in seconds relative to
the startTime that the job may be continuously active before the system tries
to terminate it.")
+ @JsonSetter(nulls = Nulls.SKIP)
+ private Long activeDeadlineSeconds;
+
+ @JsonProperty("backoffLimit")
+ @JsonPropertyDescription("Specifies the number of retries before marking
this job failed.")
+ @JsonSetter(nulls = Nulls.SKIP)
+ private Integer backoffLimit;
+
+ @JsonProperty("durationMaxIdleSeconds")
+ @JsonPropertyDescription("How long time in seconds Camel can be idle
before automatic terminating the JVM, it sets the
camel.main.duration-max-idle-seconds property, default to 1.")
+ @JsonSetter(nulls = Nulls.SKIP)
+ private Integer durationMaxIdleSeconds = 1;
+
+ public CronJob() {
+ }
+
+ public Boolean getEnabled() {
+ return enabled;
+ }
+
+ public void setEnabled(Boolean enabled) {
+ this.enabled = enabled;
+ }
+
+ public String getSchedule() {
+ return schedule;
+ }
+
+ public void setSchedule(String schedule) {
+ this.schedule = schedule;
+ }
+
+ public String getTimezone() {
+ return timezone;
+ }
+
+ public void setTimezone(String timezone) {
+ this.timezone = timezone;
+ }
+
+ public Long getStartingDeadlineSeconds() {
+ return startingDeadlineSeconds;
+ }
+
+ public void setStartingDeadlineSeconds(Long startingDeadlineSeconds) {
+ this.startingDeadlineSeconds = startingDeadlineSeconds;
+ }
+
+ public Long getActiveDeadlineSeconds() {
+ return activeDeadlineSeconds;
+ }
+
+ public void setActiveDeadlineSeconds(Long activeDeadlineSeconds) {
+ this.activeDeadlineSeconds = activeDeadlineSeconds;
+ }
+
+ public Integer getBackoffLimit() {
+ return backoffLimit;
+ }
+
+ public void setBackoffLimit(Integer backoffLimit) {
+ this.backoffLimit = backoffLimit;
+ }
+
+ public Integer getDurationMaxIdleSeconds() {
+ return durationMaxIdleSeconds;
+ }
+
+ public void setDurationMaxIdleSeconds(Integer durationMaxIdleSeconds) {
+ this.durationMaxIdleSeconds = durationMaxIdleSeconds;
+ }
+
+}
diff --git
a/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/main/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/traits/model/Traits.java
b/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/main/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/traits/model/Traits.java
index 512f53fe37b7..496663f9c61b 100644
---
a/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/main/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/traits/model/Traits.java
+++
b/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/main/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/traits/model/Traits.java
@@ -28,7 +28,8 @@ import com.fasterxml.jackson.annotation.Nulls;
@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonPropertyOrder({
- "camel", "container", "environment", "ingress", "knative",
"knative-service", "mount", "openapi", "pod", "route",
+ "camel", "container", "cronjob", "environment", "ingress", "knative",
"knative-service", "mount", "openapi", "pod",
+ "route",
"service", "service-binding", "jolokia" })
public class Traits {
@@ -47,6 +48,11 @@ public class Traits {
@JsonSetter(nulls = Nulls.SKIP)
private Container container;
+ @JsonProperty("cronjob")
+ @JsonPropertyDescription("The configuration of CronJob trait")
+ @JsonSetter(nulls = Nulls.SKIP)
+ private CronJob cronjob;
+
@JsonProperty("environment")
@JsonPropertyDescription("The configuration of Environment trait")
@JsonSetter(nulls = Nulls.SKIP)
@@ -121,6 +127,14 @@ public class Traits {
this.container = container;
}
+ public CronJob getCronjob() {
+ return cronjob;
+ }
+
+ public void setCronjob(CronJob cronjob) {
+ this.cronjob = cronjob;
+ }
+
public Environment getEnvironment() {
return environment;
}
diff --git
a/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/test/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/KubernetesExportTest.java
b/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/test/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/KubernetesExportTest.java
index c5745982518a..99d3729f2bf1 100644
---
a/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/test/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/KubernetesExportTest.java
+++
b/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/test/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/KubernetesExportTest.java
@@ -27,8 +27,11 @@ import io.fabric8.kubernetes.api.model.ServicePort;
import io.fabric8.kubernetes.api.model.Volume;
import io.fabric8.kubernetes.api.model.VolumeMount;
import io.fabric8.kubernetes.api.model.apps.Deployment;
+import io.fabric8.kubernetes.api.model.batch.v1.CronJob;
+import io.fabric8.kubernetes.api.model.batch.v1.JobSpec;
import io.fabric8.kubernetes.api.model.networking.v1.Ingress;
import io.fabric8.openshift.api.model.Route;
+import org.apache.camel.RuntimeCamelException;
import org.apache.camel.dsl.jbang.core.commands.kubernetes.traits.BaseTrait;
import org.apache.camel.dsl.jbang.core.common.RuntimeType;
import org.apache.camel.util.IOHelper;
@@ -76,7 +79,6 @@ class KubernetesExportTest extends KubernetesExportBaseTest {
Properties applicationProperties =
getApplicationProperties(workingDir);
if (RuntimeType.quarkus == RuntimeType.fromValue(rt.runtime())) {
- Assertions.assertEquals("9876",
props.get("quarkus.management.port"));
Assertions.assertEquals("9876",
applicationProperties.get("quarkus.management.port"));
Assertions.assertEquals("9876",
props.get("jkube.enricher.jkube-healthcheck-quarkus.port"));
Assertions.assertEquals("/observe/health",
props.get("quarkus.smallrye-health.root-path"));
@@ -213,6 +215,65 @@ class KubernetesExportTest extends
KubernetesExportBaseTest {
Assertions.assertFalse(hasKnativeService(rt));
}
+ @ParameterizedTest
+ @MethodSource("runtimeProvider")
+ public void shouldGenerateCronjobKubernetesManifest(RuntimeType rt) throws
Exception {
+ KubernetesExport command = createCommand(new String[] {
"classpath:route.yaml" },
+ "--image-registry=quay.io", "--image-group=camel-test",
"--runtime=" + rt.runtime(),
+ "--service-account=my-svc-account");
+ command.traits = new String[] {
+ "cronjob.enabled=true",
+ "cronjob.schedule=\"0 22 * * 1-5\"",
+ "cronjob.timezone=Europe/Lisbon",
+ "cronjob.startingDeadlineSeconds=2",
+ "cronjob.activeDeadlineSeconds=3",
+ "cronjob.backoffLimit=4",
+ "cronjob.durationMaxIdleSeconds=5",
+ "container.imagePullPolicy=Never"
+ };
+ int exit = command.doCall();
+ Assertions.assertEquals(0, exit);
+
+ CronJob cronjob = getResource(rt, CronJob.class)
+ .orElseThrow(() -> new RuntimeCamelException("Cannot find
cronjob for: %s".formatted(rt.runtime())));
+ JobSpec jobSpec = cronjob.getSpec().getJobTemplate().getSpec();
+ Assertions.assertEquals("route", cronjob.getMetadata().getName());
+ Assertions.assertEquals("0 22 * * 1-5",
cronjob.getSpec().getSchedule());
+ Assertions.assertEquals("Europe/Lisbon",
cronjob.getSpec().getTimeZone());
+ Assertions.assertEquals(2,
cronjob.getSpec().getStartingDeadlineSeconds());
+ Assertions.assertEquals(3, jobSpec.getActiveDeadlineSeconds());
+ Assertions.assertEquals(4, jobSpec.getBackoffLimit());
+ Assertions.assertEquals("Never",
jobSpec.getTemplate().getSpec().getContainers().get(0).getImagePullPolicy());
+ Assertions.assertEquals("my-svc-account",
jobSpec.getTemplate().getSpec().getServiceAccountName());
+
+ Properties applicationProperties =
getApplicationProperties(workingDir);
+ Assertions.assertEquals("5",
applicationProperties.getProperty("camel.main.duration-max-idle-seconds"));
+
+ Model model = readMavenModel();
+ Assertions.assertEquals("org.example.project", model.getGroupId());
+ Assertions.assertEquals("route", model.getArtifactId());
+ Assertions.assertEquals("1.0-SNAPSHOT", model.getVersion());
+
+ Properties props = model.getProperties();
+ Assertions.assertEquals("quay.io/camel-test/route:1.0-SNAPSHOT",
props.get("jkube.image.name"));
+ Assertions.assertEquals("quay.io/camel-test/route:1.0-SNAPSHOT",
props.get("jkube.container-image.name"));
+ Assertions.assertTrue(hasService(rt));
+ Assertions.assertFalse(hasKnativeService(rt));
+
+ // there are no health probes for cronjobs
+ if (RuntimeType.quarkus == RuntimeType.fromValue(rt.runtime())) {
+
Assertions.assertNull(applicationProperties.get("quarkus.management.port"));
+
Assertions.assertNull(props.get("jkube.enricher.jkube-healthcheck-quarkus.port"));
+
Assertions.assertNull(props.get("quarkus.smallrye-health.root-path"));
+ } else if (RuntimeType.springBoot ==
RuntimeType.fromValue(rt.runtime())) {
+
Assertions.assertNull(applicationProperties.get("management.server.port"));
+
Assertions.assertNull(applicationProperties.get("management.endpoints.web.base-path"));
+
Assertions.assertNull(applicationProperties.get("management.health.probes.enabled"));
+ } else if (RuntimeType.main == RuntimeType.fromValue(rt.runtime())) {
+
Assertions.assertNull(applicationProperties.get("camel.management.port"));
+ }
+ }
+
@ParameterizedTest
@MethodSource("runtimeProvider")
public void shouldAddApplicationProperties(RuntimeType rt) throws
Exception {
diff --git
a/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/test/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/KubernetesRunTest.java
b/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/test/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/KubernetesRunTest.java
index b01fb78b6337..d1eb6ed0c05c 100644
---
a/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/test/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/KubernetesRunTest.java
+++
b/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/test/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/KubernetesRunTest.java
@@ -28,6 +28,8 @@ import java.util.stream.Stream;
import io.fabric8.kubernetes.api.model.HasMetadata;
import io.fabric8.kubernetes.api.model.apps.Deployment;
+import io.fabric8.kubernetes.api.model.batch.v1.CronJob;
+import io.fabric8.kubernetes.api.model.batch.v1.JobSpec;
import org.apache.camel.RuntimeCamelException;
import org.apache.camel.dsl.jbang.core.commands.CamelJBangMain;
import org.apache.camel.dsl.jbang.core.commands.kubernetes.traits.BaseTrait;
@@ -149,6 +151,46 @@ class KubernetesRunTest extends KubernetesBaseTest {
}
}
+ @ParameterizedTest
+ @MethodSource("runtimeProvider")
+ public void shouldGenerateKubernetesCronjobManifest(RuntimeType rt) throws
Exception {
+ KubernetesRun command = createCommand(List.of("classpath:route.yaml"),
+ "--disable-auto=true", "--image-registry=quay.io",
"--image-group=camel-test", "--output=yaml",
+ "--service-account=my-svc-account", "--runtime=" +
rt.runtime(), "--java-version=17");
+ command.traits = new String[] {
+ "cronjob.enabled=true",
+ "cronjob.schedule=\"0 22 * * 1-5\"",
+ "cronjob.timezone=Europe/Lisbon",
+ "cronjob.startingDeadlineSeconds=2",
+ "cronjob.activeDeadlineSeconds=3",
+ "cronjob.backoffLimit=4",
+ "cronjob.durationMaxIdleSeconds=5",
+ "container.imagePullPolicy=Never"
+ };
+ int exit = command.doCall();
+ Assertions.assertEquals(0, exit);
+
+ var manifest = getKubernetesManifestAsStream(printer.getOutput(),
command.output);
+ List<HasMetadata> resources = kubernetesClient.load(manifest).items();
+ Assertions.assertEquals(2, resources.size());
+
+ CronJob cronjob = resources.stream()
+ .filter(it -> CronJob.class.isAssignableFrom(it.getClass()))
+ .map(CronJob.class::cast)
+ .findFirst()
+ .orElseThrow(() -> new RuntimeCamelException("Missing cronjob
in Kubernetes manifest"));
+
+ JobSpec jobSpec = cronjob.getSpec().getJobTemplate().getSpec();
+ Assertions.assertEquals("route", cronjob.getMetadata().getName());
+ Assertions.assertEquals("0 22 * * 1-5",
cronjob.getSpec().getSchedule());
+ Assertions.assertEquals("Europe/Lisbon",
cronjob.getSpec().getTimeZone());
+ Assertions.assertEquals(2,
cronjob.getSpec().getStartingDeadlineSeconds());
+ Assertions.assertEquals(3, jobSpec.getActiveDeadlineSeconds());
+ Assertions.assertEquals(4, jobSpec.getBackoffLimit());
+ Assertions.assertEquals("Never",
jobSpec.getTemplate().getSpec().getContainers().get(0).getImagePullPolicy());
+ Assertions.assertEquals("my-svc-account",
jobSpec.getTemplate().getSpec().getServiceAccountName());
+ }
+
@ParameterizedTest
@MethodSource("runtimeProvider")
public void shouldHandleUnsupportedOutputFormat(RuntimeType rt) throws
Exception {
diff --git
a/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/test/resources/docker-java.properties
b/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/test/resources/docker-java.properties
new file mode 100644
index 000000000000..54ee16b0d9cc
--- /dev/null
+++
b/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/test/resources/docker-java.properties
@@ -0,0 +1,17 @@
+## ---------------------------------------------------------------------------
+## Licensed to the Apache Software Foundation (ASF) under one or more
+## contributor license agreements. See the NOTICE file distributed with
+## this work for additional information regarding copyright ownership.
+## The ASF licenses this file to You under the Apache License, Version 2.0
+## (the "License"); you may not use this file except in compliance with
+## the License. You may obtain a copy of the License at
+##
+## http://www.apache.org/licenses/LICENSE-2.0
+##
+## Unless required by applicable law or agreed to in writing, software
+## distributed under the License is distributed on an "AS IS" BASIS,
+## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+## See the License for the specific language governing permissions and
+## limitations under the License.
+## ---------------------------------------------------------------------------
+api.version=1.44
\ No newline at end of file