This is an automated email from the ASF dual-hosted git repository.

pcongiusti pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel-k.git


The following commit(s) were added to refs/heads/main by this push:
     new ce2d16e14 feat(trait): cross namespace Kamelets
ce2d16e14 is described below

commit ce2d16e14bde4884e58125536e08cb4f4fa7657c
Author: Pasquale Congiusti <[email protected]>
AuthorDate: Sat Oct 11 09:17:53 2025 +0200

    feat(trait): cross namespace Kamelets
    
    Allow the Integration to run any Kamelet available in any namespace
---
 docs/modules/ROOT/nav.adoc                         |   6 +-
 .../kamelets.adoc => kamelets/architecture.adoc}   |   6 +-
 .../modules/ROOT/pages/kamelets/configuration.adoc | 247 +++++++++++++++++++++
 ...amelets-distribution.adoc => distribution.adoc} |   8 +-
 .../modules/ROOT/pages/kamelets/kamelets-user.adoc | 191 ----------------
 docs/modules/ROOT/pages/kamelets/kamelets.adoc     |   2 +-
 .../ROOT/pages/troubleshooting/debugging.adoc      |   9 -
 e2e/common/traits/files/kamelet-it-ns.yaml         |  24 ++
 .../traits/files/my-timer-source-ns.kamelet.yaml   |  36 +++
 e2e/common/traits/kamelet_test.go                  |  52 ++++-
 pkg/apis/camel/v1/kamelet_types.go                 |   6 +-
 pkg/trait/kamelets.go                              | 102 ++++++---
 pkg/trait/kamelets_test.go                         | 159 ++++++++++++-
 pkg/util/source/kamelet.go                         |  31 ++-
 14 files changed, 628 insertions(+), 251 deletions(-)

diff --git a/docs/modules/ROOT/nav.adoc b/docs/modules/ROOT/nav.adoc
index b9c649026..ee3ec7c4b 100644
--- a/docs/modules/ROOT/nav.adoc
+++ b/docs/modules/ROOT/nav.adoc
@@ -80,8 +80,9 @@
 ** xref:traits:toleration.adoc[Toleration]
 // End of autogenerated code - DO NOT EDIT! (trait-nav)
 * xref:kamelets/kamelets.adoc[Kamelets]
-** xref:kamelets/kamelets-distribution.adoc[Distribution]
-** xref:kamelets/kamelets-user.adoc[Configuration]
+** xref:kamelets/architecture.adoc[Architecture]
+** xref:kamelets/distribution.adoc[Distribution]
+** xref:kamelets/configuration.adoc[Configuration]
 * xref:pipeline/pipeline.adoc[Pipelines]
 ** xref:pipeline/external.adoc[External CICD]
 * Scaling
@@ -106,7 +107,6 @@
 *** xref:architecture/cr/camel-catalog.adoc[CamelCatalog]
 ** xref:architecture/runtime.adoc[Runtime]
 ** xref:architecture/traits.adoc[Traits]
-** xref:architecture/kamelets.adoc[Kamelets]
 ** xref:architecture/incremental-image.adoc[Incremental Image]
 * API
 ** xref:apis/camel-k.adoc[Camel K v1]
diff --git a/docs/modules/ROOT/pages/architecture/kamelets.adoc 
b/docs/modules/ROOT/pages/kamelets/architecture.adoc
similarity index 65%
rename from docs/modules/ROOT/pages/architecture/kamelets.adoc
rename to docs/modules/ROOT/pages/kamelets/architecture.adoc
index 6efa7cd8d..1561ad402 100644
--- a/docs/modules/ROOT/pages/architecture/kamelets.adoc
+++ b/docs/modules/ROOT/pages/kamelets/architecture.adoc
@@ -1,8 +1,8 @@
 = Kamelets architecture in Camel K
 
-xref:kamelets/kamelets.adoc[Kamelets] are a technology which were originally 
developed as a Camel K side resource but moved into Camel framework as Kamelet 
component. From an design point of view, a Kamelet is a specification that is 
provided into the cluster and which can be used at any point by an Integration 
or a Pipe, in order to reuse the connector style approach.
+Kamelets are a technology which were originally developed as a Camel K side 
resource but moved into Camel framework as Kamelet component. From an design 
point of view, a Kamelet is a specification that is provided into the cluster 
and which can be used at any point by an Integration or a Pipe, in order to 
reuse the connector style approach.
 
-In Camel framework, a Kamelet is nothing than a component which can be used as 
any other component with the `kamelet` uri scheme. This is translated to one or 
more Route Templates. What's important for Camel runtime is to have the Kamelet 
spec available somewhere when running the application making reference to it.
+In Camel framework, a Kamelet is a component which can be used as any other 
component with the `kamelet` uri scheme. This is translated to one or more 
Route Templates. What's important for Camel runtime is to have the Kamelet spec 
available somewhere when running the application making reference to it (by 
default in any `/kamelet` folder available in the classpath).
 
 [[deployment-model]]
 == Deployment model
@@ -15,6 +15,8 @@ The operator creates a ConfigMap in order to bundle all the 
Kamelets which are e
 
 NOTE: as the Configmap resource is limited to 1 MiB, the operator may split 
into more than a single Configmap bundle.
 
+In resume, the operator is responsible to pick the Kamelet spec available in 
the cluster and transform into a format that the runtime will be able to 
execute.
+
 [[kamelet-parsing]]
 === Parsing capabilities defined in a Kamelet
 
