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 47b0792c41b camel-jbang: fix several issues (#19168)
47b0792c41b is described below
commit 47b0792c41b65a4592d6a7cf4bebabf8e0570dfe
Author: Claudio Miranda <[email protected]>
AuthorDate: Tue Sep 16 10:32:55 2025 +0100
camel-jbang: fix several issues (#19168)
CAMEL-22404 camel-jbang-plugin-kubernetes: duplicate declaration of
camel:observability-services in pom.xml
CAMEL-22407 camel-jbang-plugin-kubernetes: --config and --resource fails
when setting a key or destination file path
---
.../modules/ROOT/pages/camel-jbang-kubernetes.adoc | 162 ++++++++++++++++++++-
.../resources/templates/main-kubernetes-pom.tmpl | 5 +-
.../templates/quarkus-kubernetes-pom.tmpl | 4 -
.../templates/spring-boot-kubernetes-pom.tmpl | 4 -
.../core/commands/kubernetes/KubernetesExport.java | 2 +-
.../core/commands/kubernetes/KubernetesRun.java | 108 +++++++++++++-
.../commands/kubernetes/traits/MountTrait.java | 31 ++--
.../commands/kubernetes/traits/TraitHelper.java | 9 ++
.../commands/kubernetes/KubernetesExportTest.java | 155 ++++++++++++++------
.../commands/kubernetes/KubernetesRunTest.java | 26 ++++
.../kubernetes/traits/TraitHelperTest.java | 1 +
.../src/test/resources/my-route-props1.properties | 18 +++
.../src/test/resources/my-route-props2.properties | 18 +++
13 files changed, 478 insertions(+), 65 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 974d61425bc..6f49ff879b8 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-jbang-kubernetes.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-jbang-kubernetes.adoc
@@ -113,7 +113,7 @@ The Camel JBang Kubernetes export command provides several
options to customize
|Maven/Gradle build properties (syntax: --build-property=prop1=foo)
|--property
-|Add a runtime property or properties file from a path, a config map or a
secret (syntax:
[my-key=my-value,file:/path/to/my-conf.properties,[configmap,secret]:name]).
+|Add a runtime property or properties from a file (syntax:
[my-key=my-value|file:/path/to/my-conf.properties,/path/to/my-conf.properties]).
|--config
|Add a runtime configuration from a ConfigMap or a Secret (syntax:
[configmap,secret]:name[/key], where name represents the configmap/secret name
and key optionally represents the configmap/secret key to be filtered).
@@ -1467,6 +1467,166 @@ This automatically removes the Kubernetes deployment
from the cluster on shutdow
NOTE: On macOS hosts, the file watch mechanism is known to be much slower and
less stable compared to using the `--dev` option on other operating systems
like Linux.
This is due to limited native file operations on macOS for Java processes.
+=== Setting properties and resources at runtime
+
+There are several ways to set runtime properties:
+
+==== Properties as command parameters
+
+The simplest way is to set the property as `key`=`value`:
+
+[source,bash]
+----
+camel kubernetes run route.yaml --property my-key=my-val
+----
+
+==== Properties from files
+
+You can also reference an existing properties file, which in this case the
properties content will be read and set in the
`src/main/resources/application.properties` file (in the maven project
generated by the internal export command).
+
+[source,bash]
+----
+camel kubernetes run route.yaml --property /some/directory/file.properties
+----
+
+You can also use the `file:` prefix
+
+[source,bash]
+----
+camel kubernetes run route.yaml --property file:/some/directory/file.properties
+----
+
+==== Properties from ConfigMap and Secret kubernetes resources
+
+The `--config` supports mapping from `ConfigMap` and `Secret` kubernetes
resources and materialize as properties files in the pod. There is a standard
directory path materialized in the pod filesystem:
+
+* `--config configmap:` materialized in
`/etc/camel/conf.d/_configmaps/<configmap name>/<key name>`
+* `--config secret:` materialized in `/etc/camel/conf.d/_secrets/<secret
name>/<key name>`
+
+NOTE: For each `--config` the following properties
`camel.component.properties.location=<path>` and
`camel.component.properties.ignore-missing-location=true` will be set in the
generated application.properties, to allow the mapped properties to be readily
available in the camel runtime with property placeholders `{{my key}}`.
+
+So, given the following `ConfigMap`, you can see there is a key named
`game.properties` whose content is a set of key named values.
+
+[source,bash]
+----
+kind: ConfigMap
+apiVersion: v1
+metadata:
+ name: game-config
+data:
+ game.properties: |
+ my-key=property from SINGLE game.properties
+ enemies.cheat=true
+ secret.code.lives=30
+----
+
+Materialize the content of `ConfigMap/game-config` using the `--config`
parameter:
+
+[source,bash]
+----
+camel kubernetes run route.java --config configmap:game-config
+----
+
+It will set the following mount options in the deployment manifest:
+
+[source,yaml]
+----
+ volumeMounts:
+ - mountPath: /etc/camel/conf.d/_configmaps/game-config
+ name: game-config
+ readOnly: true
+volumes:
+- configMap:
+ name: game-config
+ name: game-config
+----
+
+Then it will materialize the following file in the pod filesystem:
+----
+/etc/camel/conf.d/_configmaps/game-config/game.properties
+----
+
+Another example, with a `ConfigMap` with more key properties:
+
+[source,yaml]
+----
+kind: ConfigMap
+apiVersion: v1
+metadata:
+ name: multiple-config
+data:
+ game.properties: |
+ my-key=property from game.properties
+ secret.code.lives=30
+ ui.properties: |
+ how.nice.to.look=fairlyNice
+----
+
+[source,bash]
+----
+camel kubernetes run route.java --config configmap:multiple-config
+----
+
+It will materialize the files:
+----
+/etc/camel/conf.d/_configmaps/multiple-config/game.properties
+/etc/camel/conf.d/_configmaps/multiple-config/ui.properties
+----
+
+Note that each `ConfigMap` key is the file name.
+
+You can also map specific keys from the `data` section by using the `/<key
name>`:
+
+----
+camel kubernetes run route.java --config configmap:game-config/game.properties
+----
+
+
+==== Mounting generic files from ConfigMap
+
+The `--resource` supports mapping from `ConfigMap` and `Secret` kubernetes
resources and materialize as files in the pod. There is a standard directory
path materialized in the pod filesystem:
+
+* `--resource configmap:` materialized in
`/etc/camel/resources.d/_configmaps/<configmap name>/<key name>`
+* `--resource secret:` materialized in
`/etc/camel/resources.d/_secrets/<secret name>/<key name>`
+
+NOTE: The `--resource` parameter doesn't set the
`camel.component.properties.location=<path>` as the `--config`.
+
+The `--resource` syntax is `[configmap|secret]:name[/key][@path]`, the `key`
and `@path` are optionals. The `@path` allows you to override the materialized
file path in the pod filesystem.
+
+Given the following command:
+[source,bash]
+----
+camel kubernetes run route.java --resource configmap:multiple-config
+----
+
+It will materialize the files, similar to `--config`:
+[source,bash]
+----
+/etc/camel/resources.d/_configmaps/multiple-config/game.properties
+/etc/camel/resources.d/_configmaps/multiple-config/ui.properties
+----
+
+Given the following command:
+
+[source,bash]
+----
+camel kubernetes run route.java --resource
configmap:multiple-config/ui.properties@/etc/my-file.txt
+----
+
+It will materialize the `/etc/my-file.txt` in the pod filesystem whose content
is the `ui.properties` key of the `configmap:multiple-config`.
+[source,bash]
+----
+cat /etc/my-file.txt
+----
+[source,bash]
+----
+color.good=purple
+color.bad=yellow
+allow.textmode=true
+how.nice.to.look=fairlyNice
+my-key=property from ui.properties
+----
+
== Show logs
To inspect the log output of a running deployment call:
diff --git
a/dsl/camel-jbang/camel-jbang-core/src/main/resources/templates/main-kubernetes-pom.tmpl
b/dsl/camel-jbang/camel-jbang-core/src/main/resources/templates/main-kubernetes-pom.tmpl
index 7129d89e402..33df7bae0db 100644
---
a/dsl/camel-jbang/camel-jbang-core/src/main/resources/templates/main-kubernetes-pom.tmpl
+++
b/dsl/camel-jbang/camel-jbang-core/src/main/resources/templates/main-kubernetes-pom.tmpl
@@ -32,10 +32,7 @@
<groupId>org.apache.camel</groupId>
<artifactId>camel-main</artifactId>
</dependency>
- <dependency>
- <groupId>org.apache.camel</groupId>
- <artifactId>camel-observability-services</artifactId>
- </dependency>
+
{{ .CamelDependencies }}
<!-- for logging in color -->
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 34c37511222..8d4f35f28ce 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
@@ -50,10 +50,6 @@
<groupId>org.apache.camel.quarkus</groupId>
<artifactId>camel-quarkus-core</artifactId>
</dependency>
- <dependency>
- <groupId>org.apache.camel.quarkus</groupId>
- <artifactId>camel-quarkus-observability-services</artifactId>
- </dependency>
{{ .CamelDependencies }}
<dependency>
diff --git
a/dsl/camel-jbang/camel-jbang-core/src/main/resources/templates/spring-boot-kubernetes-pom.tmpl
b/dsl/camel-jbang/camel-jbang-core/src/main/resources/templates/spring-boot-kubernetes-pom.tmpl
index a2fe8833b25..d474c206b26 100644
---
a/dsl/camel-jbang/camel-jbang-core/src/main/resources/templates/spring-boot-kubernetes-pom.tmpl
+++
b/dsl/camel-jbang/camel-jbang-core/src/main/resources/templates/spring-boot-kubernetes-pom.tmpl
@@ -55,10 +55,6 @@
<groupId>org.apache.camel.springboot</groupId>
<artifactId>camel-spring-boot-starter</artifactId>
</dependency>
- <dependency>
- <groupId>org.apache.camel.springboot</groupId>
- <artifactId>camel-observability-services-starter</artifactId>
- </dependency>
{{ .CamelDependencies }}
<dependency>
<groupId>org.springframework.boot</groupId>
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 83955df26d5..5bc410fd9fd 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
@@ -513,7 +513,7 @@ public class KubernetesExport extends Export {
}
private void setContainerHealthPaths(int port) {
- // the camel-observability-services artifact is set in the pom template
+ // the camel-observability-services artifact is added as dependency if
observe=true in run command
// it renames the container health base path to /observe, so this has
to be in the container health probes http path
// only quarkus and sb runtimes, because there is no published health
endpoints when using runtime=main
String probePort = port > 0 ? "" + port : "9876";
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 122b7beef6e..7fcd4dcfb46 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
@@ -16,7 +16,9 @@
*/
package org.apache.camel.dsl.jbang.core.commands.kubernetes;
+import java.io.File;
import java.io.FileFilter;
+import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
@@ -24,6 +26,9 @@ import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
import java.util.Stack;
import java.util.concurrent.TimeUnit;
@@ -69,7 +74,7 @@ public class KubernetesRun extends KubernetesBaseCommand {
String serviceAccount;
@CommandLine.Option(names = { "--property" },
- description = "Add a runtime property or properties
file from a path, a config map or a secret (syntax:
[my-key=my-value|file:/path/to/my-conf.properties|[configmap|secret]:name]).")
+ description = "Add a runtime property or properties
from a file (syntax:
[my-key=my-value|file:/path/to/my-conf.properties|/path/to/my-conf.properties].")
String[] properties;
@CommandLine.Option(names = { "--config" },
@@ -315,6 +320,29 @@ public class KubernetesRun extends KubernetesBaseCommand {
RunHelper.dirToFiles(name, files);
}
}
+ // merge the properties from files
+ if (properties != null) {
+ List<String> definiteProperties = new ArrayList<>();
+ for (String p : properties) {
+ if (p.startsWith("file:") || p.contains(File.separator)) {
+ String filename = p.startsWith("file:") ?
p.substring("file:".length()) : p;
+ File f = new File(filename);
+ if (f.exists()) {
+ Properties prop = new Properties();
+ try (FileInputStream input = new FileInputStream(f)) {
+ prop.load(input);
+ }
+ prop.forEach((k, v) -> definiteProperties.add(k + "="
+ v));
+ }
+ } else {
+ definiteProperties.add(p);
+ }
+ }
+ properties = definiteProperties.toArray(new
String[definiteProperties.size()]);
+ }
+ // when user sets configuration or resources from configmap/secret
+ // the projected properties files must be set in the app runtime
+ setPropertiesLocation();
String workingDir = getIndexedWorkingDir(projectName);
KubernetesExport export = configureExport(workingDir, baseDir);
@@ -762,6 +790,84 @@ public class KubernetesRun extends KubernetesBaseCommand {
}
}
+ /*
+ * When a configmap/secret is projected as a properties file and mounted
into the running pod
+ * and the properties file must be set at runtime to be discovered by the
camel runtime
+ * with the camel.component.properties.location property.
+ * This is only for the --config parameter as the --resource are for any
file type, not configurations.
+ */
+ private void setPropertiesLocation() {
+ if (configs != null) {
+ List<String> propertiesLocation = new ArrayList<>();
+ for (String c : configs) {
+ if (c.startsWith("configmap:")) {
+ String name = c.substring("configmap:".length());
+ // in case the user has set a property to filter from the
configmap, the "name" var is
+ // the configmap name and the projected file.
+ if (name.contains("/")) {
+ String rtProperty = String
+
.format("camel.component.properties.location=file:/etc/camel/conf.d/_configmaps/%s",
name);
+ propertiesLocation.add(rtProperty);
+ } else {
+ // we have to inspect the configmap and retrieve the
key names, as they are
+ // mapped to the mounted file names.
+ Set<String> configmapKeys =
retrieveConfigmapKeys(name);
+ configmapKeys.forEach(key -> {
+ String rtProperty = String.format(
+
"camel.component.properties.location=file:/etc/camel/conf.d/_configmaps/%s/%s",
name, key);
+ propertiesLocation.add(rtProperty);
+ });
+ }
+ } else if (c.startsWith("secret:")) {
+ String name = c.substring("secret:".length());
+ if (name.contains("/")) {
+ String rtProperty
+ =
String.format("camel.component.properties.location=file:/etc/camel/conf.d/_secrets/%s",
name);
+ propertiesLocation.add(rtProperty);
+ } else {
+ Set<String> secretKeys = retrieveSecretKeys(name);
+ secretKeys.forEach(key -> {
+ String rtProperty = String.format(
+
"camel.component.properties.location=file:/etc/camel/conf.d/_secrets/%s/%s",
name, key);
+ propertiesLocation.add(rtProperty);
+ });
+ }
+ }
+ }
+ if (propertiesLocation.size() > 0) {
+
propertiesLocation.add("camel.component.properties.ignore-missing-location=true");
+ }
+ if (properties == null) {
+ properties = propertiesLocation.toArray(new
String[propertiesLocation.size()]);
+ } else {
+ for (String s : properties) {
+ propertiesLocation.add(s);
+ }
+ properties = propertiesLocation.toArray(new
String[propertiesLocation.size()]);
+ }
+ }
+ }
+
+ private Set<String> retrieveConfigmapKeys(String name) {
+ KubernetesClient client = client();
+ String ns = "default";
+ if (namespace != null || client.getNamespace() != null) {
+ ns = namespace != null ? namespace : client.getNamespace();
+ }
+ Map<String, String> data =
client.configMaps().inNamespace(ns).withName(name).get().getData();
+ return data.keySet();
+ }
+
+ private Set<String> retrieveSecretKeys(String name) {
+ KubernetesClient client = client();
+ String ns = "default";
+ if (namespace != null || client.getNamespace() != null) {
+ ns = namespace != null ? namespace : client.getNamespace();
+ }
+ Map<String, String> data =
client.secrets().inNamespace(ns).withName(name).get().getData();
+ return data.keySet();
+ }
+
@Override
protected Printer printer() {
if (quiet || output != null) {
diff --git
a/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/main/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/traits/MountTrait.java
b/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/main/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/traits/MountTrait.java
index 900ff594d3f..4cd3532fae5 100644
---
a/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/main/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/traits/MountTrait.java
+++
b/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/main/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/traits/MountTrait.java
@@ -37,6 +37,7 @@ import org.apache.camel.RuntimeCamelException;
import org.apache.camel.dsl.jbang.core.commands.kubernetes.KubernetesHelper;
import org.apache.camel.dsl.jbang.core.commands.kubernetes.traits.model.Mount;
import org.apache.camel.dsl.jbang.core.commands.kubernetes.traits.model.Traits;
+import org.apache.camel.tooling.model.Strings;
import org.apache.camel.util.FileUtil;
import org.apache.camel.util.ObjectHelper;
@@ -123,24 +124,31 @@ public class MountTrait extends BaseTrait {
String volumeName = KubernetesHelper.sanitize(mountResource.name);
String dstDir =
Optional.ofNullable(mountResource.destinationPath).orElse("");
String dstFile;
+ boolean overrideFilePath = false;
if (!dstDir.isEmpty() && !mountResource.key.isEmpty()) {
- dstFile = FileUtil.onlyName(dstDir);
+ dstFile = FileUtil.stripPath(dstDir);
+ overrideFilePath = true;
} else {
dstFile = mountResource.key;
}
Volume vol = getVolume(volumeName, mountResource, dstFile);
boolean readOnly = mountResource.storageType !=
MountResource.StorageType.PVC;
- VolumeMount mnt = getMount(volumeName, getMountPath(mountResource,
dstDir), dstFile, readOnly);
+ VolumeMount mnt = getMount(volumeName, getMountPath(mountResource,
dstDir), dstFile, readOnly, overrideFilePath);
volumes.add(vol);
volumeMounts.add(mnt);
}
- private VolumeMount getMount(String volumeName, String mountPath, String
subPath, boolean readOnly) {
+ private VolumeMount getMount(
+ String volumeName, String mountPath, String subPath, boolean
readOnly, boolean overrideFilePath) {
+ String adjustedMountPath = mountPath + "/" + subPath;
+ if (ObjectHelper.isEmpty(subPath) || overrideFilePath) {
+ adjustedMountPath = mountPath;
+ }
VolumeMountBuilder mount = new VolumeMountBuilder()
.withName(volumeName)
- .withMountPath(mountPath)
+ .withMountPath(adjustedMountPath)
.withReadOnly(readOnly);
if (ObjectHelper.isNotEmpty(subPath)) {
@@ -232,14 +240,19 @@ public class MountTrait extends BaseTrait {
private MountResource createConfig(
String expression, MountResource.StorageType storageType,
MountResource.ContentType contentType) {
+ String name = expression;
+ String key = "";
+ String destPath = "";
Matcher resourceCoordinates =
RESOURCE_VALUE_EXPRESSION.matcher(expression);
if (resourceCoordinates.matches()) {
- return new MountResource(
- storageType, contentType, resourceCoordinates.group(0),
resourceCoordinates.group(2),
- resourceCoordinates.group(4));
- } else {
- return new MountResource(storageType, contentType, expression, "",
"");
+ name = resourceCoordinates.group(1);
+ key = resourceCoordinates.group(3);
+ destPath = resourceCoordinates.group(4);
+ if (!Strings.isNullOrEmpty(destPath)) {
+ destPath = destPath.replace("@", "");
+ }
}
+ return new MountResource(storageType, contentType, name, key,
destPath);
}
private record MountResource(StorageType storageType, ContentType
contentType, String name, String key,
diff --git
a/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/main/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/traits/TraitHelper.java
b/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/main/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/traits/TraitHelper.java
index 990c6749712..5f34b738cc9 100644
---
a/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/main/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/traits/TraitHelper.java
+++
b/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/main/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/traits/TraitHelper.java
@@ -69,9 +69,18 @@ public final class TraitHelper {
Map<String, Map<String, Object>> traitConfigMap = new HashMap<>();
for (String traitExpression : traits) {
+ // as the properties are merged to the traits, it may contain some
property like:
+ // my-key=my-val which is invalid as a trait expression
+ // so it may be skipped
+ if (!traitExpression.contains(".")) {
+ continue;
+ }
//traitName.key=value
final String[] trait = traitExpression.split("\\.", 2);
final String[] traitConfig = trait[1].split("=", 2);
+ if (traitConfig == null || traitConfig.length < 2) {
+ continue;
+ }
// the CRD api is in CamelCase, then we have to
// convert the kebab-case to CamelCase
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 51f21a05b5e..3cd8fea5966 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
@@ -24,6 +24,8 @@ import java.util.stream.Stream;
import io.fabric8.kubernetes.api.model.Container;
import io.fabric8.kubernetes.api.model.Service;
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.networking.v1.Ingress;
import io.fabric8.openshift.api.model.Route;
@@ -32,6 +34,7 @@ import org.apache.camel.dsl.jbang.core.common.RuntimeType;
import org.apache.camel.util.IOHelper;
import org.apache.maven.model.Model;
import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.DisabledIfSystemProperty;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
@@ -599,52 +602,125 @@ class KubernetesExportTest extends
KubernetesExportBaseTest {
Assertions.assertEquals("bar", labels.get("foo"));
}
- @ParameterizedTest
- @MethodSource("runtimeProvider")
- public void shouldAddConfigs(RuntimeType rt) throws Exception {
- KubernetesExport command = createCommand(new String[] {
"classpath:route.yaml" }, "--runtime=" + rt.runtime());
- command.configs = new String[] { "secret:foo", "configmap:bar" };
+ @Test
+ public void shouldAddConfigs() throws Exception {
+ KubernetesExport command = createCommand(new String[] {
"classpath:route.yaml" }, "--runtime=" + RuntimeType.main);
+ command.configs = new String[] {
+ "secret:foo", "secret:foo/key-foo", "configmap:bar",
"configmap:bar/key-bar", "configmap:bar2/my.properties" };
var exit = command.doCall();
Assertions.assertEquals(0, exit);
- Deployment deployment = getDeployment(rt);
+ Deployment deployment = getDeployment(RuntimeType.main);
+ List<Volume> volumes =
deployment.getSpec().getTemplate().getSpec().getVolumes();
+ List<VolumeMount> volumeMounts =
deployment.getSpec().getTemplate().getSpec().getContainers().get(0).getVolumeMounts();
+
Assertions.assertEquals("route", deployment.getMetadata().getName());
Assertions.assertEquals(1,
deployment.getSpec().getTemplate().getSpec().getContainers().size());
- Assertions.assertEquals(2,
-
deployment.getSpec().getTemplate().getSpec().getContainers().get(0).getVolumeMounts().size());
- Assertions.assertEquals("foo",
-
deployment.getSpec().getTemplate().getSpec().getContainers().get(0).getVolumeMounts().get(0).getName());
- Assertions.assertEquals("/etc/camel/conf.d/_secrets/foo",
-
deployment.getSpec().getTemplate().getSpec().getContainers().get(0).getVolumeMounts().get(0).getMountPath());
- Assertions.assertTrue(
-
deployment.getSpec().getTemplate().getSpec().getContainers().get(0).getVolumeMounts().get(0).getReadOnly());
- Assertions.assertEquals("bar",
-
deployment.getSpec().getTemplate().getSpec().getContainers().get(0).getVolumeMounts().get(1).getName());
- Assertions.assertEquals("/etc/camel/conf.d/_configmaps/bar",
-
deployment.getSpec().getTemplate().getSpec().getContainers().get(0).getVolumeMounts().get(1).getMountPath());
- Assertions.assertTrue(
-
deployment.getSpec().getTemplate().getSpec().getContainers().get(0).getVolumeMounts().get(1).getReadOnly());
+ Assertions.assertEquals(5, volumeMounts.size());
+ // secret:foo
+ Assertions.assertEquals("foo", volumeMounts.get(0).getName());
+ Assertions.assertEquals("/etc/camel/conf.d/_secrets/foo",
volumeMounts.get(0).getMountPath());
+ Assertions.assertTrue(volumeMounts.get(0).getReadOnly());
+ // secret:foo/key-foo
+ Assertions.assertEquals("foo", volumeMounts.get(1).getName());
+ Assertions.assertEquals("/etc/camel/conf.d/_secrets/foo/key-foo",
volumeMounts.get(1).getMountPath());
+ Assertions.assertTrue(volumeMounts.get(1).getReadOnly());
+ Assertions.assertEquals("key-foo",
volumes.get(1).getSecret().getItems().get(0).getKey());
+ Assertions.assertEquals("key-foo",
volumes.get(1).getSecret().getItems().get(0).getPath());
+ // configmap:bar
+ Assertions.assertEquals("bar", volumeMounts.get(2).getName());
+ Assertions.assertEquals("/etc/camel/conf.d/_configmaps/bar",
volumeMounts.get(2).getMountPath());
+ Assertions.assertTrue(volumeMounts.get(2).getReadOnly());
+ // configmap:bar/key-bar
+ Assertions.assertEquals("bar", volumeMounts.get(3).getName());
+ Assertions.assertEquals("/etc/camel/conf.d/_configmaps/bar/key-bar",
volumeMounts.get(3).getMountPath());
+ Assertions.assertTrue(volumeMounts.get(3).getReadOnly());
+ Assertions.assertEquals("key-bar",
volumes.get(3).getConfigMap().getItems().get(0).getKey());
+ Assertions.assertEquals("key-bar",
volumes.get(3).getConfigMap().getItems().get(0).getPath());
+ // configmap:bar2/my.properties
+ Assertions.assertEquals("bar2", volumeMounts.get(4).getName());
+
Assertions.assertEquals("/etc/camel/conf.d/_configmaps/bar2/my.properties",
volumeMounts.get(4).getMountPath());
+ Assertions.assertEquals("my.properties",
volumes.get(4).getConfigMap().getItems().get(0).getKey());
+ Assertions.assertEquals("my.properties",
volumes.get(4).getConfigMap().getItems().get(0).getPath());
}
- @ParameterizedTest
- @MethodSource("runtimeProvider")
- public void shouldAddResources(RuntimeType rt) throws Exception {
- KubernetesExport command = createCommand(new String[] {
"classpath:route.yaml" }, "--runtime=" + rt.runtime());
- command.resources = new String[] { "configmap:foo/file.txt" };
+ @Test
+ public void shouldAddResources() throws Exception {
+ KubernetesExport command = createCommand(new String[] {
"classpath:route.yaml" }, "--runtime=" + RuntimeType.main);
+ command.resources = new String[] {
+ "secret:foo", "secret:foo/key-foo",
"secret:foo/key-foo@/etc/foodir/my-file.txt", "configmap:bar",
+ "configmap:bar/key-bar",
"configmap:bar2/my.properties@/var/dir1/bar.bin" };
var exit = command.doCall();
Assertions.assertEquals(0, exit);
- Deployment deployment = getDeployment(rt);
+ Deployment deployment = getDeployment(RuntimeType.main);
+ List<Volume> volumes =
deployment.getSpec().getTemplate().getSpec().getVolumes();
+ List<VolumeMount> volumeMounts =
deployment.getSpec().getTemplate().getSpec().getContainers().get(0).getVolumeMounts();
+
Assertions.assertEquals("route", deployment.getMetadata().getName());
Assertions.assertEquals(1,
deployment.getSpec().getTemplate().getSpec().getContainers().size());
- Assertions.assertEquals(1,
-
deployment.getSpec().getTemplate().getSpec().getContainers().get(0).getVolumeMounts().size());
- Assertions.assertEquals("file",
-
deployment.getSpec().getTemplate().getSpec().getContainers().get(0).getVolumeMounts().get(0).getName());
-
Assertions.assertEquals("/etc/camel/resources.d/_configmaps/foo/file.txt",
-
deployment.getSpec().getTemplate().getSpec().getContainers().get(0).getVolumeMounts().get(0).getMountPath());
- Assertions.assertEquals("/file.txt",
-
deployment.getSpec().getTemplate().getSpec().getContainers().get(0).getVolumeMounts().get(0).getSubPath());
+ Assertions.assertEquals(6, volumeMounts.size());
+ // secret:foo
+ Assertions.assertEquals("foo", volumeMounts.get(0).getName());
+ Assertions.assertEquals("/etc/camel/resources.d/_secrets/foo",
volumeMounts.get(0).getMountPath());
+ Assertions.assertTrue(volumeMounts.get(0).getReadOnly());
+ // secret:foo/key-foo
+ Assertions.assertEquals("foo", volumeMounts.get(1).getName());
+ Assertions.assertEquals("/etc/camel/resources.d/_secrets/foo/key-foo",
volumeMounts.get(1).getMountPath());
+ Assertions.assertEquals("key-foo",
volumes.get(1).getSecret().getItems().get(0).getKey());
+ Assertions.assertEquals("key-foo",
volumes.get(1).getSecret().getItems().get(0).getPath());
+ // secret:foo/key-foo@/etc/foodir/my-file.txt
+ Assertions.assertEquals("foo", volumeMounts.get(2).getName());
+ Assertions.assertEquals("/etc/foodir/my-file.txt",
volumeMounts.get(2).getMountPath());
+ Assertions.assertEquals("my-file.txt",
volumeMounts.get(2).getSubPath());
+ Assertions.assertEquals("key-foo",
volumes.get(2).getSecret().getItems().get(0).getKey());
+ Assertions.assertEquals("my-file.txt",
volumes.get(2).getSecret().getItems().get(0).getPath());
+ // configmap:bar
+ Assertions.assertEquals("bar", volumeMounts.get(3).getName());
+ Assertions.assertEquals("/etc/camel/resources.d/_configmaps/bar",
volumeMounts.get(3).getMountPath());
+ // configmap:bar/key-bar
+ Assertions.assertEquals("bar", volumeMounts.get(4).getName());
+
Assertions.assertEquals("/etc/camel/resources.d/_configmaps/bar/key-bar",
volumeMounts.get(4).getMountPath());
+ Assertions.assertEquals("key-bar",
volumes.get(4).getConfigMap().getItems().get(0).getKey());
+ Assertions.assertEquals("key-bar",
volumes.get(4).getConfigMap().getItems().get(0).getPath());
+ // configmap:bar2/my.properties@/var/dir1/bar.bin
+ Assertions.assertEquals("bar2", volumeMounts.get(5).getName());
+ Assertions.assertEquals("/var/dir1/bar.bin",
volumeMounts.get(5).getMountPath());
+ Assertions.assertEquals("bar.bin", volumeMounts.get(5).getSubPath());
+ Assertions.assertEquals("my.properties",
volumes.get(5).getConfigMap().getItems().get(0).getKey());
+ Assertions.assertEquals("bar.bin",
volumes.get(5).getConfigMap().getItems().get(0).getPath());
+ }
+
+ @Test
+ public void shouldAddConfigAndResources() throws Exception {
+ KubernetesExport command = createCommand(new String[] {
"classpath:route.yaml" }, "--runtime=" + RuntimeType.main);
+ command.configs = new String[] { "configmap:bar1a/my.key1" };
+ command.resources = new String[] { "configmap:bar2/key-bar2",
"configmap:bar2a/my.key2@/var/dir2/bar.bin" };
+ var exit = command.doCall();
+ Assertions.assertEquals(0, exit);
+
+ Deployment deployment = getDeployment(RuntimeType.main);
+ List<Volume> volumes =
deployment.getSpec().getTemplate().getSpec().getVolumes();
+ List<VolumeMount> volumeMounts =
deployment.getSpec().getTemplate().getSpec().getContainers().get(0).getVolumeMounts();
+
+ // config configmap:bar1a/my.key1
+ Assertions.assertEquals("bar1a", volumeMounts.get(0).getName());
+ Assertions.assertEquals("/etc/camel/conf.d/_configmaps/bar1a/my.key1",
volumeMounts.get(0).getMountPath());
+ Assertions.assertEquals("my.key1", volumeMounts.get(0).getSubPath());
+ Assertions.assertEquals("my.key1",
volumes.get(0).getConfigMap().getItems().get(0).getKey());
+ Assertions.assertEquals("my.key1",
volumes.get(0).getConfigMap().getItems().get(0).getPath());
+ // resources configmap:bar2/key-bar2
+ Assertions.assertEquals("bar2", volumeMounts.get(1).getName());
+
Assertions.assertEquals("/etc/camel/resources.d/_configmaps/bar2/key-bar2",
volumeMounts.get(1).getMountPath());
+ Assertions.assertEquals("key-bar2", volumeMounts.get(1).getSubPath());
+ Assertions.assertEquals("key-bar2",
volumes.get(1).getConfigMap().getItems().get(0).getKey());
+ Assertions.assertEquals("key-bar2",
volumes.get(1).getConfigMap().getItems().get(0).getPath());
+ // resources configmap:bar2a/my.key2@/var/dir2/bar.bin
+ Assertions.assertEquals("bar2a", volumeMounts.get(2).getName());
+ Assertions.assertEquals("/var/dir2/bar.bin",
volumeMounts.get(2).getMountPath());
+ Assertions.assertEquals("bar.bin", volumeMounts.get(2).getSubPath());
+ Assertions.assertEquals("my.key2",
volumes.get(2).getConfigMap().getItems().get(0).getKey());
+ Assertions.assertEquals("bar.bin",
volumes.get(2).getConfigMap().getItems().get(0).getPath());
}
@ParameterizedTest
@@ -661,11 +737,11 @@ class KubernetesExportTest extends
KubernetesExportBaseTest {
Assertions.assertEquals(1,
deployment.getSpec().getTemplate().getSpec().getContainers().size());
Assertions.assertEquals(1,
deployment.getSpec().getTemplate().getSpec().getContainers().get(0).getVolumeMounts().size());
- Assertions.assertEquals("spec",
+ Assertions.assertEquals("openapi",
deployment.getSpec().getTemplate().getSpec().getContainers().get(0).getVolumeMounts().get(0).getName());
Assertions.assertEquals("/etc/camel/resources.d/_configmaps/openapi/spec.yaml",
deployment.getSpec().getTemplate().getSpec().getContainers().get(0).getVolumeMounts().get(0).getMountPath());
- Assertions.assertEquals("/spec.yaml",
+ Assertions.assertEquals("spec.yaml",
deployment.getSpec().getTemplate().getSpec().getContainers().get(0).getVolumeMounts().get(0).getSubPath());
}
@@ -741,7 +817,7 @@ class KubernetesExportTest extends KubernetesExportBaseTest
{
@MethodSource("runtimeProvider")
public void shouldObserveByDefault(RuntimeType rt) throws Exception {
KubernetesExport command = createCommand(new String[] {
"classpath:route.yaml" },
- "--gav=examples:route:1.0.0", "--runtime=" + rt.runtime());
+ "--observe", "--gav=examples:route:1.0.0", "--runtime=" +
rt.runtime());
int exit = command.doCall();
Assertions.assertEquals(0, exit);
@@ -750,10 +826,7 @@ class KubernetesExportTest extends
KubernetesExportBaseTest {
Assertions.assertEquals("route", model.getArtifactId());
Assertions.assertEquals("1.0.0", model.getVersion());
- if (rt == RuntimeType.main) {
- Assertions.assertTrue(
- containsDependency(model.getDependencies(),
"org.apache.camel", "camel-observability-services", null));
- } else if (rt == RuntimeType.springBoot) {
+ if (rt == RuntimeType.springBoot) {
Assertions.assertTrue(
containsDependency(model.getDependencies(),
"org.apache.camel.springboot",
"camel-observability-services-starter", null));
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 97b8d1904e0..8cd779bd2e4 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
@@ -17,10 +17,13 @@
package org.apache.camel.dsl.jbang.core.commands.kubernetes;
+import java.io.File;
+import java.io.FileInputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
+import java.util.Properties;
import java.util.stream.Stream;
import io.fabric8.kubernetes.api.model.HasMetadata;
@@ -33,6 +36,7 @@ import org.apache.camel.dsl.jbang.core.common.StringPrinter;
import org.apache.camel.dsl.jbang.core.common.VersionHelper;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.DisabledIfSystemProperty;
import org.junit.jupiter.api.condition.EnabledIf;
import org.junit.jupiter.params.ParameterizedTest;
@@ -74,6 +78,28 @@ class KubernetesRunTest extends KubernetesBaseTest {
Assertions.assertTrue(printer.getOutput().contains("ERROR: Project
export failed!"));
}
+ @Test
+ public void verifyProperties() throws Exception {
+ KubernetesRun command = createCommand(List.of("classpath:route.yaml"),
+ "--gav=examples:route:1.0.0", "--runtime=quarkus",
"--name=my-route-props",
+ "--disable-auto=true", "--image-registry=quay.io",
"--image-group=camel-test", "--output=yaml",
+ "--property=a=b", "--property=c=d",
"--property=src/test/resources/my-route-props1.properties",
+
"--property=file:src/test/resources/my-route-props2.properties");
+ int exit = command.doCall();
+ Assertions.assertEquals(0, exit);
+
+ Properties materializedProps = new Properties();
+ String propsFilepath =
".camel-jbang-run/my-route-props/src/main/resources/application.properties";
+ try (FileInputStream input = new FileInputStream(new
File(propsFilepath))) {
+ materializedProps.load(input);
+ }
+
+ Assertions.assertEquals("b", materializedProps.get("a"));
+ Assertions.assertEquals("d", materializedProps.get("c"));
+ Assertions.assertEquals("v1", materializedProps.get("k1"));
+ Assertions.assertEquals("v2", materializedProps.get("k2"));
+ }
+
@ParameterizedTest
@MethodSource("runtimeProvider")
public void shouldGenerateKubernetesManifest(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/traits/TraitHelperTest.java
b/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/test/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/traits/TraitHelperTest.java
index 7b773bc7aa4..a751290e1bc 100644
---
a/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/test/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/traits/TraitHelperTest.java
+++
b/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/test/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/traits/TraitHelperTest.java
@@ -92,6 +92,7 @@ public class TraitHelperTest {
@Test
public void parseTraitsTest() {
String[] traits = new String[] {
+ "my-prop=my-val",
"custom.property=custom",
"container.port=8080",
"container.port-name=custom" };
diff --git
a/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/test/resources/my-route-props1.properties
b/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/test/resources/my-route-props1.properties
new file mode 100644
index 00000000000..ffdb308786f
--- /dev/null
+++
b/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/test/resources/my-route-props1.properties
@@ -0,0 +1,18 @@
+## ---------------------------------------------------------------------------
+## 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.
+## ---------------------------------------------------------------------------
+
+k1=v1
diff --git
a/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/test/resources/my-route-props2.properties
b/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/test/resources/my-route-props2.properties
new file mode 100644
index 00000000000..5b5636e369a
--- /dev/null
+++
b/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/test/resources/my-route-props2.properties
@@ -0,0 +1,18 @@
+## ---------------------------------------------------------------------------
+## 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.
+## ---------------------------------------------------------------------------
+
+k2=v2