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

cdeppisch 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 ea0bb2c161d CAMEL-20983: Add JBang Knative trait support
ea0bb2c161d is described below

commit ea0bb2c161d485910e4193ddc5af97d4de42b717
Author: Christoph Deppisch <cdeppi...@redhat.com>
AuthorDate: Wed Jul 24 11:14:18 2024 +0200

    CAMEL-20983: Add JBang Knative trait support
    
    - When applicable add Knative trigger to Kubenretes manifest
    - When applicable add Knative channel subscription to Kubernetes manifest
    - When applicable add Knative sink binding for endpoint URI injection
    - Auto configure Knative Camel component with proper resources in 
environment config (knative.json)
    - Auto configure application.properties in exported project to hold Knative 
environment configuration
---
 .../modules/ROOT/pages/camel-jbang-kubernetes.adoc | 244 ++++++++++
 .../core/commands/kubernetes/KubernetesExport.java |  20 +
 .../core/commands/kubernetes/MetadataHelper.java   |  24 +
 .../kubernetes/support/StubComponentResolver.java  |   2 +-
 .../commands/kubernetes/traits/CamelTrait.java     |  48 ++
 .../commands/kubernetes/traits/TraitCatalog.java   |   3 +
 .../commands/kubernetes/traits/TraitContext.java   |  74 +++
 .../commands/kubernetes/traits/TraitHelper.java    |   2 +-
 .../kubernetes/traits/knative/KnativeTrait.java    | 509 +++++++++++++++++++++
 .../commands/kubernetes/KubernetesExportTest.java  | 254 ++++++++++
 .../src/test/resources/knative-channel-sink.yaml   |  23 +
 .../src/test/resources/knative-channel-source.yaml |  21 +
 .../src/test/resources/knative-endpoint-sink.yaml  |  23 +
 .../src/test/resources/knative-event-sink.yaml     |  23 +
 .../src/test/resources/knative-event-source.yaml   |  21 +
 .../download/DependencyDownloaderRoutesLoader.java |   4 +
 16 files changed, 1293 insertions(+), 2 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 e9a06d01e11..d1c9f05b197 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-jbang-kubernetes.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-jbang-kubernetes.adoc
@@ -474,6 +474,250 @@ Refer to the Knative documentation for more information.
 
 |===
 
+=== Connecting to Knative
+
+The previous section described how the exported Apache Camel application can 
leverage the Knative service resource with auto-scaling as part of the 
deployment to Kubernetes.
+
+Apache Camel also provides a Knative component that makes you easily interact 
with https://knative.dev/docs/eventing/[Knative eventing] and 
https://knative.dev/docs/serving/[Knative serving].
+
+The Knative component enables you to exchange data with the Knative eventing 
broker and other Knative services deployed on Kubernetes.
+The Camel JBang Kubernetes plugin provides some autoconfiguration options when 
connecting with the Knative component.
+The export command assists you in configuring both the Knative component and 
the Kubernetes manifest for connecting to Knative resources on the Kubenretes 
cluster.
+
+=== Knative trigger
+
+The concept of a Knative trigger allows you to consume events from the 
https://knative.dev/docs/eventing/[Knative eventing] broker.
+In case your Camel route uses the Knative component as a consumer you may need 
to create a trigger in Kubernetes in order
+to connect your Camel application with the Knative broker.
+
+The Camel JBang Kubernetes plugin is able to automatically create this trigger 
for you.
+
+The following Camel route uses the Knative event component and references a 
Knative broker by its name.
+The plugin inspects the code and automatically generates the Knative trigger 
as part of the Kubernetes manifest that is used
+to run the Camel application on Kubernetes.
+
+[source,yaml]
+----
+- from:
+    uri: knative:event/camel.evt.type?name=my-broker
+    steps:
+      - to: log:info
+----
+
+The route consumes Knative events of type `camel.evt.type`.
+If you export this route with the Camel JBang Kubernetes plugin you will see a 
Knative trigger being generated as part of the Kubernetes manifest 
(kubernetes.yml).
+
+[source,bash]
+----
+camel kubernetes export knative-route.yaml
+----
+
+The generated export project can be deployed to Kubernetes and as part of the 
deployment the trigger is automatically created so the application can start 
consuming events.
+
+The generated trigger looks as follows:
+
+[source,yaml]
+----
+apiVersion: eventing.knative.dev/v1
+kind: Trigger
+metadata:
+  name: my-broker-knative-route-camel-evt-type
+spec:
+  broker: my-broker
+  filter:
+    attributes:
+      type: camel.evt.type
+  subscriber:
+    ref:
+      apiVersion: v1
+      kind: Service
+      name: knative-route
+    uri: /events/camel-evt-type
+----
+
+The trigger uses a default filter on the event type CloudEvents attribute and 
calls the Camel application via the exposed Kubernetes service resource.
+
+The Camel application is automatically configured to expose an Http service so 
incoming events are handed over to the Camel route.
+You can review the Knative service resource configuration that makes Camel 
configure the Knative component.
+The configuration has been automatically created in 
`src/main/resources/knative.json` in the exported project.
+
+Here is an example of the generated `knative.json` file:
+
+[source,json]
+----
+{
+  "resources" : [ {
+    "name" : "camel-event",
+    "type" : "event",
+    "endpointKind" : "source",
+    "path" : "/events/camel-event",
+    "objectApiVersion" : "eventing.knative.dev/v1",
+    "objectKind" : "Broker",
+    "objectName" : "my-broker",
+    "reply" : false
+  } ]
+}
+----
+
+The exported project has everything configured to run the application on 
Kubernetes.
+Of course, you need Knative eventing installed on your target cluster, and you 
need to have a Knative broker named `my-broker` available in the target 
namespace.
+
+Now you can just deploy the application using the Kubernetes manifest and see 
the Camel route consuming events from the broker.
+
+=== Knative channel subscription
+
+Knative channels represent another form of producing and consuming events from 
the Knative broker.
+Instead of using a trigger you can create a subscription for a Knative channel 
to consume events.
+
+The Camel route that connects to a Knative channel in order to receive events 
looks like this:
+
+[source,yaml]
+----
+- from:
+    uri: knative:channel/my-channel
+    steps:
+      - to: log:info
+----
+
+The Knative channel is referenced by its name.
+The Camel JBang Kubernetes plugin will inspect your code to automatically 
create a channel subscription as part of the Kubernetes manifest.
+You just need to export the Camel route as usual.
+
+[source,bash]
+----
+camel kubernetes export knative-route.yaml
+----
+
+The code inspection recognizes the Knative component that references the 
Knative channel and the subscription automatically becomes part of the exported 
Kubernetes manifest.
+
+Here is an example subscription that has been generated during the export:
+
+[source,yaml]
+----
+apiVersion: messaging.knative.dev/v1
+kind: Subscription
+metadata:
+  name: my-channel-knative-route
+spec:
+  channel:
+    apiVersion: messaging.knative.dev/v1
+    kind: Channel
+    name: my-channel
+  subscriber:
+    ref:
+      apiVersion: v1
+      kind: Service
+      name: knative-route
+    uri: /channels/my-channel
+----
+
+The subscription connects the Camel application with the channel so each event 
on the channel is sent to the Kubernetes service resource that also has been 
created as part of the Kubernetes manifest.
+
+The Camel Knative component uses a service resource configuration internally 
to create the proper Http service.
+You can review the Knative service resource configuration that makes Camel 
configure the Knative component.
+The configuration has been automatically created in 
`src/main/resources/knative.json` in the exported project.
+
+Here is an example of the generated `knative.json` file:
+
+[source,json]
+----
+{
+  "resources" : [ {
+    "name" : "my-channel",
+    "type" : "channel",
+    "endpointKind" : "source",
+    "path" : "/channels/my-channel",
+    "objectApiVersion" : "messaging.knative.dev/v1",
+    "objectKind" : "Channel",
+    "objectName" : "my-channel",
+    "reply" : false
+  } ]
+}
+----
+
+Assuming that you have Knative eventing installed on your cluster and that you 
have setup the Knative channel `my-channel` you can start consuming events 
right away.
+The deployment of the exported project uses the Kubernetes manifest to create 
all required resources including the Knative subscription.
+
+=== Knative sink binding
+
+When connecting to a Knative resource (Broker, Channel, Service) in order to 
produce events for Knative eventing you probably want to use a `SinkBinding` 
that resolves the URL to the Knative resource for you.
+The sink binding is a Kubernetes resource that makes Knative eventing 
automatically inject the resource URL into your Camel application on startup.
+The Knative URL injection uses environment variables (`K_SINK`, 
`K_CE_OVERRIDES`) on your deployment.
+The Knative eventing operator will automatically resolve the Knative resource 
(e.g. a Knative broker URL) and inject the value so your application does not 
need to know the actual URL when deploying.
+
+The Camel JBang Kubernetes plugin leverages the sink binding concept for all 
routes that use the Knative component as an output.
+
+The following route produces events on a Knative broker:
+
+[source, yaml]
+----
+- from:
+    uri: timer:tick
+    steps:
+      - setBody:
+          constant: Hello Camel !!!
+      - to: knative:event/camel.evt.type?name=my-broker
+----
+
+The route produces events of type `camel.evt.type` and pushes the events to 
the broker named `my-broker`.
+At this point the actual Knative broker URL is unknown.
+The sink binding is going to resolve the URL and inject its value at 
deployment time using the `K_SINK` environment variable.
+
+The Camel JBang Kubernetes plugin export automatically inspects such a route 
and automatically creates the sink binding resource for us.
+The sink binding is part of the exported Kubernetes manifest and is created on 
the cluster as part of the deployment.
+
+A sink binding resource that is created by the export command looks like 
follows:
+
+[source,bash]
+----
+camel kubernetes export knative-route.yaml
+----
+
+[source,yaml]
+----
+apiVersion: sources.knative.dev/v1
+kind: SinkBinding
+metadata:
+  finalizers:
+    - sinkbindings.sources.knative.dev
+  name: knative-route
+spec:
+  sink:
+    ref:
+      apiVersion: eventing.knative.dev/v1
+      kind: Broker
+      name: my-broker
+  subject:
+    apiVersion: apps/v1
+    kind: Deployment
+    name: knative-route
+----
+
+In addition to creating the sink binding the Camel JBang plugin also takes 
care of configuring the Knative Camel component.
+The Knative component uses a configuration file that you can find in 
`src/main/resources/knative.json`.
+As you can see the configuration uses the `K_SINK` injected property 
placeholder as a broker URL.
+
+[source,json]
+----
+{
+  "resources" : [ {
+    "name" : "camel-evt-type",
+    "type" : "event",
+    "endpointKind" : "sink",
+    "url" : "{{k.sink}}",
+    "objectApiVersion" : "eventing.knative.dev/v1",
+    "objectKind" : "Broker",
+    "objectName" : "my-broker",
+    "reply" : false
+  } ]
+}
+----
+
+As soon as the Kubernetes deployment for the exported project has started the 
sink binding will inject the `K_SINK` environment variable so that the Camel 
applicaiton is ready to send events to the Knative broker.
+
+The sink binding concept works for Knative Broker, Channel and Service 
resources.
+You just reference the resource by its name in your Camel route when sending 
data to the Knative component as an output of the route 
(`to("knative:event|channel|endpoint/<resource-name>")`).
+
 === Mount trait options
 
 The mount trait is able to configure volume mounts on the Deployment resource 