diff --git a/docs/modules/ROOT/pages/kamelets/configuration.adoc 
b/docs/modules/ROOT/pages/kamelets/configuration.adoc
new file mode 100644
index 000000000..7e1b866d9
--- /dev/null
+++ b/docs/modules/ROOT/pages/kamelets/configuration.adoc
@@ -0,0 +1,247 @@
+= How to configure a Kamelet
+
+Speaking technically, a Kamelet is a resource that can be installed on any 
Kubernetes cluster or used as a plain yaml configuration in Apache Camel 
runtimes. The following is an example of a Kamelet source which can be 
programmed to trigger events with a `timer`. This is coming directly from 
Apache Camel Kamelets catalog:
+
+.timer-source.kamelet.yaml
+[source,yaml]
+----
+apiVersion: camel.apache.org/v1
+kind: Kamelet
+metadata:
+  name: timer-source
+  annotations:
+    camel.apache.org/kamelet.support.level: "Stable"
+    camel.apache.org/catalog.version: "4.16.0-SNAPSHOT"
+    camel.apache.org/kamelet.icon: 
data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4w[...]
+    camel.apache.org/provider: "Apache Software Foundation"
+    camel.apache.org/kamelet.group: "Timer"
+    camel.apache.org/kamelet.namespace: "Scheduling"
+  labels:
+    camel.apache.org/kamelet.type: "source"
+    camel.apache.org/kamelet.verified: "true"
+spec:
+  definition:
+    title: "Timer Source"
+    description: Produces periodic messages with a custom payload.
+    required:
+      - message
+    type: object
+    properties:
+      period:
+        title: Period
+        description: "The interval (in milliseconds) to wait between producing 
the next message."
+        type: integer
+        default: 1000
+      message:
+        title: Message
+        description: The message to generate.
+        type: string
+        example: hello world
+      contentType:
+        title: Content Type
+        description: The content type of the generated message.
+        type: string
+        default: text/plain
+      repeatCount:
+        title: Repeat Count
+        description: Specifies a maximum limit of number of fires
+        type: integer
+  dependencies:
+    - "camel:core"
+    - "camel:timer"
+    - "camel:kamelet"
+  template:
+    from:
+      uri: timer:tick
+      parameters:
+        period: "{{period}}"
+        repeatCount: "{{?repeatCount}}"
+      steps:
+        - setBody:
+            constant: "{{message}}"
+        - setHeader:
+            name: "Content-Type"
+            constant: "{{contentType}}"
+        - to: kamelet:sink
+----
+
+From a user perspective it's important to understand the parameter exposed 
(`.spec.definition.properties`) and the execution flow (`.spec.template`).
+
+The flow defines the business logic and generally use the parameter that you, 
as a user have to provide in your integration.
+
+The Kamelet can be installed on the cluster as a Kamelet custom resource, and 
the operator is already in charge to preinstall the Kamelets coming from the 
Apache Camel Kamelets bundled catalog. If you create your own Kamelet you can 
install it on the cluster in the following way:
+
+[source,shell]
+----
+kubectl apply -f timer-source.kamelet.yaml
+----
+
+Kamelets are standard YAML files, but their common extension is 
`.kamelet.yaml` to help IDEs to recognize them and possibly provide 
auto-completion.
+
+[[kamelets-usage-integration]]
+== Using Kamelets in Integrations
+
+Kamelets can be used in integrations **as if they were standard Camel 
components**. For example, suppose that you've created the `timer-source` 
Kamelet in the `default` namespace on Kubernetes, then you can write the 
following integration to use the Kamelet:
+
+[source,yaml]
+.kamlet-route.yaml
+----
+- from:
+    uri: "kamelet:timer-source?message=Hello!"
+    steps:
+      - to: "log:info"
+----
+
+NOTE: URI properties ("message") match the corresponding parameters in the 
Kamelet definition.
+
+Kamelets can also be used multiple times in the same route definition. This 
happens usually with sink Kamelets. Suppose that you've defined a Kamelet named 
"my-company-log-sink" in your Kubernetes namespace, then you can write a route 
like this:
+
+[source,yaml]
+.kamlet-multi-route.yaml
+----
+- from:
+    uri: "kamelet:timer-source?message=Hello!"
+    steps:
+      - to: "kamelet:my-company-log-sink?bucket=general"
+      - filter:
+          simple: '${body} contains "Camel"'
+      - to: "kamelet:my-company-log-sink?bucket=special"
+----
+
+The "my-company-log-sink" will obviously define what it means to write a log 
in the enterprise system and what is concretely a "bucket".
+
+[[kamelets-usage-pipe]]
+== Binding Kamelets into a Pipe
+
+Kamelets are the main reason why we have introduced the `Pipe` custom 
resource. They provide a very easy connector style approach as, each Kamelet, 
can be defined naturally as an event source or event sink. Usage of a Kamelet 
from a Pipe would be like:
+
+[source,yaml]
+.pipe.yaml
+----
+apiVersion: camel.apache.org/v1
+kind: Pipe
+metadata:
+  name: timer-to-log
+spec:
+  source:
+    ref:
+      kind: Kamelet
+      apiVersion: camel.apache.org/v1
+      name: timer-source
+    properties:
+      message: Hello pipe!
+  sink:
+    ref:
+      kind: Kamelet
+      apiVersion: camel.apache.org/v1
+      name: log-sink
+----
+
+=== Configuration
+
+When using a Kamelet, the instance parameters (e.g. "message", "bucket") can 
be passed explicitly in the URI or you can use Camel properties. For example:
+
+[source,yaml]
+.kamlet-route.yaml
+----
+- from:
+    uri: "kamelet:timer-source?message={{my-message}}"
+    steps:
+      - to: "log:info"
+----
+
+The application should run with Camel properties set, such as 
`my-message=Hello!`.
+
+=== Kamelet versioning
+
+Kamelets provided in a catalog are generally meant to work with a given 
runtime version (the same for which they are released). However, when you 
create a Kamelet and publish to a cluster, you may want to store and use 
different versions. If the Kamelet is provided with more than the `main` 
version, then, you can specify which version to use in your Integration by 
adding the version parameter.
+
+NOTE: multiple version Kamelets is an exclusive feature of Camel K.
+
+For instance, take the following multi version Kamelet:
+
+[source,yaml]
+.kamlet-namedconfig-route.yaml
+----
+apiVersion: camel.apache.org/v1
+kind: Kamelet
+metadata:
+  name: my-source
+  labels:
+    camel.apache.org/kamelet.type: "source"
+spec:
+  definition:
+    title: "Timer Example"
+    description: "Emit Kamelet Main body"
+  types:
+    out:
+      mediaType: text/plain
+  template:
+    from:
+      uri: timer:tick
+      steps:
+        - setBody:
+            constant: "Kamelet Main"
+        - to: "kamelet:sink"
+  versions:
+    v1:
+      definition:
+        title: "Timer Example 1"
+        description: "Emit Kamelet V1 body"
+      types:
+        out:
+          mediaType: text/plain
+      template:
+        from:
+          uri: timer:tick
+          steps:
+            - setBody:
+                constant: "Kamelet V1"
+            - to: "kamelet:sink"
+    v2:
+      definition:
+        title: "Timer Example 2"
+        description: "Emit Kamelet V2 body"
+      types:
+        out:
+          mediaType: text/plain
+      template:
+        from:
+          uri: timer:tick
+          steps:
+            - setBody:
+                constant: "Kamelet V2"
+            - to: "kamelet:sink"
+----
+
+You can see it specifies the `main` specification, which it is the default to 
use. It also specifies two additional versions, `v1` and `v2`. This is nice if 
you want to provide versioning changes maintaining the same resource. When you 
use it, you will therefore need to specify the version you want to adopt by 
setting the `kameletVersion` parameter. For example, you want to use the `v2` 
version:
+
+[source,yaml]
+.kamlet-namedconfig-route.yaml
+----
+- from:
+    uri: "kamelet:my-source?kameletVersion=v2"
+    steps:
+      - to: "log:info"
+----
+
+The operator will be able to automatically pick the right version and use it 
at runtime. If no version is specified, then you will use the default one.
+
+=== Kamelet namespace
+
+A Kamelet can be installed in any cluster namespace. By default, the operator 
will expect the Kamelet to be in the same namespace of the Integration (or 
Pipe), the operator namespace (where the bundled kamelets are stored) or any 
other repository defined in the `IntegrationPlatform`. If you want to use a 
Kamelet stored in another namespace, you will need to use the 
`kameletNamespace` parameter. For example, say you have a dedicated namespace 
called `kamelets` where you're installing your [...]
+
+NOTE: namespace Kamelets is an exclusive feature of Camel K.
+
+Now, you can instruct the operator to find the Kamelet you're using into such 
namespace, for example:
+
+[source,yaml]
+.kamlet-namedconfig-route.yaml
+----
+- from:
+    uri: "kamelet:my-source?kameletNamespace=kamelets"
+    steps:
+      - to: "log:info"
+----
+
+The operator will be therefore loading the Kamelets from that namespace, 
unless the same Kamelet exists in the same Integration namespace or the 
operator namespace.
diff --git a/docs/modules/ROOT/pages/kamelets/kamelets-distribution.adoc 
b/docs/modules/ROOT/pages/kamelets/distribution.adoc
similarity index 77%
rename from docs/modules/ROOT/pages/kamelets/kamelets-distribution.adoc
rename to docs/modules/ROOT/pages/kamelets/distribution.adoc
index a2e9f0e5e..deeadee93 100644
--- a/docs/modules/ROOT/pages/kamelets/kamelets-distribution.adoc
+++ b/docs/modules/ROOT/pages/kamelets/distribution.adoc
@@ -7,6 +7,12 @@ NOTE: the version we bundle depends directly on the default 
Camel version used.
 
 As development of Kamelet is very fast, make sure to use some version which is 
