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


Reply via email to