in order to inject data from Kubernetes resources such as config maps or 
secrets.
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 bfc65a0fe7a..57b89707985 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
@@ -20,6 +20,8 @@ package org.apache.camel.dsl.jbang.core.commands.kubernetes;
 import java.io.ByteArrayInputStream;
 import java.io.File;
 import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.StandardOpenOption;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.LinkedHashMap;
@@ -110,6 +112,8 @@ public class KubernetesExport extends Export {
                         description = "The image registry group used to push 
images to.")
     protected String imageGroup;
 
+    private static final String SRC_MAIN_RESOURCES = "/src/main/resources/";
+
     public KubernetesExport(CamelJBangMain main) {
         super(main);
     }
@@ -334,6 +338,22 @@ public class KubernetesExport extends Export {
             }
         }
 
+        context.doWithConfigurationResources((fileName, content) -> {
+            try {
+                File target = new File(exportDir + SRC_MAIN_RESOURCES + 
fileName);
+                if (target.exists()) {
+                    Files.writeString(target.toPath(), 
"%n%s".formatted(content), StandardOpenOption.APPEND);
+                } else {
+                    safeCopy(new 
ByteArrayInputStream(content.getBytes(StandardCharsets.UTF_8)), target);
+                }
+            } catch (Exception e) {
+                if (!quiet) {
+                    printer().printf("Failed to create configuration resource 
%s - %s%n",
+                            exportDir + SRC_MAIN_RESOURCES + fileName, 
e.getMessage());
+                }
+            }
+        });
+
         if (!quiet) {
             printer().println("Project export successful!");
         }
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/main/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/MetadataHelper.java
 
b/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/main/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/MetadataHelper.java
index 5829ef80da8..87c649d14bb 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/main/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/MetadataHelper.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/main/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/MetadataHelper.java
@@ -20,6 +20,7 @@ package org.apache.camel.dsl.jbang.core.commands.kubernetes;
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.Map;
+import java.util.Optional;
 import java.util.Set;
 import java.util.TreeSet;
 import java.util.function.BiConsumer;
@@ -64,6 +65,7 @@ import org.apache.camel.spi.DataFormatResolver;
 import org.apache.camel.spi.FactoryFinder;
 import org.apache.camel.spi.FactoryFinderResolver;
 import org.apache.camel.spi.LanguageResolver;
+import org.apache.camel.spi.PropertiesComponent;
 import org.apache.camel.spi.Resource;
 import org.apache.camel.spi.RoutesLoader;
 import org.apache.camel.spi.TransformerResolver;
@@ -72,6 +74,7 @@ import org.apache.camel.support.ResourceHelper;
 import org.apache.camel.tooling.model.ComponentModel;
 import org.apache.camel.tooling.model.DataFormatModel;
 import org.apache.camel.tooling.model.LanguageModel;
+import org.apache.camel.util.URISupport;
 
 public class MetadataHelper {
 
@@ -199,6 +202,23 @@ public class MetadataHelper {
                 // TODO: maybe the camel context should keep track of those ?
                 return dataFormatResolver.getNames();
             }
+
+            @Override
+            public PropertiesComponent getPropertiesComponent() {
+                return new 
org.apache.camel.component.properties.PropertiesComponent() {
+                    @Override
+                    public Optional<String> resolveProperty(String key) {
+                        // properties may not be resolvable in this stubbed 
context, just use the key as a value
+                        return Optional.of(key);
+                    }
+
+                    @Override
+                    public String parseUri(String uri, boolean 
keepUnresolvedOptional) {
+                        // we are not interested in the query part of endpoint 
uris, remove it to avoid unresolvable properties
+                        return URISupport.stripQuery(uri);
+                    }
+                };
+            }
         };
 
         final ExtendedCamelContext ec = context.getCamelContextExtension();
@@ -264,6 +284,10 @@ public class MetadataHelper {
                 if (catalog.componentModel(componentName).isConsumerOnly() && 
componentName.contains("http")) {
                     return true;
                 }
+
+                if (componentName.equals("knative")) {
+                    return true;
+                }
             }
         }
 
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/main/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/support/StubComponentResolver.java
 