compatible with the Camel runtime you're going to use.
 
+== Release Kamelets on the cluster
+
+Since Kamelets are Kubernetes custom resources, the Camel K opinionated way of 
Kamelets distribution is to expect them available on the cluster. You tipically 
develop a Kamelet and then release it in the Integration namespace or the 
operator namespace. Alternatively you can also deliver them into any other 
namespace.
+
+There is not a prescribed way how to release the Kamelets on the cluster. It 
can vary based on each company process. What's important for the operator, is 
that they are available when running the Integrations.
+
 [[kamelets-own-catalog]]
 == Provide your own catalog
 
@@ -25,7 +31,7 @@ With this approach you can dynamically include any repository 
where your Kamelet
 [[kamelets-as-dependency]]
 == Kamelets as a dependency
 
-The Camel K has an opinionated way to use Kamelets which is the one exposed 
above. Here the Kamelet spec resource is expected to be available in the 
cluster.
+The Camel K has an opinionated way to use Kamelets which is the usage of 
cluster custom resources. Here the Kamelet spec resource is expected to be 
available in the cluster.
 
 However, you may find situations where you want to bundle a Kamelet in a 
dependency (ie, some external catalog containing all Kamelets spec). As 
Kamelets are a Camel thing, then, you can use such dependency and let the 
runtime use the Kamelets available in the classpath.
 
diff --git a/docs/modules/ROOT/pages/kamelets/kamelets-user.adoc 
b/docs/modules/ROOT/pages/kamelets/kamelets-user.adoc
deleted file mode 100644
index 773c970ca..000000000
--- a/docs/modules/ROOT/pages/kamelets/kamelets-user.adoc
+++ /dev/null
@@ -1,191 +0,0 @@
-= How to configure a Kamelet
-
-Speaking technically, a Kamelet is a resource that can be installed on any 
Kubernetes cluster or used as a plain yaml configuration in Apache Camel 
runtimes. The following is an example of a Kamelet that we'll use to discuss 
the various parts:
-
-.telegram-text-source.kamelet.yaml
-[source,yaml]
-----
-apiVersion: camel.apache.org/v1
-kind: Kamelet
-metadata:
-  name: telegram-text-source # <1>
-  annotations: # <2>
-    camel.apache.org/kamelet.icon: "data:image/svg+xml;base64,PD94bW..."
-  labels: # <3>
-    camel.apache.org/kamelet.type: "source"
-spec:
-  definition: # <4>
-    title: "Telegram Text Source"
-    description: |-
-      Receive all text messages that people send to your telegram bot.
-
-      # Instructions
-      Description can include Markdown and guide the final user to configure 
the Kamelet parameters.
-    required:
-      - botToken
-    properties:
-      botToken:
-        title: Token
-        description: The token to access your bot on Telegram
-        type: string
-        x-descriptors:
-        - urn:alm:descriptor:com.tectonic.ui:password
-
-  dataTypes: # <5>
-    out:
-      default: text
-      types:
-        text:
-          mediaType: text/plain
-          # schema:
-  template: # <6>
-    from:
-      uri: telegram:bots
-      parameters:
-        authorizationToken: "#property:botToken"
-      steps:
-        - convert-body-to:
-            type: "java.lang.String"
-            type-class: "java.lang.String"
-            charset: "UTF8"
-        - filter:
-            simple: "${body} != null"
-        - log: "${body}"
-        - to: "kamelet:sink"
-----
-<1> The Kamelet ID, to be used in integrations that want to leverage the 
Kamelet
-<2> Annotations such as icon provide additional display features to the Kamelet
-<3> Labels allow users to query Kamelets e.g. by kind ("source" vs. "sink")
-<4> Description of the Kamelets and parameters in JSON-schema specification 
format
-<5> The data type that the Kamelet produces. Data type specifications contain 
the media type of the output and also may include a schema.
-<6> The route template defining the behavior of the Kamelet
-
-At a high level (more details are provided later), a Kamelet resource 
describes:
-
-- A metadata section containing the ID (`metadata` -> `name`) of the Kamelet 
and other information, such as the type of Kamelet (`source` or `sink`)
-- A JSON-schema specification (`definition`) containing a set of parameters 
that you can use to configure the Kamelet
-- An optional section containing information about input and output expected 
by the Kamelet (`types`)
-- A Camel flow in YAML DSL containing the implementation of the Kamelet 
(`flow`)
-
-Once **installed on a Kubernetes namespace**, the Kamelet can be **used by any 
Integration in that namespace**. Kamelets can be installed on a Kubernetes 
namespace with a simple command:
-
-[source,shell]
-----
-kubectl apply -f telegram-text-source.kamelet.yaml
-----
-
-Kamelets are standard YAML files, but their common extension is 
`.kamelet.yaml` to help IDEs to recognize them and possibly provide 
auto-completion.
-
-[[kamelets-usage-integration]]
-== Using Kamelets in Integrations
-
-Kamelets can be used in integrations **as if they were standard Camel 
components**. For example, suppose that you've created the 
`telegram-text-source` Kamelet in the `default` namespace on Kubernetes, then 
you can write the following integration to use the Kamelet:
-
-[source,yaml]
-.kamlet-route.yaml
-----
-- from:
-    uri: "kamelet:telegram-text-source?botToken=XXXXYYYY"
-    steps:
-      - to: "log:info"
-----
-
-NOTE: URI properties ("botToken") match the corresponding parameters in the 
Kamelet definition
-
-Kamelets can also be used multiple times in the same route definition. This 
happens usually with sink Kamelets. Suppose that you've defined a Kamelet named 
"my-company-log-sink" in your Kubernetes namespace, then you can write a route 
like this:
-
-[source,yaml]
-.kamlet-multi-route.yaml
-----
-- from:
-    uri: "kamelet:telegram-text-source?botToken=XXXXYYYY"
-    steps:
-      - to: "kamelet:my-company-log-sink?bucket=general"
-      - filter:
-          simple: '${body} contains "Camel"'
-      - to: "kamelet:my-company-log-sink?bucket=special"
-----
-
-The "my-company-log-sink" will obviously define what it means to write a log 
in the enterprise system and what is concretely a "bucket".
-
-=== Configuration
-
-When using a Kamelet, the instance parameters (e.g. "botToken", "bucket") can 
be passed explicitly in the URI or you can use properties. Properties can be 
also
-loaded implicitly by the operator from Kubernetes secrets (see below).
-
-==== URI based configuration
-
-You can configure the Kamelet by passing directly the configuration parameters 
in the URI, as in:
-
-[source,yaml]
-----
-- from:
-    uri: "kamelet:telegram-text-source?botToken=the-token-value"
-...
-----
-
-In this case, "the-token-value" is passed explicitly in the URI (you can also 
pass a custom property placeholder as value).
-
-==== Property based configuration
-
-An alternative way to configure the Kamelet is to provide configuration 
parameters as properties of the integration.
-
-Taking for example a different version of the integration above:
-
-[source,yaml]
-.kamelet-properties-route.yaml
-----
-- from:
-    uri: "kamelet:telegram-text-source"
-    steps:
-      - to: "kamelet:my-company-log-sink"
-      - filter:
-          simple: '${body} contains "Camel"'
-      - to: "kamelet:my-company-log-sink/mynamedconfig"
-----
-
-NOTE: The integration above does not contain URI query parameters and the last 
URI ("kamelet:my-company-log-sink/mynamedconfig") contains a path parameter 
with value "mynamedconfig"
-
-The integration above needs some configuration in order to run properly. The 
configuration can be provided in a property file:
-
-[source,properties]
-.kamelet-example.properties
-----
-# Configuration for the Telegram source Kamelet
-camel.kamelet.telegram-text-source.botToken=the-token-value
-
-# General configuration for the Company Log Kamelet
-camel.kamelet.my-company-log-sink.bucket=general
-# camel.kamelet.my-company-log-sink.xxx=yyy
-
-# Specific configuration for the Company Log Kamelet corresponding to the 
named configuration "mynamedconfig"
-camel.kamelet.my-company-log-sink.mynamedconfig.bucket=special
-# When using "kamelet:my-company-log-sink/mynamedconfig", the bucket will be 
"special", not "general"
-----
-
-Then the integration can be run with the following command:
-
-[source,shell]
-----
-kamel run kamelet-properties-route.yaml --property 
file:kamelet-example.properties
-----
-
-=== Kamelet versioning
-
-Kamelets provided in a catalog are generally meant to work with a given 
runtime version (the same for which they are released). However, when you 
create a Kamelet and publish to a cluster, you may want to store and use 
different versions. If the Kamelet is provided with more than the `main` 
version, then, you can specify which version to use in your Integration by 
adding the version parameter. For instance:
-
-[source,yaml]
-.kamlet-namedconfig-route.yaml
-----
-- from:
-    uri: "kamelet:my-source?kameletVersion=v2"
-    steps:
-      - to: "log:info"
-----
-
-The operator will be able to automatically pick the right version and use it 
at runtime. If no version is specified, then you will use the default one.
-
-[[kamelets-troubleshooting]]
-== Troubleshooting
-
-A `Kamelet` is translated into a `Route` used from the `Integration`. In order 
to troubleshoot any possible issue, you can have a look at the dedicated 
xref:troubleshooting/debugging.adoc#debugging-kamelets[troubleshoot section].
diff --git a/docs/modules/ROOT/pages/kamelets/kamelets.adoc 
b/docs/modules/ROOT/pages/kamelets/kamelets.adoc
index 2bea27233..bdba18535 100644
--- a/docs/modules/ROOT/pages/kamelets/kamelets.adoc
+++ b/docs/modules/ROOT/pages/kamelets/kamelets.adoc
@@ -29,4 +29,4 @@ Kamelets are also expected to be **rendered on visual tools** 
that will provide
 They are generic connectors that can be used in multiples ways, depending on 
the context, so each UIs can use them
 for its own purpose.
 