b/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/main/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/support/StubComponentResolver.java
index c3d2d7e8201..07858a27da7 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/main/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/support/StubComponentResolver.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/main/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/support/StubComponentResolver.java
@@ -27,7 +27,7 @@ import org.apache.camel.impl.engine.DefaultComponentResolver;
 
 public final class StubComponentResolver extends DefaultComponentResolver {
     private static final Set<String> ACCEPTED_STUB_NAMES = Set.of(
-            "stub", "bean", "class", "direct", "kamelet", "log", "rest", 
"rest-api", "seda", "vrtx-http");
+            "stub", "bean", "class", "direct", "kamelet", "log", "rest", 
"rest-api", "seda", "vertx-http");
 
     private final Set<String> names;
     private final String stubPattern;
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/main/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/traits/CamelTrait.java
 
b/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/main/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/traits/CamelTrait.java
new file mode 100644
index 00000000000..72d3701c700
--- /dev/null
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/main/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/traits/CamelTrait.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.camel.dsl.jbang.core.commands.kubernetes.traits;
+
+import java.util.Optional;
+import java.util.stream.Collectors;
+
+import org.apache.camel.util.ObjectHelper;
+import org.apache.camel.v1.integrationspec.Traits;
+import org.apache.camel.v1.integrationspec.traits.Camel;
+
+public class CamelTrait extends BaseTrait {
+
+    public CamelTrait() {
+        super("camel", 12000);
+    }
+
+    @Override
+    public boolean configure(Traits traitConfig, TraitContext context) {
+        return true;
+    }
+
+    @Override
+    public void apply(Traits traitConfig, TraitContext context) {
+        Camel camelTrait = 
Optional.ofNullable(traitConfig.getCamel()).orElseGet(Camel::new);
+
+        if (ObjectHelper.isNotEmpty(camelTrait.getProperties())) {
+            // TODO: use ConfigMap resource
+            context.addConfigurationResource("application.properties",
+                    
camelTrait.getProperties().stream().collect(Collectors.joining(System.lineSeparator())));
+        }
+    }
+}
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/main/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/traits/TraitCatalog.java
 
b/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/main/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/traits/TraitCatalog.java
index 017211d904b..01c00630b31 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/main/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/traits/TraitCatalog.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/main/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/traits/TraitCatalog.java
@@ -23,6 +23,7 @@ import java.util.Locale;
 import java.util.stream.Collectors;
 
 import 