-Have a look at the xref:kamelets/kamelets-user.adoc[Kamelets User Guide] to 
learn how to immediately use Kamelets in Camel K or check 
xref:kamelets/kamelets-distribution.adoc[how to use your own Kamelet catalog].
+Have a look at the xref:kamelets/configuration.adoc[Kamelets User Guide] to 
learn how to immediately use Kamelets in Camel K or check 
xref:kamelets/distribution.adoc[how to use your own Kamelet catalog].
diff --git a/docs/modules/ROOT/pages/troubleshooting/debugging.adoc 
b/docs/modules/ROOT/pages/troubleshooting/debugging.adoc
index 6e685b0e4..8eb59e57b 100644
--- a/docs/modules/ROOT/pages/troubleshooting/debugging.adoc
+++ b/docs/modules/ROOT/pages/troubleshooting/debugging.adoc
@@ -51,12 +51,3 @@ image::debugging/remote-debugger.png[Configuration of the 
remote debugger in Int
 Once you configure a debugger, you can **add breakpoints** to various part of 
the code, then connect the debugger to trigger the JVM startup.
 
 When the debugging session is done, hitting kbd:[Ctrl+c] on the terminal where 
the kamel CLI is running will restore the integration to its original status.
-
-[[debugging-kamelets]]
-== Debugging Kamelets
-
-As we've seen in the previous section, all `Integration` created in Camel K 
are finally bundled as a Java application, hence, the possibility to debug via 
JVM debugger. Any `Kamelet` you will be using directly in your `Route` 
definition or in a `Pipe` is automatically converted in a `yaml` route and 
injected in the Camel Context to be executed. That means that you cannot 
directly debug a `Kamelet` as you would do with a Java or any other JVM 
language `Route`.
-
-However, you can troubleshoot individually each `Kamelet` definition by 
focusing on the specification `Flow`. As an example, you can create a simple 
`yaml` test `Route` substituting the `kamelet:source` or `kamelet:sink` with 
any mock endpoint that can help you in debugging the single `Kamelet` flow. 
Even using a `timer` and a `log` component may be enough for a basic check.
-
-NOTE: the same idea applies for a `Pipe` which translates to an `Integration` 
type under the hood. If you need to debug a `Pipe` just apply the same 
troubleshooting technique that you would apply on an `Integration`.
diff --git a/e2e/common/traits/files/kamelet-it-ns.yaml 
b/e2e/common/traits/files/kamelet-it-ns.yaml
new file mode 100644
index 000000000..c4bd766ef
--- /dev/null
+++ b/e2e/common/traits/files/kamelet-it-ns.yaml
@@ -0,0 +1,24 @@
+# camel-k: language=yaml
+
+# ---------------------------------------------------------------------------
+# 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.
+# ---------------------------------------------------------------------------
+
+- from:
+    # The test will take care to replace %%% with the testing namespace
+    uri: "kamelet:my-timer-source?kameletNamespace=%%%"
+    steps:
+      - log: "${body}"
\ No newline at end of file
diff --git a/e2e/common/traits/files/my-timer-source-ns.kamelet.yaml 
b/e2e/common/traits/files/my-timer-source-ns.kamelet.yaml
new file mode 100644
index 000000000..dca31ad89
--- /dev/null
+++ b/e2e/common/traits/files/my-timer-source-ns.kamelet.yaml
@@ -0,0 +1,36 @@
+# ---------------------------------------------------------------------------
+# 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.
+# ---------------------------------------------------------------------------
+apiVersion: camel.apache.org/v1
+kind: Kamelet
+metadata:
+  name: my-timer-source
+  labels:
+    camel.apache.org/kamelet.type: "source"
+spec:
+  definition:
+    title: "Timer Example"
+    description: "Produces periodic events with a custom payload"
+  types:
+    out:
+      mediaType: text/plain
+  template:
+    from:
+      uri: timer:tick
+      steps:
+        - setBody:
+            constant: "Kamelet NS"
+        - to: "kamelet:sink"
diff --git a/e2e/common/traits/kamelet_test.go 
b/e2e/common/traits/kamelet_test.go
index 257566471..d96ce6215 100644
--- a/e2e/common/traits/kamelet_test.go
+++ b/e2e/common/traits/kamelet_test.go
@@ -24,10 +24,12 @@ package common
 
 import (
        "context"
+       "os"
+       "path/filepath"
+       "strings"
        "testing"
 
        . "github.com/onsi/gomega"
-
        corev1 "k8s.io/api/core/v1"
 
        . "github.com/apache/camel-k/v2/e2e/support"
@@ -82,3 +84,51 @@ func TestKameletMultiVersions(t *testing.T) {
                })
        })
 }
+
+func TestKameletNamespaced(t *testing.T) {
+       t.Parallel()
+       WithNewTestNamespace(t, func(ctx context.Context, g *WithT, ns1 string) 
{
+               t.Run("store kamelet", func(t *testing.T) {
+                       ExpectExecSucceed(t, g, Kubectl("apply", "-f", 
"files/my-timer-source-ns.kamelet.yaml", "-n", ns1))
+               })
+
+               WithNewTestNamespace(t, func(ctx context.Context, g *WithT, ns2 
string) {
+                       t.Run("namespaced kamelet", func(t *testing.T) {
+                               // Clone the resource in a temporary file as it 
will require to be changed
+                               routeFile := cloneAndReplaceNamespace(t, 
"files/kamelet-it-ns.yaml", ns1)
+                               name := 
RandomizedSuffixName("namespaced-kamelet")
+                               g.Expect(KamelRun(t, ctx, ns2, routeFile, 
"--name", name).Execute()).To(Succeed())
+                               g.Eventually(IntegrationConditionStatus(t, ctx, 
ns2, name, v1.IntegrationConditionReady), TestTimeoutMedium).
+                                       Should(Equal(corev1.ConditionTrue))
+                               g.Eventually(IntegrationPodPhase(t, ctx, ns2, 
name), TestTimeoutShort).Should(Equal(corev1.PodRunning))
+                               g.Eventually(IntegrationLogs(t, ctx, ns2, 
name), TestTimeoutShort).Should(ContainSubstring("Kamelet NS"))
+                       })
+               })
+       })
+}
+
+// cloneAndReplaceNamespace clones and replace the content marked as %%% with 
the namespace passed as parameter.
+func cloneAndReplaceNamespace(t *testing.T, srcPath, namespace string) string {
+       t.Helper()
+
+       tempDir := t.TempDir()
+       tempPath := filepath.Join(tempDir, filepath.Base(srcPath))
+
+       dstFile, err := os.Create(tempPath)
+       if err != nil {
+               t.Fatalf("failed to create temp file: %v", err)
+       }
+       defer dstFile.Close()
+
+       content, err := os.ReadFile(srcPath)
+       if err != nil {
+               t.Fatalf("failed to read src file: %v", err)
+       }
+       updated := strings.ReplaceAll(string(content), "%%%", namespace)
+       err = os.WriteFile(tempPath, []byte(updated), 0644)
+       if err != nil {
+               t.Fatalf("failed to write dst file: %v", err)
+       }
+
+       return tempPath
+}
diff --git a/pkg/apis/camel/v1/kamelet_types.go 
b/pkg/apis/camel/v1/kamelet_types.go
index be31072c3..0e8146af2 100644
--- a/pkg/apis/camel/v1/kamelet_types.go
+++ b/pkg/apis/camel/v1/kamelet_types.go
@@ -50,8 +50,12 @@ var (
        reservedKameletNames = map[string]bool{"source": true, "sink": true}
        // KameletIDProperty used to identify.
        KameletIDProperty = "id"
-       // KameletVersionProperty used to specify the version to use.
+       // KameletVersionProperty used to specify the version to use. NOTE: 
this parameter is exclusive use of the operator
+       // and should not be used by final users.
        KameletVersionProperty = "kameletVersion"
+       // KameletNamespaceProperty used to specify the namespace to use. NOTE: 
this parameter is exclusive use of the operator
+       // and should not be used by final users.
+       KameletNamespaceProperty = "kameletNamespace"
 )
 
 // +genclient