org.apache.camel.dsl.jbang.core.commands.kubernetes.traits.knative.KnativeServiceTrait;
+import 
org.apache.camel.dsl.jbang.core.commands.kubernetes.traits.knative.KnativeTrait;
 import org.apache.camel.v1.integrationspec.Traits;
 
 /**
@@ -36,6 +37,7 @@ public class TraitCatalog {
 
     public TraitCatalog() {
         register(new DeploymentTrait());
+        register(new KnativeTrait());
         register(new KnativeServiceTrait());
         register(new ServiceTrait());
         register(new ContainerTrait());
@@ -44,6 +46,7 @@ public class TraitCatalog {
         register(new OpenApiTrait());
         register(new LabelTrait());
         register(new AnnotationTrait());
+        register(new CamelTrait());
     }
 
     public List<Trait> allTraits() {
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/main/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/traits/TraitContext.java
 
b/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/main/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/traits/TraitContext.java
index a4682171fcb..6607d93ae1c 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/main/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/traits/TraitContext.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/main/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/traits/TraitContext.java
@@ -23,8 +23,11 @@ import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Optional;
+import java.util.function.BiConsumer;
 import java.util.stream.Collectors;
 
+import io.fabric8.knative.eventing.v1.TriggerBuilder;
+import io.fabric8.knative.messaging.v1.SubscriptionBuilder;
 import io.fabric8.kubernetes.api.builder.Builder;
 import io.fabric8.kubernetes.api.builder.VisitableBuilder;
 import io.fabric8.kubernetes.api.builder.Visitor;
@@ -35,6 +38,8 @@ import 
io.fabric8.kubernetes.api.model.batch.v1.CronJobBuilder;
 import org.apache.camel.RuntimeCamelException;
 import org.apache.camel.catalog.CamelCatalog;
 import org.apache.camel.dsl.jbang.core.commands.kubernetes.CatalogHelper;
+import org.apache.camel.dsl.jbang.core.commands.kubernetes.MetadataHelper;
+import 
org.apache.camel.dsl.jbang.core.commands.kubernetes.support.SourceMetadata;
 import org.apache.camel.dsl.jbang.core.common.Printer;
 import org.apache.camel.dsl.jbang.core.common.RuntimeType;
 import org.apache.camel.dsl.jbang.core.common.Source;
@@ -44,6 +49,10 @@ public class TraitContext {
     private final List<VisitableBuilder<?, ?>> resourceRegistry = new 
ArrayList<>();
     private TraitProfile profile = TraitProfile.KUBERNETES;
 
+    private final Map<String, String> configurationResources = new HashMap<>();
+
+    private final Map<String, SourceMetadata> sourceMetadata = new HashMap<>();
+
     private final Map<String, String> annotations = new HashMap<>();
     private final Map<String, String> labels = new HashMap<>();
 
@@ -132,6 +141,23 @@ public class TraitContext {
                 .findFirst();
     }
 
+    public Optional<TriggerBuilder> getKnativeTrigger(String triggerName, 
String brokerName) {
+        return resourceRegistry.stream()
+                .filter(it -> 
it.getClass().isAssignableFrom(TriggerBuilder.class))
+                .map(it -> (TriggerBuilder) it)
+                .filter(trigger -> 
triggerName.equals(trigger.buildMetadata().getName()))
+                .filter(trigger -> 
brokerName.equals(trigger.buildSpec().getBroker()))
+                .findFirst();
+    }
+
+    public Optional<SubscriptionBuilder> getKnativeSubscription(String name) {
+        return resourceRegistry.stream()
+                .filter(it -> 
it.getClass().isAssignableFrom(SubscriptionBuilder.class))
+                .map(it -> (SubscriptionBuilder) it)
+                .filter(subscription -> 
name.equals(subscription.buildMetadata().getName()))
+                .findFirst();
+    }
+
     public String getName() {
         return name;
     }
@@ -188,4 +214,52 @@ public class TraitContext {
     public String getServiceAccount() {
         return serviceAccount;
     }
+
+    /**
+     * Performs source metadata inspection and uses local cache to not inspect 
the same source over and over again.
+     *
+     * @param  source    the source to inspect and create the metadata from.
+     * @return           the metadata holding information such as components, 
endpoints, languages used with the source.
+     * @throws Exception
+     */
+    public SourceMetadata inspectMetaData(Source source) throws Exception {
+        if (sourceMetadata.containsKey(source.name())) {
+            return sourceMetadata.get(source.name());
+        }
+
+        SourceMetadata metadata = MetadataHelper.readFromSource(catalog, 
source);
+        sourceMetadata.put(source.name(), metadata);
+        return metadata;
+    }
+
+    /**
+     * Inspect all sources in this context and retrieve the source metadata. 
Uses internal cache for sources that have
+     * already been inspected.
+     *
+     * @return
+     */
+    public List<SourceMetadata> getSourceMetadata() {
+        List<SourceMetadata> answer = new ArrayList<>();
+        if (sources != null) {
+            for (Source source : sources) {
+                answer.add(sourceMetadata.computeIfAbsent(source.name(), name 
-> {
+                    try {
+                        return MetadataHelper.readFromSource(catalog, source);
+                    } catch (Exception e) {
+                        throw new RuntimeCamelException(e);
+                    }
+                }));
+            }
+        }
+
+        return answer;
+    }
+
+    public void addConfigurationResource(String name, String content) {
+        this.configurationResources.put(name, content);
+    }
+
+    public void doWithConfigurationResources(BiConsumer<String, String> 
consumer) {
+        configurationResources.forEach(consumer);
+    }
 }
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 d4d65c38bb5..6e7a9bf32b2 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
@@ -327,7 +327,7 @@ public final class TraitHelper {
             CamelCatalog catalog = context.getCatalog();
             if (context.getSources() != null) {
                 for (Source source : context.getSources()) {
-                    SourceMetadata metadata = 
MetadataHelper.readFromSource(catalog, source);
+                    SourceMetadata metadata = context.inspectMetaData(source);
                     if (MetadataHelper.exposesHttpServices(catalog, metadata)) 
{
                         exposesHttpServices = true;
                         break;
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/main/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/traits/knative/KnativeTrait.java
 
b/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/main/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/traits/knative/KnativeTrait.java
new file mode 100644
index 00000000000..f92fd26152b
--- /dev/null
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/main/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/traits/knative/KnativeTrait.java
@@ -0,0 +1,509 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.camel.dsl.jbang.core.commands.kubernetes.traits.knative;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Optional;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import io.fabric8.knative.eventing.v1.TriggerBuilder;
+import io.fabric8.knative.eventing.v1.TriggerFilterBuilder;
+import io.fabric8.knative.internal.pkg.apis.duck.v1.DestinationBuilder;
+import io.fabric8.knative.internal.pkg.apis.duck.v1.KReference;
+import io.fabric8.knative.internal.pkg.apis.duck.v1.KReferenceBuilder;
+import io.fabric8.knative.internal.pkg.tracker.ReferenceBuilder;
+import io.fabric8.knative.messaging.v1.SubscriptionBuilder;
+import io.fabric8.knative.sources.v1.SinkBindingBuilder;
+import org.apache.camel.dsl.jbang.core.commands.kubernetes.KubernetesHelper;
+import 
org.apache.camel.dsl.jbang.core.commands.kubernetes.support.SourceMetadata;
+import org.apache.camel.dsl.jbang.core.commands.kubernetes.traits.ServiceTrait;
+import org.apache.camel.dsl.jbang.core.commands.kubernetes.traits.TraitContext;
+import org.apache.camel.util.ObjectHelper;
+import org.apache.camel.util.URISupport;
+import org.apache.camel.v1.integrationspec.Traits;
+import org.apache.camel.v1.integrationspec.traits.Camel;
+import org.apache.camel.v1.integrationspec.traits.Knative;
+
+public class KnativeTrait extends KnativeBaseTrait {
+
+    private static final Pattern knativeUriPattern
+            = 
Pattern.compile("^knative:/*(channel|endpoint|event)(?:[?].*|$|/([A-Za-z0-9.-]+)(?:[/?].*|$))");
+    private static final Pattern plainNamePattern = 
Pattern.compile("^[A-Za-z0-9.-]+$");
+    private static final String K_SINK_URL = 
"{{k.sink:http://localhost:8080}}";;
+
+    private final List<Map<String, Object>> knativeResourcesConfig = new 
ArrayList<>();
+
+    public KnativeTrait() {
+        super("knative", ServiceTrait.SERVICE_TRAIT_ORDER + 100);
+    }
+
+    @Override
+    public boolean configure(Traits traitConfig, TraitContext context) {
+        Knative knativeTrait = 
Optional.ofNullable(traitConfig.getKnative()).orElseGet(Knative::new);
+
+        if (knativeTrait.getEnabled() != null && !knativeTrait.getEnabled()) {
+            // Knative explicitly disabled
+            return false;
+        }
+
+        List<SourceMetadata> allSourcesMetadata = context.getSourceMetadata();
+
+        if (knativeTrait.getChannelSources() == null) {
+            
knativeTrait.setChannelSources(extractKnativeEndpointUris(allSourcesMetadata, 
KnativeResourceType.CHANNEL, "from"));
+        }
+
+        if (knativeTrait.getChannelSinks() == null) {
+            
knativeTrait.setChannelSinks(extractKnativeEndpointUris(allSourcesMetadata, 
KnativeResourceType.CHANNEL, "to"));
+        }
+
+        if (knativeTrait.getEndpointSources() == null) {
+            knativeTrait
+                    
.setEndpointSources(extractKnativeEndpointUris(allSourcesMetadata, 
KnativeResourceType.ENDPOINT, "from"));
+        }
+
+        if (knativeTrait.getEndpointSinks() == null) {
+            
knativeTrait.setEndpointSinks(extractKnativeEndpointUris(allSourcesMetadata, 
KnativeResourceType.ENDPOINT, "to"));
+        }
+
+        if (knativeTrait.getEventSources() == null) {
+            
knativeTrait.setEventSources(extractKnativeEndpointUris(allSourcesMetadata, 
KnativeResourceType.EVENT, "from"));
+        }
+
+        if (knativeTrait.getEventSinks() == null) {
+            
knativeTrait.setEventSinks(extractKnativeEndpointUris(allSourcesMetadata, 
KnativeResourceType.EVENT, "to"));
+        }
+
+        boolean hasKnativeEndpoint = 
!knativeTrait.getChannelSources().isEmpty() ||
+                !knativeTrait.getChannelSinks().isEmpty() ||
+                !knativeTrait.getEndpointSources().isEmpty() ||
+                !knativeTrait.getEndpointSinks().isEmpty() ||
+                !knativeTrait.getEventSources().isEmpty() ||
+                !knativeTrait.getEventSinks().isEmpty();
+
+        if (knativeTrait.getSinkBinding() == null) {
+            knativeTrait.setSinkBinding(shouldCreateSinkBinding(knativeTrait));
+        }
+
+        knativeTrait.setEnabled(hasKnativeEndpoint);
+        traitConfig.setKnative(knativeTrait);
+
+        return hasKnativeEndpoint;
+    }
+
+    @Override
+    public void apply(Traits traitConfig, TraitContext context) {
+        Knative knativeTrait = 
Optional.ofNullable(traitConfig.getKnative()).orElseGet(Knative::new);
+
+        configureChannels(knativeTrait, context);
+        configureEndpoints(knativeTrait, context);
+        configureEvents(knativeTrait, context);
+
+        if (knativeTrait.getSinkBinding() != null && 
knativeTrait.getSinkBinding()) {
+            createSinkBinding(knativeTrait, context);
+        }
+
+        if (!knativeResourcesConfig.isEmpty()) {
+            try {
+                context.addConfigurationResource("knative.json", 
KubernetesHelper.json()
+                        .writerWithDefaultPrettyPrinter()
+                        
.writeValueAsString(Collections.singletonMap("resources", 
knativeResourcesConfig)));
+                Camel camelTrait = 
Optional.ofNullable(traitConfig.getCamel()).orElseGet(Camel::new);
+                if (camelTrait.getProperties() == null) {
+                    camelTrait.setProperties(new ArrayList<>());
+                }
+                
camelTrait.getProperties().add("camel.component.knative.environmentPath=classpath:knative.json");
+                traitConfig.setCamel(camelTrait);
+            } catch (JsonProcessingException e) {
+                context.printer().printf("Failed to write knative.json 
environment configuration - %s%n", e.getMessage());
+            }
+        }
+    }
+
+    private void configureChannels(Knative knativeTrait, TraitContext context) 
{
+        for (String uri : knativeTrait.getChannelSources()) {
+            createSubscription(toKnativeUri(KnativeResourceType.CHANNEL, uri), 
knativeTrait, context);
+        }
+
+        for (String uri : knativeTrait.getChannelSinks()) {
+            String channelName = extractKnativeResource(uri);
+            addKnativeResourceConfiguration(new KnativeResourceConfiguration(
+                    channelName,
+                    KnativeResourceType.CHANNEL,
+                    "sink",
+                    K_SINK_URL,
+                    null,
+                    channelName,
+                    null));
+        }
+    }
+
+    private void configureEndpoints(Knative knativeTrait, TraitContext 
context) {
+        for (String uri : knativeTrait.getEndpointSources()) {
+            String endpointName = extractKnativeResource(uri);
+            addKnativeResourceConfiguration(new KnativeResourceConfiguration(
+                    endpointName,
+                    KnativeResourceType.ENDPOINT,
+                    "source",
+                    null,
+                    "/",
+                    endpointName,
+                    null));
+        }
+
+        for (String uri : knativeTrait.getEndpointSinks()) {
+            String endpointName = extractKnativeResource(uri);
+            addKnativeResourceConfiguration(new KnativeResourceConfiguration(
+                    endpointName,
+                    KnativeResourceType.ENDPOINT,
+                    "sink",
+                    K_SINK_URL,
+                    null,
+                    endpointName,
+                    null));
+        }
+    }
+
+    private void configureEvents(Knative knativeTrait, TraitContext context) {
+        for (String uri : knativeTrait.getEventSources()) {
+            createTrigger(toKnativeUri(KnativeResourceType.EVENT, uri), 
knativeTrait, context);
+        }
+
+        for (String uri : knativeTrait.getEventSinks()) {
+            String eventType = extractKnativeResource(uri);
+            String brokerName = extractBrokerName(uri);
+            String serviceName = ObjectHelper.isNotEmpty(eventType) ? 
brokerName : "default";
+            addKnativeResourceConfiguration(new KnativeResourceConfiguration(
+                    serviceName,
+                    KnativeResourceType.EVENT,
+                    "sink",
+                    K_SINK_URL,
+                    null,
+                    brokerName,
+                    null));
+        }
+    }
+
+    private void createSubscription(String uri, Knative knativeTrait, 
TraitContext context) {
+        String channelName = extractKnativeResource(uri);
+
+        String subscriptionName = createSubscriptionName(context.getName(), 
channelName);
+        if (ObjectHelper.isEmpty(channelName) || 
context.getKnativeSubscription(subscriptionName).isPresent()) {
+            // no channel name given or same subscription already exists
+            return;
+        }
+
+        String servicePath = "/channels/%s".formatted(channelName);
+
+        SubscriptionBuilder subscription = new SubscriptionBuilder()
+                .withNewMetadata()
+                .withName(subscriptionName)
+                .endMetadata()
+                .withNewSpec()
+                .withChannel(new KReferenceBuilder()
+                        
.withApiVersion(KnativeResourceType.CHANNEL.getApiVersion())
+                        .withKind(KnativeResourceType.CHANNEL.getKind())
+                        .withName(channelName)
+                        .build())
+                .withSubscriber(new DestinationBuilder()
+                        .withRef(getSubscriberRef(context))
+                        .withUri(servicePath)
+                        .build())
+                .endSpec();
+
+        context.add(subscription);
+
+        addKnativeResourceConfiguration(new KnativeResourceConfiguration(
+                channelName,
+                KnativeResourceType.CHANNEL,
+                "source",
+                null,
+                servicePath,
+                channelName,
+                null));
+    }
+
+    private void createTrigger(String uri, Knative knativeTrait, TraitContext 
context) {
+        String eventType = extractKnativeResource(uri);
+        String brokerName = extractBrokerName(uri);
+
+        String triggerName = createTriggerName(context.getName(), brokerName, 
eventType);
+        if (context.getKnativeTrigger(triggerName, brokerName).isPresent()) {
+            // same trigger already exists
+            return;
+        }
+
+        String servicePath = "/events/%s".formatted(eventType);
+
+        TriggerBuilder trigger = new TriggerBuilder()
+                .withNewMetadata()
+                .withName(triggerName)
+                .endMetadata()
+                .withNewSpec()
+                .withBroker(brokerName)
+                .withSubscriber(new DestinationBuilder()
+                        .withRef(getSubscriberRef(context))
+                        .withUri(servicePath)
+                        .build())
+                .withFilter(new 
TriggerFilterBuilder().addToAttributes(getFilterAttributes(knativeTrait, 
eventType)).build())
+                .endSpec();
+
+        context.add(trigger);
+
+        String serviceName = ObjectHelper.isNotEmpty(eventType) ? eventType : 
"default";
+        addKnativeResourceConfiguration(new KnativeResourceConfiguration(
+                serviceName,
+                KnativeResourceType.EVENT,
+                "source",
+                null,
+                servicePath,
+                brokerName,
+                null));
+    }
+
+    private Map<String, String> getFilterAttributes(Knative knativeTrait, 
String eventType) {
+        Map<String, String> filterAttributes = new HashMap<>();
+        filterAttributes.put("type", eventType);
+
+        // TODO: use this as soon as new Camel K CRD model has been released
+        //        for (String filterExpression : knativeTrait.getFilters()) {
+        //            String[] keyValue = filterExpression.split("=", 2);
+        //            if (keyValue.length != 2) {
+        //                throw new RuntimeCamelException("Invalid Knative 
trigger filter expression: %s".formatted(filterExpression));
+        //            }
+        //            filterAttributes.put(keyValue[0].trim(), 
keyValue[1].trim());
+        //        }
+        //
+        //        if (!filterAttributes.containsKey("type") && 
Optional.ofNullable(knativeTrait.getFilterEventType()).orElse(true) && 
ObjectHelper.isNotEmpty(eventType)) {
+        //            // Apply default trigger filter attribute for the event 
type
+        //            filterAttributes.put("type", eventType);
+        //        }
+
+        return filterAttributes;
+    }
+
+    /**
+     * Adds service endpoint to the Knative component environment 
configuration.
+     */
+    private void addKnativeResourceConfiguration(KnativeResourceConfiguration 
configuration) {
+        knativeResourcesConfig.add(configuration.toJsonMap());
+    }
+
+    /**
+     * Create subscriber reference based on which service type has been set on 
the give context. Assumes that the either
+     * Knative service or arbitrary service has already been set on the 
context due to trait execution ordering. If
+     * Knative service is present use this service subscriber kind otherwise 
use arbitrary service subscriber reference.
+     */
+    private static KReference getSubscriberRef(TraitContext context) {
+        if (context.getKnativeService().isPresent()) {
+            return new KReferenceBuilder()
+                    
.withApiVersion(KnativeResourceType.ENDPOINT.getApiVersion())
+                    .withKind(KnativeResourceType.ENDPOINT.getKind())
+                    .withName(context.getName())
+                    .build();
+        } else {
+            return new KReferenceBuilder()
+                    .withApiVersion("v1")
+                    .withKind("Service")
+                    .withName(context.getName())
+                    .build();
+        }
+    }
+
+    private static String createTriggerName(String subscriberName, String 
brokerName, String eventType) {
+        String nameSuffix = "";
+        if (ObjectHelper.isNotEmpty(eventType)) {
+            nameSuffix = "-%s".formatted(KubernetesHelper.sanitize(eventType));
+        }
+
+        return KubernetesHelper.sanitize(brokerName) + "-" + subscriberName + 
nameSuffix;
+    }
+
+    private void createSinkBinding(Knative knativeTrait, TraitContext context) 
{
+        final KnativeResourceType resourceType;
+        final String uri;
+        final String resourceName;
+        if (!knativeTrait.getChannelSinks().isEmpty()) {
+            uri = knativeTrait.getChannelSinks().get(0);
+            resourceType = KnativeResourceType.CHANNEL;
+            resourceName = extractKnativeResource(uri);
+        } else if (!knativeTrait.getEndpointSinks().isEmpty()) {
+            uri = knativeTrait.getEndpointSinks().get(0);
+            resourceType = KnativeResourceType.ENDPOINT;
+            resourceName = extractKnativeResource(uri);
+        } else if (!knativeTrait.getEventSinks().isEmpty()) {
+            uri = knativeTrait.getEventSinks().get(0);
+            resourceType = KnativeResourceType.EVENT;
+            resourceName = extractBrokerName(uri);
+        } else {
+            context.printer().println("Failed to create sink binding!");
+            return;
+        }
+
+        context.add(new SinkBindingBuilder()
+                .withNewMetadata()
+                .withName(context.getName())
+                .withFinalizers("sinkbindings.sources.knative.dev")
+                .endMetadata()
+                .withNewSpec()
+                .withSubject(new ReferenceBuilder()
+                        .withApiVersion("apps/v1")
+                        .withKind("Deployment")
+                        .withName(context.getName())
+                        .build())
+                .withNewSink()
+                .withRef(resourceType.getReference(resourceName))
+                .endSink()
+                .endSpec());
+    }
+
+    private static String createSubscriptionName(String subscriberName, String 
channelName) {
+        return "%s-%s".formatted(channelName, subscriberName);
+    }
+
+    private static String extractBrokerName(String uri) {
+        try {
+            return 
URISupport.parseQuery(URISupport.extractQuery(uri)).getOrDefault("name", 
"default").toString();
+        } catch (Exception e) {
+            return "default";
+        }
+    }
+
+    /**
+     * Sink binding should be created only when a single Knative sink is being 
used.
+     *
+     * @param  knativeTrait holding the sinks for channels, endpoints and 
events.
+     * @return              true when single Knative sink is used, otherwise 
false.
+     */
+    private static boolean shouldCreateSinkBinding(Knative knativeTrait) {
+        return knativeTrait.getChannelSinks().size() + 
knativeTrait.getEndpointSinks().size()
+               + knativeTrait.getEventSinks().size() == 1;
+    }
+
+    private static List<String> extractKnativeEndpointUris(
+            List<SourceMetadata> metadata, KnativeResourceType resourceType, 
String endpointType) {
+        return metadata.stream()
+                .flatMap(m -> endpointType.equals("from") ? 
m.endpoints.from.stream() : m.endpoints.to.stream())
+                .filter(uri -> isKnativeUri(resourceType, uri))
+                .collect(Collectors.toList());
+    }
+
+    private static String extractKnativeResource(String uri) {
+        Matcher uriMatch = knativeUriPattern.matcher(uri);
+        if (uriMatch.matches()) {
+            return Optional.ofNullable(uriMatch.group(2)).orElse("");
+        }
+
+        return "";
+    }
+
+    private static boolean isKnativeUri(KnativeResourceType resourceType, 
String uri) {
+        Matcher uriMatcher = knativeUriPattern.matcher(uri);
+        return uriMatcher.matches() && 
uriMatcher.group(1).equals(resourceType.getType());
+    }
+
+    /**
+     * If applicable, converts plain service, channel or broker name to 
Knative component endpoint URI.
+     */
+    private static String toKnativeUri(KnativeResourceType resourceType, 
String uriOrName) {
+        if (plainNamePattern.matcher(uriOrName).matches()) {
+            return "knative://%s/%s".formatted(resourceType.getType(), 
uriOrName);
+        }
+
+        return uriOrName;
+    }
+
+    enum KnativeResourceType {
+        CHANNEL("Channel", "messaging.knative.dev/v1"),
+        ENDPOINT("Service", "serving.knative.dev/v1"),
+        EVENT("Broker", "eventing.knative.dev/v1");
+
+        private final String kind;
+        private final String apiVersion;
+
+        KnativeResourceType(String kind, String apiVersion) {
+            this.kind = kind;
+            this.apiVersion = apiVersion;
+        }
+
+        public String getKind() {
+            return kind;
+        }
+
+        public String getApiVersion() {
+            return apiVersion;
+        }
+
+        public String getType() {
+            return this.name().toLowerCase(Locale.US);
+        }
+
+        public KReference getReference(String resourceName) {
+            return new KReferenceBuilder()
+                    .withApiVersion(apiVersion)
+                    .withKind(kind)
+                    .withName(resourceName)
+                    .build();
+        }
+    }
+
+    private record KnativeResourceConfiguration(String name, 
KnativeResourceType resourceType, String endpointKind, String url,
+            String path, String objectName, Map<String, String> ceOverrides) {
+
+        public Map<String, Object> toJsonMap() {
+            Map<String, Object> json = new LinkedHashMap<>();
+
+            json.put("name", name);
+            json.put("type", resourceType.getType());
+            json.put("endpointKind", endpointKind);
+
+            if (ObjectHelper.isNotEmpty(url)) {
+                json.put("url", url);
+            }
+
+            if (ObjectHelper.isNotEmpty(path)) {
+                json.put("path", path);
+            }
+
+            json.put("objectApiVersion", resourceType.getApiVersion());
+            json.put("objectKind", resourceType.getKind());
+            json.put("objectName", objectName);
+
+            if (ObjectHelper.isNotEmpty(ceOverrides)) {
+                json.put("ceOverrides", ceOverrides);
+            }
+
+            // knative.reply is set to true in case of endpoint service as a 
source
+            if (resourceType == KnativeResourceType.ENDPOINT && 
endpointKind.equals("source")) {
+                json.put("reply", true);
+            } else {
+                json.put("reply", false);
+            }
+
+            return json;
+        }
+    }
+}
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 8d7918ac5d7..6e75daf6bff 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
@@ -17,18 +17,24 @@
 
 package org.apache.camel.dsl.jbang.core.commands.kubernetes;
 
+import java.io.ByteArrayInputStream;
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileReader;
 import java.io.IOException;
+import java.nio.charset.StandardCharsets;
 import java.nio.file.Files;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
 import java.util.Map;
 import java.util.Optional;
+import java.util.Properties;
 import java.util.stream.Stream;
 
+import io.fabric8.knative.eventing.v1.Trigger;
+import io.fabric8.knative.messaging.v1.Subscription;
+import io.fabric8.knative.sources.v1.SinkBinding;
 import io.fabric8.kubernetes.api.model.HasMetadata;
 import io.fabric8.kubernetes.api.model.Service;
 import io.fabric8.kubernetes.api.model.apps.Deployment;
@@ -36,6 +42,7 @@ import org.apache.camel.RuntimeCamelException;
 import org.apache.camel.dsl.jbang.core.commands.CamelJBangMain;
 import org.apache.camel.dsl.jbang.core.commands.kubernetes.traits.BaseTrait;
 import org.apache.camel.dsl.jbang.core.common.RuntimeType;
+import org.apache.camel.util.IOHelper;
 import org.apache.maven.model.Model;
 import org.apache.maven.model.io.xpp3.MavenXpp3Reader;
 import org.junit.jupiter.api.Assertions;
@@ -108,6 +115,36 @@ class KubernetesExportTest extends KubernetesBaseTest {
         Assertions.assertFalse(hasKnativeService(rt));
     }
 
+    @ParameterizedTest
+    @MethodSource("runtimeProvider")
+    public void shouldAddApplicationProperties(RuntimeType rt) throws 
Exception {
+        KubernetesExport command = createCommand(new String[] { 
"classpath:route.yaml" },
+                "--image-group=camel-test", "--runtime=" + rt.runtime());
+        command.traits = new String[] {
+                "camel.properties=[foo=bar, bar=baz]" };
+        int exit = command.doCall();
+
+        Assertions.assertEquals(0, exit);
+        Deployment deployment = getDeployment(rt);
+        Assertions.assertEquals("route", deployment.getMetadata().getName());
+        Assertions.assertEquals(1, 
deployment.getSpec().getTemplate().getSpec().getContainers().size());
+        Assertions.assertEquals("route", 
deployment.getMetadata().getLabels().get(BaseTrait.INTEGRATION_LABEL));
+        Assertions.assertEquals("route", 
deployment.getSpec().getTemplate().getSpec().getContainers().get(0).getName());
+        Assertions.assertEquals(1, 
deployment.getSpec().getSelector().getMatchLabels().size());
+        Assertions.assertEquals("route",
+                
deployment.getSpec().getSelector().getMatchLabels().get(BaseTrait.INTEGRATION_LABEL));
+        Assertions.assertEquals("camel-test/route:1.0-SNAPSHOT",
+                
deployment.getSpec().getTemplate().getSpec().getContainers().get(0).getImage());
+
+        Assertions.assertFalse(hasService(rt));
+        Assertions.assertFalse(hasKnativeService(rt));
+
+        Properties applicationProperties = 
getApplicationProperties(workingDir);
+
+        Assertions.assertEquals("bar", applicationProperties.get("foo"));
+        Assertions.assertEquals("baz", applicationProperties.get("bar"));
+    }
+
     @ParameterizedTest
     @MethodSource("runtimeProvider")
     public void shouldAddServiceSpec(RuntimeType rt) throws Exception {
@@ -235,6 +272,206 @@ class KubernetesExportTest extends KubernetesBaseTest {
                 
service.getSpec().getTemplate().getMetadata().getAnnotations().get("autoscaling.knative.dev/maxScale"));
     }
 
+    @ParameterizedTest
+    @MethodSource("runtimeProvider")
+    public void shouldAddKnativeTrigger(RuntimeType rt) throws Exception {
+        KubernetesExport command = createCommand(new String[] { 
"classpath:knative-event-source.yaml" },
+                "--image-group=camel-test", "--runtime=" + rt.runtime());
+        command.doCall();
+
+        Assertions.assertTrue(hasService(rt));
+        Assertions.assertFalse(hasKnativeService(rt));
+
+        Trigger trigger = getResource(rt, Trigger.class)
+                .orElseThrow(() -> new RuntimeCamelException("Missing Knative 
trigger in Kubernetes manifest"));
+
+        Assertions.assertEquals("my-broker-knative-event-source-camel-event", 
trigger.getMetadata().getName());
+        Assertions.assertEquals("my-broker", trigger.getSpec().getBroker());
+        Assertions.assertEquals(1, 
trigger.getSpec().getFilter().getAttributes().size());
+        Assertions.assertEquals("camel-event", 
trigger.getSpec().getFilter().getAttributes().get("type"));
+        Assertions.assertEquals("knative-event-source", 
trigger.getSpec().getSubscriber().getRef().getName());
+        Assertions.assertEquals("Service", 
trigger.getSpec().getSubscriber().getRef().getKind());
+        Assertions.assertEquals("v1", 
trigger.getSpec().getSubscriber().getRef().getApiVersion());
+        Assertions.assertEquals("/events/camel-event", 
trigger.getSpec().getSubscriber().getUri());
+
+        Properties applicationProperties = 
getApplicationProperties(workingDir);
+        Assertions.assertEquals("classpath:knative.json", 
applicationProperties.get("camel.component.knative.environmentPath"));
+
+        Assertions.assertEquals("""
+                {
+                  "resources" : [ {
+                    "name" : "camel-event",
+                    "type" : "event",
+                    "endpointKind" : "source",
+                    "path" : "/events/camel-event",
+                    "objectApiVersion" : "eventing.knative.dev/v1",
+                    "objectKind" : "Broker",
+                    "objectName" : "my-broker",
+                    "reply" : false
+                  } ]
+                }
+                """, getKnativeResourceConfiguration(workingDir));
+    }
+
+    @ParameterizedTest
+    @MethodSource("runtimeProvider")
+    public void shouldAddKnativeSubscription(RuntimeType rt) throws Exception {
+        KubernetesExport command = createCommand(new String[] { 
"classpath:knative-channel-source.yaml" },
+                "--image-group=camel-test", "--runtime=" + rt.runtime());
+        command.doCall();
+
+        Assertions.assertTrue(hasService(rt));
+        Assertions.assertFalse(hasKnativeService(rt));
+
+        Subscription subscription = getResource(rt, Subscription.class)
+                .orElseThrow(() -> new RuntimeCamelException("Missing Knative 
subscription in Kubernetes manifest"));
+
+        Assertions.assertEquals("my-channel-knative-channel-source", 
subscription.getMetadata().getName());
+        Assertions.assertEquals("my-channel", 
subscription.getSpec().getChannel().getName());
+        Assertions.assertEquals("knative-channel-source", 
subscription.getSpec().getSubscriber().getRef().getName());
+        Assertions.assertEquals("Service", 
subscription.getSpec().getSubscriber().getRef().getKind());
+        Assertions.assertEquals("v1", 
subscription.getSpec().getSubscriber().getRef().getApiVersion());
+        Assertions.assertEquals("/channels/my-channel", 
subscription.getSpec().getSubscriber().getUri());
+
+        Properties applicationProperties = 
getApplicationProperties(workingDir);
+        Assertions.assertEquals("classpath:knative.json", 
applicationProperties.get("camel.component.knative.environmentPath"));
+
+        Assertions.assertEquals("""
+                {
+                  "resources" : [ {
+                    "name" : "my-channel",
+                    "type" : "channel",
+                    "endpointKind" : "source",
+                    "path" : "/channels/my-channel",
+                    "objectApiVersion" : "messaging.knative.dev/v1",
+                    "objectKind" : "Channel",
+                    "objectName" : "my-channel",
+                    "reply" : false
+                  } ]
+                }
+                """, getKnativeResourceConfiguration(workingDir));
+    }
+
+    @ParameterizedTest
+    @MethodSource("runtimeProvider")
+    public void shouldAddKnativeBrokerSinkBinding(RuntimeType rt) throws 
Exception {
+        KubernetesExport command = createCommand(new String[] { 
"classpath:knative-event-sink.yaml" },
+                "--image-group=camel-test", "--runtime=" + rt.runtime());
+        command.doCall();
+
+        Assertions.assertFalse(hasService(rt));
+        Assertions.assertFalse(hasKnativeService(rt));
+
+        SinkBinding sinkBinding = getResource(rt, SinkBinding.class)
+                .orElseThrow(() -> new RuntimeCamelException("Missing Knative 
sinkBinding in Kubernetes manifest"));
+
+        Assertions.assertEquals("knative-event-sink", 
sinkBinding.getMetadata().getName());
+        Assertions.assertEquals("my-broker", 
sinkBinding.getSpec().getSink().getRef().getName());
+        Assertions.assertEquals("Broker", 
sinkBinding.getSpec().getSink().getRef().getKind());
+        Assertions.assertEquals("eventing.knative.dev/v1", 
sinkBinding.getSpec().getSink().getRef().getApiVersion());
+        Assertions.assertEquals("knative-event-sink", 
sinkBinding.getSpec().getSubject().getName());
+        Assertions.assertEquals("Deployment", 
sinkBinding.getSpec().getSubject().getKind());
+        Assertions.assertEquals("apps/v1", 
sinkBinding.getSpec().getSubject().getApiVersion());
+
+        Properties applicationProperties = 
getApplicationProperties(workingDir);
+        Assertions.assertEquals("classpath:knative.json", 
applicationProperties.get("camel.component.knative.environmentPath"));
+
+        Assertions.assertEquals("""
+                {
+                  "resources" : [ {
+                    "name" : "my-broker",
+                    "type" : "event",
+                    "endpointKind" : "sink",
+                    "url" : "{{k.sink:http://localhost:8080}}";,
+                    "objectApiVersion" : "eventing.knative.dev/v1",
+                    "objectKind" : "Broker",
+                    "objectName" : "my-broker",
+                    "reply" : false
+                  } ]
+                }
+                """, getKnativeResourceConfiguration(workingDir));
+    }
+
+    @ParameterizedTest
+    @MethodSource("runtimeProvider")
+    public void shouldAddKnativeChannelSinkBinding(RuntimeType rt) throws 
Exception {
+        KubernetesExport command = createCommand(new String[] { 
"classpath:knative-channel-sink.yaml" },
+                "--image-group=camel-test", "--runtime=" + rt.runtime());
+        command.doCall();
+
+        Assertions.assertFalse(hasService(rt));
+        Assertions.assertFalse(hasKnativeService(rt));
+
+        SinkBinding sinkBinding = getResource(rt, SinkBinding.class)
+                .orElseThrow(() -> new RuntimeCamelException("Missing Knative 
sinkBinding in Kubernetes manifest"));
+
+        Assertions.assertEquals("knative-channel-sink", 
sinkBinding.getMetadata().getName());
+        Assertions.assertEquals("my-channel", 
sinkBinding.getSpec().getSink().getRef().getName());
+        Assertions.assertEquals("Channel", 
sinkBinding.getSpec().getSink().getRef().getKind());
+        Assertions.assertEquals("messaging.knative.dev/v1", 
sinkBinding.getSpec().getSink().getRef().getApiVersion());
+        Assertions.assertEquals("knative-channel-sink", 
sinkBinding.getSpec().getSubject().getName());
+        Assertions.assertEquals("Deployment", 
sinkBinding.getSpec().getSubject().getKind());
+        Assertions.assertEquals("apps/v1", 
sinkBinding.getSpec().getSubject().getApiVersion());
+
+        Properties applicationProperties = 
getApplicationProperties(workingDir);
+        Assertions.assertEquals("classpath:knative.json", 
applicationProperties.get("camel.component.knative.environmentPath"));
+
+        Assertions.assertEquals("""
+                {
+                  "resources" : [ {
+                    "name" : "my-channel",
+                    "type" : "channel",
+                    "endpointKind" : "sink",
+                    "url" : "{{k.sink:http://localhost:8080}}";,
+                    "objectApiVersion" : "messaging.knative.dev/v1",
+                    "objectKind" : "Channel",
+                    "objectName" : "my-channel",
+                    "reply" : false
+                  } ]
+                }
+                """, getKnativeResourceConfiguration(workingDir));
+    }
+
+    @ParameterizedTest
+    @MethodSource("runtimeProvider")
+    public void shouldAddKnativeEndpointSinkBinding(RuntimeType rt) throws 
Exception {
+        KubernetesExport command = createCommand(new String[] { 
"classpath:knative-endpoint-sink.yaml" },
+                "--image-group=camel-test", "--runtime=" + rt.runtime());
+        command.doCall();
+
+        Assertions.assertFalse(hasService(rt));
+        Assertions.assertFalse(hasKnativeService(rt));
+
+        SinkBinding sinkBinding = getResource(rt, SinkBinding.class)
+                .orElseThrow(() -> new RuntimeCamelException("Missing Knative 
sinkBinding in Kubernetes manifest"));
+
+        Assertions.assertEquals("knative-endpoint-sink", 
sinkBinding.getMetadata().getName());
+        Assertions.assertEquals("my-endpoint", 
sinkBinding.getSpec().getSink().getRef().getName());
+        Assertions.assertEquals("Service", 
sinkBinding.getSpec().getSink().getRef().getKind());
+        Assertions.assertEquals("serving.knative.dev/v1", 
sinkBinding.getSpec().getSink().getRef().getApiVersion());
+        Assertions.assertEquals("knative-endpoint-sink", 
sinkBinding.getSpec().getSubject().getName());
+        Assertions.assertEquals("Deployment", 
sinkBinding.getSpec().getSubject().getKind());
+        Assertions.assertEquals("apps/v1", 
sinkBinding.getSpec().getSubject().getApiVersion());
+
+        Properties applicationProperties = 
getApplicationProperties(workingDir);
+        Assertions.assertEquals("classpath:knative.json", 
applicationProperties.get("camel.component.knative.environmentPath"));
+
+        Assertions.assertEquals("""
+                {
+                  "resources" : [ {
+                    "name" : "my-endpoint",
+                    "type" : "endpoint",
+                    "endpointKind" : "sink",
+                    "url" : "{{k.sink:http://localhost:8080}}";,
+                    "objectApiVersion" : "serving.knative.dev/v1",
+                    "objectKind" : "Service",
+                    "objectName" : "my-endpoint",
+                    "reply" : false
+                  } ]
+                }
+                """, getKnativeResourceConfiguration(workingDir));
+    }
+
     @ParameterizedTest
     @MethodSource("runtimeProvider")
     public void shouldAddVolumes(RuntimeType rt) throws Exception {
@@ -447,6 +684,23 @@ class KubernetesExportTest extends KubernetesBaseTest {
         return Optional.empty();
     }
 
+    private String readResource(File workingDir, String path) throws 
IOException {
+        try (FileInputStream fis = new 
FileInputStream(workingDir.toPath().resolve(path).toFile())) {
+            return IOHelper.loadText(fis);
+        }
+    }
+
+    private Properties getApplicationProperties(File workingDir) throws 
IOException {
+        String content = readResource(workingDir, 
"src/main/resources/application.properties");
+        Properties applicationProperties = new Properties();
+        applicationProperties.load(new 
ByteArrayInputStream(content.getBytes(StandardCharsets.UTF_8)));
+        return applicationProperties;
+    }
+
+    private String getKnativeResourceConfiguration(File workingDir) throws 
IOException {
+        return readResource(workingDir, "src/main/resources/knative.json");
+    }
+
     private Model readMavenModel() throws Exception {
         File f = workingDir.toPath().resolve("pom.xml").toFile();
         Assertions.assertTrue(f.isFile(), "Not a pom.xml file: " + f);
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/test/resources/knative-channel-sink.yaml
 
b/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/test/resources/knative-channel-sink.yaml
new file mode 100644
index 00000000000..8ab97dad8fe
--- /dev/null
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/test/resources/knative-channel-sink.yaml
@@ -0,0 +1,23 @@
+#
+# 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:
+    uri: timer:tick
+    steps:
+      - setBody:
+          constant: Hello Camel !!!
+      - to: knative:channel/my-channel
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/test/resources/knative-channel-source.yaml
 
b/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/test/resources/knative-channel-source.yaml
new file mode 100644
index 00000000000..e5ec605b038
--- /dev/null
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/test/resources/knative-channel-source.yaml
@@ -0,0 +1,21 @@
+#
+# 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:
+    uri: knative:channel/my-channel
+    steps:
+      - to: log:info
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/test/resources/knative-endpoint-sink.yaml
 
b/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/test/resources/knative-endpoint-sink.yaml
new file mode 100644
index 00000000000..db73e3191e2
--- /dev/null
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/test/resources/knative-endpoint-sink.yaml
@@ -0,0 +1,23 @@
+#
+# 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:
+    uri: timer:tick
+    steps:
+      - setBody:
+          constant: Hello Camel !!!
+      - to: knative:endpoint/my-endpoint
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/test/resources/knative-event-sink.yaml
 
b/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/test/resources/knative-event-sink.yaml
new file mode 100644
index 00000000000..fe58b60a7ea
--- /dev/null
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/test/resources/knative-event-sink.yaml
@@ -0,0 +1,23 @@
+#
+# 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:
+    uri: timer:tick
+    steps:
+      - setBody:
+          constant: Hello Camel !!!
+      - to: knative:event/camel-event?name=my-broker
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/test/resources/knative-event-source.yaml
 
b/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/test/resources/knative-event-source.yaml
new file mode 100644
index 00000000000..24e1d35cecf
--- /dev/null
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/test/resources/knative-event-source.yaml
@@ -0,0 +1,21 @@
+#
+# 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:
+    uri: knative:event/camel-event?name=my-broker
+    steps:
+      - to: log:info
diff --git 
a/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/download/DependencyDownloaderRoutesLoader.java
 
b/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/download/DependencyDownloaderRoutesLoader.java
index 5539d1642fb..6a81aaa6ecc 100644
--- 
a/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/download/DependencyDownloaderRoutesLoader.java
+++ 
b/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/download/DependencyDownloaderRoutesLoader.java
@@ -74,6 +74,10 @@ public class DependencyDownloaderRoutesLoader extends 
DefaultRoutesLoader {
         // special for kamelet as we want to track loading kamelets
         RoutesBuilderLoader loader;
         if (KameletRoutesBuilderLoader.EXTENSION.equals(extension)) {
+            if (!downloader.alreadyOnClasspath("org.apache.camel.kamelets", 
"camel-kamelets-catalog", kameletsVersion)) {
+                downloader.downloadDependency("org.apache.camel.kamelets", 
"camel-kamelets-catalog", kameletsVersion);
+            }
+
             loader = new KnownKameletRoutesBuilderLoader(kameletsVersion);
             CamelContextAware.trySetCamelContext(loader, getCamelContext());
             // allows for custom initialization

Reply via email to