diff --git a/pkg/trait/kamelets.go b/pkg/trait/kamelets.go
index 0cb2f3a15..091788677 100644
--- a/pkg/trait/kamelets.go
+++ b/pkg/trait/kamelets.go
@@ -20,6 +20,7 @@ package trait
 import (
        "errors"
        "fmt"
+       "net/url"
        "path/filepath"
        "sort"
        "strconv"
@@ -51,8 +52,6 @@ const (
        kameletMountPointAnnotation = "camel.apache.org/kamelet.mount-point"
 )
 
-var kameletVersionProperty = fmt.Sprintf("?%s=", v1.KameletVersionProperty)
-
 type kameletsTrait struct {
        BaseTrait
        traitv1.KameletsTrait `property:",squash"`
@@ -89,7 +88,7 @@ func (t *kameletsTrait) Configure(e *Environment) (bool, 
*TraitCondition, error)
                }
        }
 
-       return len(t.getKameletKeys(false)) > 0, nil, nil
+       return len(t.getKameletKeys()) > 0, nil, nil
 }
 
 func (t *kameletsTrait) Apply(e *Environment) error {
@@ -103,18 +102,22 @@ func (t *kameletsTrait) Apply(e *Environment) error {
 
 // collectKamelets load a Kamelet specification setting the specific version 
specification.
 func (t *kameletsTrait) collectKamelets(e *Environment) 
(map[string]*v1.Kamelet, error) {
-       repo, err := repository.NewForPlatform(e.Ctx, e.Client, e.Platform, 
e.Integration.Namespace, platform.GetOperatorNamespace())
+       namespaces, err := t.calculateNamespaces(e.Integration.Namespace, 
platform.GetOperatorNamespace())
+       if err != nil {
+               return nil, err
+       }
+       repo, err := repository.NewForPlatform(e.Ctx, e.Client, e.Platform, 
namespaces...)
        if err != nil {
                return nil, err
        }
 
        kamelets := make(map[string]*v1.Kamelet)
-       missingKamelets := make([]string, 0)
-       availableKamelets := make([]string, 0)
-       bundledKamelets := make([]string, 0)
+       var missingKamelets []string
+       var availableKamelets []string
+       var bundledKamelets []string
 
        for _, kml := range strings.Split(t.List, ",") {
-               name := getKameletKey(kml, false)
+               name := getKameletKey(kml)
                if !v1.ValidKameletName(name) {
                        // Skip kamelet sink and source id
                        continue
@@ -133,7 +136,11 @@ func (t *kameletsTrait) collectKamelets(e *Environment) 
(map[string]*v1.Kamelet,
                        bundledKamelets = append(bundledKamelets, name)
                }
                // We control which version to use (if any is specified)
-               clonedKamelet, err := 
kamelet.CloneWithVersion(getKameletVersion(kml))
+               version, err := getKameletVersion(kml)
+               if err != nil {
+                       return nil, fmt.Errorf("could not parse kamelet 
version: %w", err)
+               }
+               clonedKamelet, err := kamelet.CloneWithVersion(version)
                if err != nil {
                        return nil, err
                }
@@ -186,8 +193,38 @@ func (t *kameletsTrait) collectKamelets(e *Environment) 
(map[string]*v1.Kamelet,
        return kamelets, nil
 }
 
+// calculateNamespaces is in charge to scan the kamelets specification and 
provide a list of
+// namespaces where to look for Kamelets.
+func (t *kameletsTrait) calculateNamespaces(defaultNamespaces ...string) 
([]string, error) {
+       return calculateNamespaces(strings.Split(t.List, ","), 
defaultNamespaces...)
+}
+
+func calculateNamespaces(kamelets []string, defaultNamespaces ...string) 
([]string, error) {
+       namespaces := defaultNamespaces
+       for _, kml := range kamelets {
+               ns, err := getKameletNamespace(kml)
+               if err != nil {
+                       return nil, fmt.Errorf("could not parse kamelet 
namespace: %w", err)
+               }
+               if ns != "" {
+                       addNs := true
+               loop:
+                       for _, addedNs := range namespaces {
+                               if addedNs == ns {
+                                       addNs = false
+                                       break loop
+                               }
+                       }
+                       if addNs {
+                               namespaces = append(namespaces, ns)
+                       }
+               }
+       }
+       return namespaces, nil
+}
+
 func (t *kameletsTrait) addKamelets(e *Environment) error {
-       if len(t.getKameletKeys(false)) == 0 {
+       if len(t.getKameletKeys()) == 0 {
                return nil
        }
        kamelets, err := t.collectKamelets(e)
@@ -280,10 +317,10 @@ func (t *kameletsTrait) addKameletAsSource(e 
*Environment, kamelet *v1.Kamelet)
        return nil
 }
 
-func (t *kameletsTrait) getKameletKeys(withVersion bool) []string {
+func (t *kameletsTrait) getKameletKeys() []string {
        answer := make([]string, 0)
        for _, item := range strings.Split(t.List, ",") {
-               i := getKameletKey(item, withVersion)
+               i := getKameletKey(item)
                if i != "" && v1.ValidKameletName(i) {
                        util.StringSliceUniqueAdd(&answer, i)
                }
@@ -300,28 +337,35 @@ func (t *kameletsTrait) getMountPoint() string {
        return t.MountPoint
 }
 
-func getKameletKey(item string, withVersion bool) string {
-       i := strings.Trim(item, " \t\"")
-       if strings.Contains(i, "/") {
-               i = strings.SplitN(i, "/", 2)[0]
+// getKameletKey remove any params from the kamelet, eg 
my-kamelet/abc?param1=1 will return the uri path filtered (my-kamelet).
+func getKameletKey(item string) string {
+       parsedURL, err := url.Parse(item)
+       if err != nil {
+               return ""
        }
-       if strings.Contains(i, kameletVersionProperty) {
-               versionedKamelet := strings.SplitN(i, kameletVersionProperty, 2)
-               if withVersion {
-                       i = fmt.Sprintf("%s-%s", versionedKamelet[0], 
versionedKamelet[1])
-               } else {
-                       i = versionedKamelet[0]
-               }
+       parts := strings.Split(parsedURL.Path, "/")
+       if len(parts) > 0 {
+               return parts[0]
        }
-       return i
+       return ""
 }
 
-func getKameletVersion(item string) string {
-       if strings.Contains(item, fmt.Sprintf("?%s=", 
v1.KameletVersionProperty)) {
-               versionedKamelet := strings.SplitN(item, 
kameletVersionProperty, 2)
-               return versionedKamelet[1]
+func getKameletVersion(item string) (string, error) {
+       return getKameletParam(item, v1.KameletVersionProperty)
+}
+
+func getKameletNamespace(item string) (string, error) {
+       return getKameletParam(item, v1.KameletNamespaceProperty)
+}
+
+func getKameletParam(uri, param string) (string, error) {
+       parsedURL, err := url.Parse(uri)
+       if err != nil {
+               return "", err
        }
-       return ""
+
+       queryParams := parsedURL.Query()
+       return queryParams.Get(param), nil
 }
 
 func integrationSourceFromKameletSource(e *Environment, kamelet *v1.Kamelet, 
source v1.SourceSpec, name string) (v1.SourceSpec, error) {
diff --git a/pkg/trait/kamelets_test.go b/pkg/trait/kamelets_test.go
index bc59b47be..87c24cda8 100644
--- a/pkg/trait/kamelets_test.go
+++ b/pkg/trait/kamelets_test.go
@@ -68,8 +68,7 @@ func TestConfigurationWithKamelets(t *testing.T) {
        require.NoError(t, err)
        assert.True(t, enabled)
        assert.Nil(t, condition)
-       assert.Equal(t, []string{"c0", "c1", "c2", "c3", "complex-.-.-1a", 
"complex-.-.-1b", "complex-.-.-1c"}, trait.getKameletKeys(false))
-       assert.Equal(t, []string{"c0", "c1", "c2", "c3-v1", "complex-.-.-1a", 
"complex-.-.-1b", "complex-.-.-1c"}, trait.getKameletKeys(true))
+       assert.Equal(t, []string{"c0", "c1", "c2", "c3", "complex-.-.-1a", 
"complex-.-.-1b", "complex-.-.-1c"}, trait.getKameletKeys())
 }
 
 func TestKameletLookup(t *testing.T) {
@@ -101,7 +100,7 @@ func TestKameletLookup(t *testing.T) {
        require.NoError(t, err)
        assert.True(t, enabled)
        assert.Nil(t, condition)
-       assert.Equal(t, []string{"timer"}, trait.getKameletKeys(false))
+       assert.Equal(t, []string{"timer"}, trait.getKameletKeys())
 
        err = trait.Apply(environment)
        require.NoError(t, err)
@@ -152,7 +151,7 @@ func TestKameletSecondarySourcesLookup(t *testing.T) {
        require.NoError(t, err)
        assert.True(t, enabled)
        assert.Nil(t, condition)
-       assert.Equal(t, []string{"timer"}, trait.getKameletKeys(false))
+       assert.Equal(t, []string{"timer"}, trait.getKameletKeys())
 
        err = trait.Apply(environment)
        require.NoError(t, err)
@@ -205,7 +204,7 @@ func TestNonYAMLKameletLookup(t *testing.T) {
        require.NoError(t, err)
        assert.True(t, enabled)
        assert.Nil(t, condition)
-       assert.Equal(t, []string{"timer"}, trait.getKameletKeys(false))
+       assert.Equal(t, []string{"timer"}, trait.getKameletKeys())
 
        err = trait.Apply(environment)
        require.NoError(t, err)
@@ -312,8 +311,7 @@ func TestMultipleKamelets(t *testing.T) {
        assert.True(t, enabled)
        assert.Nil(t, condition)
        assert.Equal(t, "logger,timer?kameletVersion=v1", trait.List)
-       assert.Equal(t, []string{"logger", "timer"}, 
trait.getKameletKeys(false))
-       assert.Equal(t, []string{"logger", "timer-v1"}, 
trait.getKameletKeys(true))
+       assert.Equal(t, []string{"logger", "timer"}, trait.getKameletKeys())
 
        err = trait.Apply(environment)
        require.NoError(t, err)
@@ -409,7 +407,7 @@ func TestKameletConfigLookup(t *testing.T) {
        require.NoError(t, err)
        assert.True(t, enabled)
        assert.Nil(t, condition)
-       assert.Equal(t, []string{"timer"}, trait.getKameletKeys(false))
+       assert.Equal(t, []string{"timer"}, trait.getKameletKeys())
 }
 
 func TestKameletNamedConfigLookup(t *testing.T) {
@@ -467,7 +465,7 @@ func TestKameletNamedConfigLookup(t *testing.T) {
        require.NoError(t, err)
        assert.True(t, enabled)
        assert.Nil(t, condition)
-       assert.Equal(t, []string{"timer"}, trait.getKameletKeys(false))
+       assert.Equal(t, []string{"timer"}, trait.getKameletKeys())
 }
 
 func TestKameletConditionFalse(t *testing.T) {
@@ -753,3 +751,146 @@ func TestKameletAuto(t *testing.T) {
        )
        assert.Equal(t, "false", 
environment.ApplicationProperties[KameletErrorHandler])
 }
+
+func TestKameletVersionParameter(t *testing.T) {
+       v1, err := getKameletVersion("my-kamelet?kameletVersion=v2")
+       require.NoError(t, err)
+       assert.Equal(t, "v2", v1)
+       v2, err := getKameletVersion("my-kamelet?prop1=1&kameletVersion=v2")
+       require.NoError(t, err)
+       assert.Equal(t, "v2", v2)
+       v3, err := getKameletVersion("my-kamelet")
+       require.NoError(t, err)
+       assert.Equal(t, "", v3)
+}
+
+func TestKameletNamespaceParameter(t *testing.T) {
+       v2, err := 
getKameletNamespace("my-kamelet?prop1=1&kameletNamespace=my-ns")
+       require.NoError(t, err)
+       assert.Equal(t, "my-ns", v2)
+}
+
+func TestCalculateKameletNamespaces(t *testing.T) {
+       namespaces, err := calculateNamespaces(
+               []string{"my-kamelet", "my-kamelet?kameletNamespace=ns1", 
"my-kamelet?kameletVersion=v2&kameletNamespace=ns2"},
+               "default", "camel-k",
+       )
+       require.NoError(t, err)
+       assert.Len(t, namespaces, 4)
+       assert.Contains(t, namespaces, "ns1")
+       assert.Contains(t, namespaces, "ns2")
+       assert.Contains(t, namespaces, "default")
+       assert.Contains(t, namespaces, "camel-k")
+}
+
+func TestKameletMultiNamespace(t *testing.T) {
+       flow := `
+- from:
+    uri: kamelet:timer
+    steps:
+    - to: kamelet:extra?kameletNamespace=ns1
+`
+       trait, environment := createKameletsTestEnvironment(
+               flow,
+               &v1.Kamelet{
+                       ObjectMeta: metav1.ObjectMeta{
+                               Namespace: "test",
+                               Name:      "timer",
+                       },
+                       Spec: v1.KameletSpec{
+                               KameletSpecBase: v1.KameletSpecBase{
+                                       Template: 
templateOrFail(map[string]interface{}{
+                                               "from": map[string]interface{}{
+                                                       "uri": "timer:tick",
+                                               },
+                                       }),
+                               },
+                       },
+               },
+               &v1.Kamelet{
+                       ObjectMeta: metav1.ObjectMeta{
+                               Namespace: "ns1",
+                               Name:      "extra",
+                       },
+                       Spec: v1.KameletSpec{
+                               KameletSpecBase: v1.KameletSpecBase{
+                                       Template: 
templateOrFail(map[string]interface{}{
+                                               "from": map[string]interface{}{
+                                                       "uri": "timer:tick",
+                                               },
+                                       }),
+                               },
+                       },
+               })
+
+       enabled, condition, err := trait.Configure(environment)
+       require.NoError(t, err)
+       assert.True(t, enabled)
+       assert.Nil(t, condition)
+
+       err = trait.Apply(environment)
+       require.NoError(t, err)
+       assert.Equal(t, "extra?kameletNamespace=ns1,timer", trait.List)
+       assert.Equal(t,
+               corev1.ConditionTrue,
+               
environment.Integration.Status.GetCondition(v1.IntegrationConditionKameletsAvailable).Status)
+       assert.Equal(t,
+               "kamelets [extra,timer] found in (Kubernetes[namespace=test], 
Kubernetes[namespace=ns1], Empty[]) repositories",
+               
environment.Integration.Status.GetCondition(v1.IntegrationConditionKameletsAvailable).Message)
+}
+
+func TestKameletMultiNamespaceMissing(t *testing.T) {
+       flow := `
+- from:
+    uri: kamelet:timer
+    steps:
+    - to: kamelet:missing?kameletNamespace=ns1
+`
+       trait, environment := createKameletsTestEnvironment(
+               flow,
+               &v1.Kamelet{
+                       ObjectMeta: metav1.ObjectMeta{
+                               Namespace: "test",
+                               Name:      "timer",
+                       },
+                       Spec: v1.KameletSpec{
+                               KameletSpecBase: v1.KameletSpecBase{
+                                       Template: 
templateOrFail(map[string]interface{}{
+                                               "from": map[string]interface{}{
+                                                       "uri": "timer:tick",
+                                               },
+                                       }),
+                               },
+                       },
+               },
+               &v1.Kamelet{
+                       ObjectMeta: metav1.ObjectMeta{
+                               Namespace: "another-namespace",
+                               Name:      "missing",
+                       },
+                       Spec: v1.KameletSpec{
+                               KameletSpecBase: v1.KameletSpecBase{
+                                       Template: 
templateOrFail(map[string]interface{}{
+                                               "from": map[string]interface{}{
+                                                       "uri": "timer:tick",
+                                               },
+                                       }),
+                               },
+                       },
+               })
+
+       enabled, condition, err := trait.Configure(environment)
+       require.NoError(t, err)
+       assert.True(t, enabled)
+       assert.Nil(t, condition)
+       assert.Equal(t, "missing?kameletNamespace=ns1,timer", trait.List)
+
+       err = trait.Apply(environment)
+       require.Error(t, err)
+       assert.Equal(t,
+               corev1.ConditionFalse,
+               
environment.Integration.Status.GetCondition(v1.IntegrationConditionKameletsAvailable).Status)
+       assert.Equal(t,
+               "kamelets [missing] not found in (Kubernetes[namespace=test], 
Kubernetes[namespace=ns1], Empty[]) repositories",
+               err.Error())
+}
diff --git a/pkg/util/source/kamelet.go b/pkg/util/source/kamelet.go
index d220bc5ba..82579aead 100644
--- a/pkg/util/source/kamelet.go
+++ b/pkg/util/source/kamelet.go
@@ -19,20 +19,32 @@ package source
 
 import (
        "fmt"
+       "net/url"
        "regexp"
 
        v1 "github.com/apache/camel-k/v2/pkg/apis/camel/v1"
 )
 
 var kameletNameRegexp = 
regexp.MustCompile("kamelet:(?://)?([a-z0-9-.]+(/[a-z0-9-.]+)?)(?:$|[^a-z0-9-.].*)")
-var kameletVersionRegexp = regexp.MustCompile(v1.KameletVersionProperty + 
"=([a-z0-9-.]+)")
 
+//nolint:nestif
 func ExtractKamelet(uri string) string {
        matches := kameletNameRegexp.FindStringSubmatch(uri)
        if len(matches) > 1 {
-               version := kameletVersionRegexp.FindString(uri)
-               if version != "" {
-                       return fmt.Sprintf("%s?%s", matches[1], version)
+               version := getKameletParam(uri, v1.KameletVersionProperty)
+               namespace := getKameletParam(uri, v1.KameletNamespaceProperty)
+               if version != "" || namespace != "" {
+                       var querystring string
+                       if version != "" {
+                               querystring = v1.KameletVersionProperty + "=" + 
version
+                       }
+                       if namespace != "" {
+                               if querystring != "" {
+                                       querystring += "&"
+                               }
+                               querystring += v1.KameletNamespaceProperty + 
"=" + namespace
+                       }
+                       return fmt.Sprintf("%s?%s", matches[1], querystring)
                }
                return matches[1]
        }
@@ -44,3 +56,14 @@ func AddKamelet(meta *Metadata, content string) {
                meta.Kamelets = append(meta.Kamelets, maybeKamelet)
        }
 }
+
+// getKameletParam parses the URI and return the query parameter or an empty 
value if not found.
+func getKameletParam(uri, param string) string {
+       parsedURL, err := url.Parse(uri)
+       if err != nil {
+               return ""
+       }
+
+       queryParams := parsedURL.Query()
+       return queryParams.Get(param)
+}


Reply via email to