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 1abb6c1368f CAMEL-20606: Create pipe on cluster with bind command
1abb6c1368f is described below

commit 1abb6c1368ff9cea9c61c749d9266c66a8bb9843
Author: Christoph Deppisch <cdeppi...@redhat.com>
AuthorDate: Sun Mar 24 11:49:04 2024 +0100

    CAMEL-20606: Create pipe on cluster with bind command
    
    - By default create pipe on the cluster with Camel K JBang plugin
    - Add wait and logs option to align with Camel K integration run command
    - Improve unit tests
    - Add bind command documentation
---
 .../modules/ROOT/pages/camel-jbang-k.adoc          | 321 ++++++++++++++++++++-
 .../modules/ROOT/pages/camel-jbang.adoc            | 124 +++++++-
 .../apache/camel/dsl/jbang/core/commands/Bind.java |   2 +-
 .../camel/dsl/jbang/core/commands/BindTest.java    | 181 ++++--------
 .../camel/dsl/jbang/core/commands/k/Bind.java      |  53 +++-
 .../dsl/jbang/core/commands/k/IntegrationLogs.java |   8 +-
 .../dsl/jbang/core/commands/k/IntegrationRun.java  |   9 +-
 .../camel/dsl/jbang/core/commands/k/BindTest.java  | 103 +++++--
 .../jbang/core/commands/k/IntegrationRunTest.java  |  12 +
 .../dsl/jbang/core/commands/k/KubeBaseTest.java    |  25 ++
 .../camel/dsl/jbang/core/commands/k/pipe.yaml      |  38 +++
 11 files changed, 691 insertions(+), 185 deletions(-)

diff --git a/docs/user-manual/modules/ROOT/pages/camel-jbang-k.adoc 
b/docs/user-manual/modules/ROOT/pages/camel-jbang-k.adoc
index 4cca0a82c0f..2eeff9fc6b7 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-jbang-k.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-jbang-k.adoc
@@ -24,6 +24,9 @@ camel plugin get
 ----
 
 Now Camel JBang is able to run the subcommands offered by the plugin.
+
+== Run integrations
+
 Simply run the integration using the `k` subcommand in Camel JBang.
 
 [source,bash]
@@ -110,7 +113,7 @@ The run command offers a lot more options that you may use 
to configure the Came
 |Add a label to the integration. Use name values pairs like "--label 
my.company=hello".
 
 |--traits, -t
-|Add a label to the integration. Use name values pairs like "--label 
my.company=hello".
+|Add a trait configuration to the integration. Use name values pairs like 
"--trait trait.name.config=hello".
 
 |--use-flows
 |Write yaml sources as Flow objects in the integration custom resource 
(default=true).
@@ -128,6 +131,8 @@ The run command offers a lot more options that you may use 
to configure the Came
 |Just output the generated integration custom resource (supports: yaml or 
json).
 |=======================================================================
 
+== List integrations
+
 You can list the available integration resources with the following command.
 
 [source,bash]
@@ -139,6 +144,8 @@ my-route  Running  kit-123456789   1/1
 
 This looks for all integrations in the current namespace and lists their 
individual status.
 
+== Show integration logs
+
 To inspect the log output of a running integration call:
 
 [source,bash]
@@ -149,6 +156,8 @@ camel k logs my-route
 The command connects to the running integration Pod and prints the log output.
 Just terminate the process to stop printing the logs.
 
+== Delete integrations
+
 Of course, you may also delete an integration resource from the cluster.
 
 [source,bash]
@@ -162,3 +171,313 @@ To remove all available integrations on the current 
namespace use the `--all` op
 ----
 camel k delete --all
 ----
+
+== Create integration pipes
+
+In some contexts (for example **"serverless"**) users often want to leverage 
the power of Apache Camel to be able to connect to various sources/sinks, with 
focus on connectivity to 3rd party technologies and services and less focus on 
doing complex processing (such as transformations or other enterprise 
integration patterns).
+
+Pipe resources represent a special form of Camel integrations where a source 
gets bound to a sink.
+The operation to create such a Pipe resource is often related to as the 
process of binding a source to a sink.
+
+You can use the Camel JBang subcommand `bind` to create Pipe resources.
+The result of this Pipe resource being created on a Kubernetes cluster is a 
running Camel integration.
+
+The Camel K bind command supports the following options:
+
+[width="100%",cols="1m,3",options="header",]
+|=======================================================================
+|Option |Description
+
+|--operator-id
+|Operator id selected to manage this integration. (default=camel-k)
+
+|--source
+|Source (from) such as a Kamelet or Camel endpoint uri that provides data..
+
+|--sink
+|Sink (to) such as a Kamelet or Camel endpoint uri where data should be sent 
to.
+
+|--step
+|Add optional 1-n steps to the pipe processing. Each step represents a 
reference to a Kamelet of type action.
+
+|--property
+|Add a pipe property in the form of 
[source,sink,error-handler,step-<n>].<key>=<value> where <n> is the step number 
starting from 1.
+
+|--error-handler
+|Add error handler (none,log,sink:<endpoint>). Sink endpoints are expected in 
the format [[apigroup/]version:]kind:[namespace/]name, plain Camel URIs or 
Kamelet name.
+
+|--annotation
+|Add an annotation to the integration. Use name values pairs like 
"--annotation my.company=hello".
+
+|--connect
+|A Service that the integration should bind to, specified as 
[[apigroup/]version:]kind:[namespace/]name.
+
+|--traits
+|Add a trait configuration to the integration. Use name values pairs like 
"--trait trait.name.config=hello".
+
+|--wait
+|Wait for the integration to become ready.
+
+|--logs
+|Print logs after integration has been started.
+
+|--output
+|Just output the generated pipe custom resource (supports: file, yaml or json).
+|=======================================================================
+
+Sources and sinks in a pipe may be Camel endpoint URIs, a Kamelet or a 
references to a Kubernetes resource (e.g. Knative brokers, Kafka topics).
+
+=== Binding Kamelets
+
+In a typical use case a Pipe connects Kamelets of type source and sink.
+Usually a Kamelet gets identified by its name (e.g. timer-source, log-sink).
+
+[source,bash]
+----
+camel k bind my-pipe --source timer-source --sink log-sink --property 
source.message="Camel rocks!" --property sink.showHeaders=true
+----
+
+The bind command receives the name of the pipe as a command argument and uses 
several options to specify the source and the sink.
+In addition to that the user is able to specify properties on the individual 
source and sink (e.g. the message property on the timer-source Kamelet).
+
+The result of this command is a Pipe custom resource that you can apply to a 
Kubernetes cluster.
+
+[source,yaml]
+----
+apiVersion: camel.apache.org/v1
+kind: Pipe
+metadata:
+  name: my-pipe
+  annotations:
+    camel.apache.org/operator.id: camel-k
+spec:
+  source: # <1>
+    ref:
+      kind: Kamelet
+      apiVersion: camel.apache.org/v1
+      name: timer-source
+    properties:
+      message: "Camel rocks!"
+  sink: # <2>
+    ref:
+      kind: Kamelet
+      apiVersion: camel.apache.org/v1
+      name: log-sink
+    properties:
+      showHeaders: true
+----
+<1> Reference to the source that provides data
+<2> Reference to the sink where data should be sent to
+
+Each Pipe resource uses an operator id annotation to specify which operator on 
the cluster should handle the resource.
+
+NOTE: The bind command is able to inspect the properties defined in the 
Kamelet specification in order to set default values. In case the Kamelet 
defines a required property that is not explicitly set by the user the bind 
command automatically creates a property placeholder with an example value.
+
+=== Add binding steps
+
+You can specify 1-n additional steps that get executed between the source and 
sink.
+
+[source,bash]
+----
+camel k bind my-pipe --source timer-source --sink log-sink --step 
set-body-action --property step-1.value="Camel rocks!"
+----
+
+[source,yaml]
+----
+apiVersion: camel.apache.org/v1
+kind: Pipe
+metadata:
+  name: my-pipe
+spec:
+  source:
+# ...
+  steps:
+  - ref:
+      kind: Kamelet
+      apiVersion: camel.apache.org/v1
+      name: set-body-action
+    properties:
+      value: "Camel rocks!"
+  sink:
+# ...
+----
+
+NOTE: Each step should reverence a Kamelet of type `action`.
+The properties for a step can be set with the respective `step-<n>` prefix 
where `n` is the step number beginning with 1.
+
+=== Binding Camel endpoint URIs
+
+Instead of referencing a Kamelet or Kubernetes resource you can also configure 
the source/sink to be an explicit Camel URI.
+For example, the following bind command is allowed:
+
+[source,bash]
+----
+camel k bind my-pipe --source timer:tick --sink 
https://mycompany.com/the-service --property source.period=5000
+----
+
+This will use the Camel endpoint URIs `timer:tick` and `log:info` as source 
and sink in the Pipe.
+The properties are set as endpoint parameters.
+
+[source,yaml]
+----
+apiVersion: camel.apache.org/v1
+kind: Pipe
+metadata:
+  name: my-pipe
+spec:
+  source:
+    uri: timer:tick # <1>
+    properties:
+      period: 5000
+  sink:
+    uri: https://mycompany.com/the-service # <2>
+----
+<1> Pipe with explicit Camel endpoint URI as source
+<2> Pipe with explicit Camel endpoint URI as sink where the data gets pushed to
+
+This Pipe explicitly defines Camel endpoint URIs that act as a source and sink.
+
+NOTE: You can also specify endpoint parameters directly on the source/sink 
like `--source timer:tick?period=5000`
+
+=== Error handling
+
+You can configure an error handler in order to specify what to do when some 
event ends up with failure.
+Pipes offer a mechanism to specify an error policy to adopt in case an event 
processing fails.
+
+In case of an exception thrown during the pipe processing the respective error 
handler will perform its actions.
+
+The Pipe knows different types of error handlers `none`, `log` and `sink`:
+
+* none -> Explicit `noErrorHandler` is set and the error is ignored.
+* log -> Errors get logged to the output.
+* sink -> Errors get pushed to a specified endpoint in the form of dead letter 
queue.
+
+The error handler may be configured with special properties that allow you to 
define the error handling behavior such as redelivery or delay policy.
+
+==== No error handler
+
+There may be certain cases where you want to just ignore any failure happening 
on your integration.
+In this situation just use a `none` error handler.
+
+[source,bash]
+----
+camel k bind my-pipe --source timer-source --sink log-sink --error-handler none
+----
+
+This results in following error handler configuration on the pipe:
+
+[source,yaml]
+----
+apiVersion: camel.apache.org/v1
+kind: Pipe
+metadata:
+  name: my-pipe
+spec:
+  source:
+# ...
+  sink:
+# ...
+  errorHandler:
+    none: {}
+----
+
+==== Log error handler
+
+Apache Camel offers a default behavior for handling failure: log to standard 
output.
+However, you can use the `log` error handler to specify other behaviors such 
as redelivery or delay policy.
+
+[source,bash]
+----
+camel k bind my-pipe --source timer-source --sink log-sink --error-handler log 
--property error-handler.maximumRedeliveries=3 --property 
error-handler.redeliveryDelay=2000
+----
+
+This results in the error handler configuration on the Pipe:
+
+[source,yaml]
+----
+apiVersion: camel.apache.org/v1
+kind: Pipe
+metadata:
+  name: my-pipe
+spec:
+  source:
+# ...
+  sink:
+# ...
+  errorHandler:
+    log:
+      parameters: # <1>
+        redeliveryDelay: 2000
+        maximumRedeliveries: 3
+----
+<1> Parameters belonging to the `log` error handler type
+
+==== Sink error handler
+
+The `sink` error handler is probably the most interesting error handler type 
as it allows you to redirect failing events to other components, such as a 
third party URI, a queue or topic or even another `Kamelet` which will be 
performing certain logic with the failing event.
+
+The sink error handler expects a proper endpoint URI which may be a reference 
to another Kamelet, a fully qualified custom resource reference or an arbitrary 
Camel endpoint URI.
+
+[source,bash]
+----
+camel k bind my-pipe --source timer-source --sink log-sink --error-handler 
sink:my-error-handler --property error-handler.sink.message=ERROR! --property 
error-handler.maximumRedeliveries=1
+----
+
+[source,yaml]
+----
+apiVersion: camel.apache.org/v1
+kind: Pipe
+metadata:
+  name: my-pipe
+spec:
+  source:
+# ...
+  sink:
+# ...
+  errorHandler:
+    sink:
+      endpoint:
+        ref: # <1>
+          kind: Kamelet
+          apiVersion: camel.apache.org/v1
+          name: my-error-handler
+        properties:
+          message: "ERROR!" # <2>
+          # ...
+      parameters:
+        maximumRedeliveries: 1 # <3>
+        # ...
+----
+<1> You can use `ref` or `uri`. `ref` will be interpreted by the operator 
according the `kind`, `apiVersion` and `name`. You can use any `Kamelet`, 
`KafkaTopic` channel or `Knative` destination.
+<2> Properties targeting the sink endpoint (in this example a property on the 
`Kamelet` named `my-error-handler`). Properties targeting the sink endpoint 
need to use the `error-handler.sink.*` prefix.
+<3> Parameters for the error handler (such as redelivery or delay policy). 
Error handler parameters need to use the `error-handler.*` prefix.
+
+NOTE: The error handler properties are divided into properties that target the 
error handler sink endpoint and properties that should be set on the Camel 
error handler component (e.g. maximumRedeliveries). You need to specify the 
respective property prefix (`error-handler.` or `error-handler.sink.`) to 
decide where the property should be set.
+
+As an alternative to referencing a Kamelet as an error handler sink you may 
also use an arbitrary Camel endpoint URI.
+
+[source,bash]
+----
+camel k bind my-pipe --source timer-source --sink log-sink --error-handler 
sink:log:error --property error-handler.sink.showHeaders=true
+----
+
+It creates the error handler specification as follows:
+
+[source,yaml]
+----
+apiVersion: camel.apache.org/v1
+kind: Pipe
+metadata:
+  name: my-pipe
+spec:
+  source:
+# ...
+  sink:
+# ...
+  errorHandler:
+    sink:
+      endpoint:
+        uri: log:error
+        properties:
+          showHeaders: true
+----
diff --git a/docs/user-manual/modules/ROOT/pages/camel-jbang.adoc 
b/docs/user-manual/modules/ROOT/pages/camel-jbang.adoc
index 4c61e011c98..cb9340b271f 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-jbang.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-jbang.adoc
@@ -1045,7 +1045,7 @@ camel plugin delete <plugin-name>
 
 === Running Camel K integrations or pipes
 
-Camel also supports running Camel K integrations and binding files, which are 
in CRD format (Kubernetes Custom Resource Definitions).
+Camel also supports running Camel K integrations and pipes, which represent 
Kubernetes custom resources following a specific CRD format (Kubernetes Custom 
Resource Definitions).
 
 For example a pipe file named `joke.yaml`:
 
@@ -1080,6 +1080,102 @@ Can be run with camel:
 camel run joke.yaml
 ----
 
+==== Binding Kamelets in a pipe
+
+Camel JBang is able to create the Pipe custom resource for you.
+You can use the `bind` command to specify a source and a sink that should be 
set in the pipe.
+As a result Camel JBang will create a proper Pipe custom resource for you.
+
+The command expects a file name as command argument and provides several 
options to define the source and the sink that should be used in the pipe.
+
+[source,bash]
+----
+camel bind joke.yaml --source chuck-norris-source --sink log-sink
+----
+
+This creates the `joke.yaml` file that represents the Pipe resource.
+
+[source,yaml]
+----
+apiVersion: camel.apache.org/v1
+kind: Pipe
+metadata:
+  name: joke
+spec:
+  source:
+    ref:
+      kind: Kamelet
+      apiVersion: camel.apache.org/v1
+      name: chuck-norris-source
+    properties:
+      period: 5000
+  sink:
+    ref:
+      kind: Kamelet
+      apiVersion: camel.apache.org/v1
+      name: log-sink
+----
+
+NOTE: The bind command is able to inspect the Kamelets being used as a source 
and sink in order to automatically set all required properties.
+In case the Kamelet defines a required property and the user has not specified 
such the command will automatically set this property with an example value.
+Once the pipe resource file is generated you can review and set the properties 
as you wish.
+
+The bind command supports the following options:
+
+[width="100%",cols="1m,3",options="header",]
+|=======================================================================
+|Option |Description
+
+|--source
+|Source (from) such as a Kamelet or Camel endpoint uri that provides data..
+
+|--sink
+|Sink (to) such as a Kamelet or Camel endpoint uri where data should be sent 
to.
+
+|--step
+|Add optional 1-n steps to the pipe processing. Each step represents a 
reference to a Kamelet of type action.
+
+|--property
+|Add a pipe property in the form of 
[source,sink,error-handler,step-<n>].<key>=<value> where <n> is the step number 
starting from 1.
+
+|--error-handler
+|Add error handler (none,log,sink:<endpoint>). Sink endpoints are expected in 
the format [[apigroup/]version:]kind:[namespace/]name, plain Camel URIs or 
Kamelet name.
+
+|--output
+|Output format generated by this command (supports: file, yaml or json). 
Default is "file".
+|=======================================================================
+
+==== Binding explicit Camel URIs
+
+Usually, the source and sink reference a Kamelet by its name as shown in the 
previous section.
+As an alternative, you can also just use an arbitrary Camel endpoint URI that 
acts as a source or sink in the pipe.
+
+[source,bash]
+----
+camel bind joke.yaml --source chuck-norris-source --sink 
https://mycompany.com/the-service
+----
+
+As a result the Pipe resource uses the Camel endpoints as source and sink.
+
+[source,yaml]
+----
+apiVersion: camel.apache.org/v1
+kind: Pipe
+metadata:
+  name: my-pipe
+spec:
+  source:
+# ...
+  sink:
+    uri: https://mycompany.com/the-service # <1>
+----
+<1> Pipe with explicit Camel endpoint URI as sink where the data gets pushed to
+
+==== Advanced binding options
+
+The Camel K JBang plugin also provides the bind command. It enhances the 
arbitrary bind command with the option to directly create this resource on the
+Kubernetes cluster. Please refer to the plugin documentation for more complex 
examples and a full description on how to use the bind command options.
+
 === Run from clipboard
 
 You can also run Camel routes directly from the OS clipboard. This allows to 
copy some code,
@@ -1205,7 +1301,6 @@ TIP: See more options with `camel cmd send --help`.
 
 The source for this example is provided on GitHub at 
https://github.com/apache/camel-kamelets-examples/tree/main/jbang/mqtt)[camel-jbang
 MQTT example].
 
-
 === Controlling local Camel integrations
 
 To list the currently running Camel integrations you use the `ps` command:
@@ -1213,9 +1308,9 @@ To list the currently running Camel integrations you use 
the `ps` command:
 [source,bash]
 ----
 camel ps
-  PID   NAME                          READY  STATUS    AGE
- 61818  sample.camel.MyCamelApplica…   1/1   Running  26m38s
- 62506  dude                           1/1   Running   4m34s
+  PID   NAME                             READY  STATUS    AGE
+ 61818  sample.camel.MyCamelApplica...   1/1   Running  26m38s
+ 62506  dude                             1/1   Running   4m34s
 ----
 
 This lists the PID, the name and age of the integration.
@@ -1258,9 +1353,9 @@ This is done using the `--watch` parameter as follows:
 [source,bash]
 ----
 camel ps --watch
-  PID   NAME                          READY  STATUS    AGE
- 61818  sample.camel.MyCamelApplica…   1/1   Running  26m38s
- 62506  dude                           1/1   Running   4m34s
+  PID   NAME                             READY  STATUS    AGE
+ 61818  sample.camel.MyCamelApplica...   1/1   Running  26m38s
+ 62506  dude                             1/1   Running   4m34s
 ----
 
 ==== Controlling Spring Boot and Quarkus integrations
@@ -1637,9 +1732,9 @@ must be installed in the running integration, this can be 
done, either explicit
 [source,bash]
 ----
 camel ps
-  PID   NAME                          READY  STATUS    AGE
- 61818  sample.camel.MyCamelApplica…   1/1   Running  26m38s
- 62506  dude.java                      1/1   Running   4m34s
+  PID   NAME                             READY  STATUS    AGE
+ 61818  sample.camel.MyCamelApplica...   1/1   Running  26m38s
+ 62506  dude.java                        1/1   Running   4m34s
 ----
 
 With the PID you can then attach Jolokia:
@@ -2256,9 +2351,9 @@ A health check may often be failed due to an exception 
was thrown which can be s
 [source,bash]
 ----
 camel get health --trace
-  PID   NAME      AGE   ID                                      RL  STATE    
RATE       SINCE     MESSAGE
- 61038  mykafka  6m19s  camel/context                            R   UP    
187/187/-  1s/6m16s/-
- 61038  mykafka  6m19s  camel/kafka-consumer-kafka-not-secure…   R  DOWN   
187/-/187  1s/-/6m16s  KafkaConsumer is not ready - Error: Invalid url in 
bootstrap.servers: value
+  PID   NAME      AGE   ID                                        RL  STATE    
RATE       SINCE     MESSAGE
+ 61038  mykafka  6m19s  camel/context                             R   UP    
187/187/-  1s/6m16s/-
+ 61038  mykafka  6m19s  camel/kafka-consumer-kafka-not-secure...  R  DOWN   
187/-/187  1s/-/6m16s  KafkaConsumer is not ready - Error: Invalid url in 
bootstrap.servers: value
 
 
 
------------------------------------------------------------------------------------------------------------------------
@@ -2591,7 +2686,6 @@ This can be suppressed by setting the option 
`suppressExceptions` to `true` as s
  [Body is null]
 ----
 
-
 === Transforming message using Components
 
 Some components can also be used for message transformation such as FlatPack, 
Velocity, FreeMarker, Thymeleaf, and good old XSLT.
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Bind.java
 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Bind.java
index 3a005645342..61ab92c85ac 100644
--- 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Bind.java
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Bind.java
@@ -75,7 +75,7 @@ public class Bind extends CamelCommand {
     String errorHandler;
 
     @CommandLine.Option(names = { "--property" },
-                        description = "Adds a pipe property in the form of 
[source|sink|step-<n>].<key>=<value> where <n> is the step number starting from 
1",
+                        description = "Adds a pipe property in the form of 
[source|sink|error-handler|step-<n>].<key>=<value> where <n> is the step number 
starting from 1",
                         arity = "0")
     String[] properties;
 
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/BindTest.java
 
b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/BindTest.java
index 02cdc70e6e6..bdde0d78c43 100644
--- 
a/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/BindTest.java
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/BindTest.java
@@ -17,6 +17,7 @@
 
 package org.apache.camel.dsl.jbang.core.commands;
 
+import org.apache.camel.util.StringHelper;
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.Test;
 
@@ -24,12 +25,7 @@ class BindTest extends CamelCommandBaseTest {
 
     @Test
     public void shouldBindKameletSourceToKameletSink() throws Exception {
-        Bind command = new Bind(new CamelJBangMain().withPrinter(printer));
-        command.file = "timer-to-log";
-        command.source = "timer-source";
-        command.sink = "log-sink";
-        command.output = "yaml";
-
+        Bind command = createCommand("timer", "log");
         command.doCall();
 
         String output = printer.getOutput();
@@ -58,11 +54,7 @@ class BindTest extends CamelCommandBaseTest {
 
     @Test
     public void shouldBindKameletSourceToKameletSinkWithProperties() throws 
Exception {
-        Bind command = new Bind(new CamelJBangMain().withPrinter(printer));
-        command.file = "timer-to-log";
-        command.source = "timer-source";
-        command.sink = "log-sink";
-        command.output = "yaml";
+        Bind command = createCommand("timer", "log");
 
         command.properties = new String[] {
                 "source.message=Hello",
@@ -99,11 +91,7 @@ class BindTest extends CamelCommandBaseTest {
 
     @Test
     public void shouldBindWithSteps() throws Exception {
-        Bind command = new Bind(new CamelJBangMain().withPrinter(printer));
-        command.file = "timer-to-http";
-        command.source = "timer-source";
-        command.sink = "http-sink";
-        command.output = "yaml";
+        Bind command = createCommand("timer", "http");
 
         command.steps = new String[] {
                 "set-body-action",
@@ -151,11 +139,7 @@ class BindTest extends CamelCommandBaseTest {
 
     @Test
     public void shouldBindWithUriSteps() throws Exception {
-        Bind command = new Bind(new CamelJBangMain().withPrinter(printer));
-        command.file = "timer-to-http";
-        command.source = "timer-source";
-        command.sink = "http-sink";
-        command.output = "yaml";
+        Bind command = createCommand("timer", "http");
 
         command.steps = new String[] {
                 "set-body-action",
@@ -200,11 +184,7 @@ class BindTest extends CamelCommandBaseTest {
 
     @Test
     public void shouldBindWithStepsAndProperties() throws Exception {
-        Bind command = new Bind(new CamelJBangMain().withPrinter(printer));
-        command.file = "timer-to-http";
-        command.source = "timer-source";
-        command.sink = "http-sink";
-        command.output = "yaml";
+        Bind command = createCommand("timer", "http");
 
         command.steps = new String[] {
                 "set-body-action",
@@ -259,11 +239,7 @@ class BindTest extends CamelCommandBaseTest {
 
     @Test
     public void shouldBindWithUriStepsAndProperties() throws Exception {
-        Bind command = new Bind(new CamelJBangMain().withPrinter(printer));
-        command.file = "timer-to-http";
-        command.source = "timer-source";
-        command.sink = "http-sink";
-        command.output = "yaml";
+        Bind command = createCommand("timer", "http");
 
         command.steps = new String[] {
                 "set-body-action",
@@ -313,11 +289,7 @@ class BindTest extends CamelCommandBaseTest {
 
     @Test
     public void shouldBindWithUriStepsAndUriProperties() throws Exception {
-        Bind command = new Bind(new CamelJBangMain().withPrinter(printer));
-        command.file = "timer-to-http";
-        command.source = "timer-source";
-        command.sink = "http-sink";
-        command.output = "yaml";
+        Bind command = createCommand("timer", "http");
 
         command.steps = new String[] {
                 "set-body-action",
@@ -369,11 +341,7 @@ class BindTest extends CamelCommandBaseTest {
 
     @Test
     public void shouldBindKameletSourceToUri() throws Exception {
-        Bind command = new Bind(new CamelJBangMain().withPrinter(printer));
-        command.file = "timer-to-log";
-        command.source = "timer-source";
-        command.sink = "log:info";
-        command.output = "yaml";
+        Bind command = createCommand("timer", "log:info");
 
         command.doCall();
 
@@ -400,11 +368,7 @@ class BindTest extends CamelCommandBaseTest {
 
     @Test
     public void shouldBindKameletSourceToUriWithProperties() throws Exception {
-        Bind command = new Bind(new CamelJBangMain().withPrinter(printer));
-        command.file = "timer-to-log";
-        command.source = "timer-source";
-        command.sink = "log:info";
-        command.output = "yaml";
+        Bind command = createCommand("timer", "log:info");
 
         command.properties = new String[] {
                 "source.message=Hello",
@@ -436,11 +400,7 @@ class BindTest extends CamelCommandBaseTest {
 
     @Test
     public void shouldBindKameletSourceToUriWithUriProperties() throws 
Exception {
-        Bind command = new Bind(new CamelJBangMain().withPrinter(printer));
-        command.file = "timer-to-log";
-        command.source = "timer-source";
-        command.sink = "log:info?showStreams=false";
-        command.output = "yaml";
+        Bind command = createCommand("timer", "log:info?showStreams=false");
 
         command.properties = new String[] {
                 "source.message=Hello",
@@ -473,11 +433,7 @@ class BindTest extends CamelCommandBaseTest {
 
     @Test
     public void shouldBindUriToUri() throws Exception {
-        Bind command = new Bind(new CamelJBangMain().withPrinter(printer));
-        command.file = "timer-to-log";
-        command.source = "timer:tick";
-        command.sink = "log:info";
-        command.output = "yaml";
+        Bind command = createCommand("timer:tick", "log:info");
 
         command.doCall();
 
@@ -501,11 +457,7 @@ class BindTest extends CamelCommandBaseTest {
 
     @Test
     public void shouldBindUriToUriWithProperties() throws Exception {
-        Bind command = new Bind(new CamelJBangMain().withPrinter(printer));
-        command.file = "timer-to-log";
-        command.source = "timer:tick";
-        command.sink = "log:info";
-        command.output = "yaml";
+        Bind command = createCommand("timer:tick", "log:info");
 
         command.properties = new String[] {
                 "source.message=Hello",
@@ -534,11 +486,7 @@ class BindTest extends CamelCommandBaseTest {
 
     @Test
     public void shouldBindUriToUriWithUriProperties() throws Exception {
-        Bind command = new Bind(new CamelJBangMain().withPrinter(printer));
-        command.file = "timer-to-log";
-        command.source = "timer:tick?period=10000";
-        command.sink = "log:info?showStreams=false";
-        command.output = "yaml";
+        Bind command = createCommand("timer:tick?period=10000", 
"log:info?showStreams=false");
 
         command.properties = new String[] {
                 "source.message=Hello",
@@ -569,11 +517,7 @@ class BindTest extends CamelCommandBaseTest {
 
     @Test
     public void shouldBindKameletSinkErrorHandler() throws Exception {
-        Bind command = new Bind(new CamelJBangMain().withPrinter(printer));
-        command.file = "timer-to-log";
-        command.source = "timer-source";
-        command.sink = "log-sink";
-        command.output = "yaml";
+        Bind command = createCommand("timer", "log");
 
         command.errorHandler = "sink:log-sink";
 
@@ -615,11 +559,7 @@ class BindTest extends CamelCommandBaseTest {
 
     @Test
     public void shouldBindKameletSinkErrorHandlerWithParameters() throws 
Exception {
-        Bind command = new Bind(new CamelJBangMain().withPrinter(printer));
-        command.file = "timer-to-log";
-        command.source = "timer-source";
-        command.sink = "log-sink";
-        command.output = "yaml";
+        Bind command = createCommand("timer", "log");
 
         command.errorHandler = "sink:log-sink";
 
@@ -668,11 +608,7 @@ class BindTest extends CamelCommandBaseTest {
 
     @Test
     public void shouldBindKameletSinkErrorHandlerAndSinkProperties() throws 
Exception {
-        Bind command = new Bind(new CamelJBangMain().withPrinter(printer));
-        command.file = "timer-to-log";
-        command.source = "timer-source";
-        command.sink = "log-sink";
-        command.output = "yaml";
+        Bind command = createCommand("timer", "log");
 
         command.errorHandler = "sink:log-sink";
 
@@ -722,11 +658,7 @@ class BindTest extends CamelCommandBaseTest {
 
     @Test
     public void shouldBindEndpointUriSinkErrorHandler() throws Exception {
-        Bind command = new Bind(new CamelJBangMain().withPrinter(printer));
-        command.file = "timer-to-log";
-        command.source = "timer-source";
-        command.sink = "log-sink";
-        command.output = "yaml";
+        Bind command = createCommand("timer", "log");
 
         command.errorHandler = "sink:log:error";
 
@@ -765,11 +697,7 @@ class BindTest extends CamelCommandBaseTest {
 
     @Test
     public void shouldBindEndpointUriSinkErrorHandlerWithParameters() throws 
Exception {
-        Bind command = new Bind(new CamelJBangMain().withPrinter(printer));
-        command.file = "timer-to-log";
-        command.source = "timer-source";
-        command.sink = "log-sink";
-        command.output = "yaml";
+        Bind command = createCommand("timer", "log");
 
         command.errorHandler = "sink:log:error";
 
@@ -815,11 +743,7 @@ class BindTest extends CamelCommandBaseTest {
 
     @Test
     public void shouldBindEndpointUriSinkErrorHandlerAndSinkProperties() 
throws Exception {
-        Bind command = new Bind(new CamelJBangMain().withPrinter(printer));
-        command.file = "timer-to-log";
-        command.source = "timer-source";
-        command.sink = "log-sink";
-        command.output = "yaml";
+        Bind command = createCommand("timer", "log");
 
         command.errorHandler = "sink:log:error";
 
@@ -866,11 +790,7 @@ class BindTest extends CamelCommandBaseTest {
 
     @Test
     public void shouldBindEndpointUriSinkErrorHandlerAndUriProperties() throws 
Exception {
-        Bind command = new Bind(new CamelJBangMain().withPrinter(printer));
-        command.file = "timer-to-log";
-        command.source = "timer-source";
-        command.sink = "log-sink";
-        command.output = "yaml";
+        Bind command = createCommand("timer", "log");
 
         command.errorHandler = "sink:log:error?showStreams=false";
 
@@ -918,11 +838,7 @@ class BindTest extends CamelCommandBaseTest {
 
     @Test
     public void shouldBindWithLogErrorHandler() throws Exception {
-        Bind command = new Bind(new CamelJBangMain().withPrinter(printer));
-        command.file = "timer-to-log";
-        command.source = "timer-source";
-        command.sink = "log-sink";
-        command.output = "yaml";
+        Bind command = createCommand("timer", "log");
 
         command.errorHandler = "log";
 
@@ -957,11 +873,7 @@ class BindTest extends CamelCommandBaseTest {
 
     @Test
     public void shouldBindWithLogErrorHandlerWithParameters() throws Exception 
{
-        Bind command = new Bind(new CamelJBangMain().withPrinter(printer));
-        command.file = "timer-to-log";
-        command.source = "timer-source";
-        command.sink = "log-sink";
-        command.output = "yaml";
+        Bind command = createCommand("timer", "log");
 
         command.errorHandler = "log";
 
@@ -1003,11 +915,7 @@ class BindTest extends CamelCommandBaseTest {
 
     @Test
     public void shouldBindWithNoErrorHandler() throws Exception {
-        Bind command = new Bind(new CamelJBangMain().withPrinter(printer));
-        command.file = "timer-to-log";
-        command.source = "timer-source";
-        command.sink = "log-sink";
-        command.output = "yaml";
+        Bind command = createCommand("timer", "log");
 
         command.errorHandler = "none";
 
@@ -1041,10 +949,7 @@ class BindTest extends CamelCommandBaseTest {
 
     @Test
     public void shouldSupportJsonOutput() throws Exception {
-        Bind command = new Bind(new CamelJBangMain().withPrinter(printer));
-        command.file = "timer-to-log";
-        command.source = "timer-source";
-        command.sink = "log-sink";
+        Bind command = createCommand("timer", "log");
         command.output = "json";
 
         command.doCall();
@@ -1082,14 +987,42 @@ class BindTest extends CamelCommandBaseTest {
 
     @Test
     public void shouldHandleUnsupportedOutputFormat() throws Exception {
-        Bind command = new Bind(new CamelJBangMain().withPrinter(printer));
-        command.file = "timer-to-log";
-        command.source = "timer-source";
-        command.sink = "log-sink";
+        Bind command = createCommand("timer", "log");
         command.output = "wrong";
 
         Assertions.assertEquals(-1, command.doCall());
 
         Assertions.assertEquals("Unsupported output format 'wrong' (supported: 
file, yaml, json)", printer.getOutput());
     }
+
+    private Bind createCommand(String source, String sink) {
+        Bind command = new Bind(new CamelJBangMain().withPrinter(printer));
+
+        String sourceName;
+        String sourceUri;
+        if (source.contains(":")) {
+            sourceName = StringHelper.before(source, ":");
+            sourceUri = source;
+        } else {
+            sourceName = source;
+            sourceUri = source + "-source";
+        }
+
+        String sinkName;
+        String sinkUri;
+        if (sink.contains(":")) {
+            sinkName = StringHelper.before(sink, ":");
+            sinkUri = sink;
+        } else {
+            sinkName = sink;
+            sinkUri = sink + "-sink";
+        }
+
+        command.file = sourceName + "-to-" + sinkName + ".yaml";
+        command.source = sourceUri;
+        command.sink = sinkUri;
+        command.output = "yaml";
+
+        return command;
+    }
 }
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-k/src/main/java/org/apache/camel/dsl/jbang/core/commands/k/Bind.java
 
b/dsl/camel-jbang/camel-jbang-plugin-k/src/main/java/org/apache/camel/dsl/jbang/core/commands/k/Bind.java
index c536045c68f..c5531d1add4 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-k/src/main/java/org/apache/camel/dsl/jbang/core/commands/k/Bind.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-k/src/main/java/org/apache/camel/dsl/jbang/core/commands/k/Bind.java
@@ -21,9 +21,12 @@ import java.nio.file.Path;
 import java.util.Arrays;
 import java.util.List;
 import java.util.Stack;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
 
 import org.apache.camel.dsl.jbang.core.commands.CamelJBangMain;
 import org.apache.camel.util.ObjectHelper;
+import org.apache.camel.v1.Pipe;
 import org.apache.camel.v1.integrationspec.Traits;
 import org.apache.camel.v1.integrationspec.traits.ServiceBinding;
 import picocli.CommandLine;
@@ -36,19 +39,22 @@ public class Bind extends KubeBaseCommand {
 
     private final org.apache.camel.dsl.jbang.core.commands.Bind delegate;
 
-    @CommandLine.Parameters(description = "Name of pipe", arity = "1",
+    @CommandLine.Parameters(description = "The name of the Pipe resource 
created on the cluster.", arity = "1",
                             paramLabel = "<name>", parameterConsumer = 
NameConsumer.class)
     Path pipeName; // Defined only for code completion; the field never used
     String name;
 
-    @CommandLine.Option(names = { "--source" }, description = "Source (from) 
such as a Kamelet or Camel endpoint uri",
+    @CommandLine.Option(names = { "--source" },
+                        description = "Source (from) such as a Kamelet or 
Camel endpoint uri that provides data.",
                         required = true)
     String source;
 
-    @CommandLine.Option(names = { "--step" }, description = "Optional steps 
such as a Kamelet or Camel endpoint uri")
+    @CommandLine.Option(names = { "--step" },
+                        description = "Add optional 1-n steps to the pipe 
processing. Each step represents a reference to a Kamelet of type action.")
     String[] steps;
 
-    @CommandLine.Option(names = { "--sink" }, description = "Sink (to) such as 
a Kamelet or Camel endpoint uri",
+    @CommandLine.Option(names = { "--sink" },
+                        description = "Sink (to) such as a Kamelet or Camel 
endpoint uri where data should be sent to.",
                         required = true)
     String sink;
 
@@ -57,12 +63,11 @@ public class Bind extends KubeBaseCommand {
     String errorHandler;
 
     @CommandLine.Option(names = { "--property" },
-                        description = "Adds a pipe property in the form of 
[source|sink|step-<n>].<key>=<value> where <n> is the step number starting from 
1",
+                        description = "Add a pipe property in the form of 
[source|sink|error-handler|step-<n>].<key>=<value> where <n> is the step number 
starting from 1.",
                         arity = "0")
     String[] properties;
 
     @CommandLine.Option(names = { "--output" },
-                        defaultValue = "file",
                         description = "Output format generated by this command 
(supports: file, yaml or json).")
     String output;
 
@@ -83,6 +88,12 @@ public class Bind extends KubeBaseCommand {
                         description = "Add a label to the integration. Use 
name values pairs like \"--label my.company=hello\".")
     String[] traits;
 
+    @CommandLine.Option(names = { "--wait" }, description = "Wait for the pipe 
to become ready.")
+    boolean wait;
+
+    @CommandLine.Option(names = { "--logs" }, description = "Print logs after 
pipe has been started.")
+    boolean logs;
+
     public Bind(CamelJBangMain main) {
         super(main);
         delegate = new org.apache.camel.dsl.jbang.core.commands.Bind(
@@ -109,7 +120,6 @@ public class Bind extends KubeBaseCommand {
         delegate.setSteps(steps);
         delegate.setErrorHandler(errorHandler);
         delegate.setProperties(properties);
-        delegate.setOutput(output);
 
         String pipe = delegate.constructPipe();
 
@@ -171,7 +181,34 @@ public class Bind extends KubeBaseCommand {
 
         pipe = pipe.replaceFirst("\\{\\{ \\.IntegrationSpec }}\n", 
integrationSpec);
 
-        return delegate.dumpPipe(pipe);
+        if (output != null) {
+            delegate.setOutput(output);
+            return delegate.dumpPipe(pipe);
+        }
+
+        Pipe pipeResource = KubernetesHelper.yaml().loadAs(pipe, Pipe.class);
+        final AtomicBoolean updated = new AtomicBoolean(false);
+        client(Pipe.class).resource(pipeResource).createOr(it -> {
+            updated.set(true);
+            return it.update();
+        });
+
+        if (updated.get()) {
+            printer().printf("Pipe %s updated%n", 
pipeResource.getMetadata().getName());
+        } else {
+            printer().printf("Pipe %s created%n", 
pipeResource.getMetadata().getName());
+        }
+
+        if (wait || logs) {
+            client(Pipe.class).withName(pipeResource.getMetadata().getName())
+                    .waitUntilCondition(it -> 
"Running".equals(it.getStatus().getPhase()), 10, TimeUnit.MINUTES);
+        }
+
+        if (logs) {
+            new 
IntegrationLogs(getMain()).watchLogs(pipeResource.getMetadata().getName());
+        }
+
+        return 0;
     }
 
     static class NameConsumer extends ParameterConsumer<Bind> {
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-k/src/main/java/org/apache/camel/dsl/jbang/core/commands/k/IntegrationLogs.java
 
b/dsl/camel-jbang/camel-jbang-plugin-k/src/main/java/org/apache/camel/dsl/jbang/core/commands/k/IntegrationLogs.java
index faf96f0c27c..a9d5919083f 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-k/src/main/java/org/apache/camel/dsl/jbang/core/commands/k/IntegrationLogs.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-k/src/main/java/org/apache/camel/dsl/jbang/core/commands/k/IntegrationLogs.java
@@ -55,13 +55,13 @@ public class IntegrationLogs extends KubeBaseCommand {
             return 0;
         }
 
-        watchLogs(integration);
+        watchLogs(integration.getMetadata().getName());
 
         return 0;
     }
 
-    void watchLogs(Integration integration) {
-        PodList pods = pods().withLabel(KubeCommand.INTEGRATION_LABEL, 
integration.getMetadata().getName()).list();
+    void watchLogs(String integrationName) {
+        PodList pods = pods().withLabel(KubeCommand.INTEGRATION_LABEL, 
integrationName).list();
 
         Pod pod = pods.getItems().stream()
                 .filter(p -> p.getStatus().getPhase() != null && 
!"Terminated".equals(p.getStatus().getPhase()))
@@ -73,7 +73,7 @@ public class IntegrationLogs extends KubeBaseCommand {
             if (pod.getSpec().getContainers().stream()
                     .anyMatch(container -> 
KubeCommand.INTEGRATION_CONTAINER_NAME.equals(container.getName()))) {
                 containerName = KubeCommand.INTEGRATION_CONTAINER_NAME;
-            } else if (pod.getSpec().getContainers().size() > 0) {
+            } else if (!pod.getSpec().getContainers().isEmpty()) {
                 containerName = pod.getSpec().getContainers().get(0).getName();
             }
         }
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-k/src/main/java/org/apache/camel/dsl/jbang/core/commands/k/IntegrationRun.java
 
b/dsl/camel-jbang/camel-jbang-plugin-k/src/main/java/org/apache/camel/dsl/jbang/core/commands/k/IntegrationRun.java
index a435ad67b14..4acc18ed8c2 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-k/src/main/java/org/apache/camel/dsl/jbang/core/commands/k/IntegrationRun.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-k/src/main/java/org/apache/camel/dsl/jbang/core/commands/k/IntegrationRun.java
@@ -147,7 +147,7 @@ public class IntegrationRun extends KubeBaseCommand {
     String[] labels;
 
     @CommandLine.Option(names = { "--traits", "-t" },
-                        description = "Add a label to the integration. Use 
name values pairs like \"--label my.company=hello\".")
+                        description = "Add a trait configuration to the 
integration. Use name values pairs like \"--trait trait.name.config=hello\".")
     String[] traits;
 
     @CommandLine.Option(names = { "--use-flows" }, defaultValue = "true",
@@ -313,7 +313,10 @@ public class IntegrationRun extends KubeBaseCommand {
                 case "yaml" -> 
printer().println(KubernetesHelper.yaml().dumpAsMap(integration));
                 case "json" -> printer().println(
                         
JSonHelper.prettyPrint(KubernetesHelper.json().writer().writeValueAsString(integration),
 2));
-                default -> printer().printf("Unsupported output format %s%n", 
output);
+                default -> {
+                    printer().printf("Unsupported output format '%s' 
(supported: yaml, json)%n", output);
+                    return -1;
+                }
             }
 
             return 0;
@@ -337,7 +340,7 @@ public class IntegrationRun extends KubeBaseCommand {
         }
 
         if (logs) {
-            new IntegrationLogs(getMain()).watchLogs(integration);
+            new 
IntegrationLogs(getMain()).watchLogs(integration.getMetadata().getName());
         }
 
         return 0;
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-k/src/test/java/org/apache/camel/dsl/jbang/core/commands/k/BindTest.java
 
b/dsl/camel-jbang/camel-jbang-plugin-k/src/test/java/org/apache/camel/dsl/jbang/core/commands/k/BindTest.java
index 9809bf44ee7..eafc0c209de 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-k/src/test/java/org/apache/camel/dsl/jbang/core/commands/k/BindTest.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-k/src/test/java/org/apache/camel/dsl/jbang/core/commands/k/BindTest.java
@@ -18,18 +18,47 @@
 package org.apache.camel.dsl.jbang.core.commands.k;
 
 import org.apache.camel.dsl.jbang.core.commands.CamelJBangMain;
+import org.apache.camel.util.StringHelper;
+import org.apache.camel.v1.Pipe;
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.Test;
 
 class BindTest extends KubeBaseTest {
 
+    @Test
+    public void shouldCreatePipe() throws Exception {
+        Bind command = createCommand("timer", "http");
+        command.output = null;
+        command.doCall();
+
+        Assertions.assertEquals("Pipe timer-to-http created", 
printer.getOutput());
+
+        Pipe created = 
kubernetesClient.resources(Pipe.class).withName("timer-to-http").get();
+        Assertions.assertEquals("camel-k", 
created.getMetadata().getAnnotations().get(KubeCommand.OPERATOR_ID_LABEL));
+    }
+
+    @Test
+    public void shouldUpdatePipe() throws Exception {
+        Pipe pipe = createPipe("timer-to-log");
+        kubernetesClient.resources(Pipe.class).resource(pipe).create();
+
+        Bind command = createCommand("timer", "log");
+        command.output = null;
+
+        command.properties = new String[] {
+                "sink.showHeaders=true"
+        };
+        command.doCall();
+
+        Assertions.assertEquals("Pipe timer-to-log updated", 
printer.getOutput());
+
+        Pipe created = 
kubernetesClient.resources(Pipe.class).withName("timer-to-log").get();
+        Assertions.assertEquals("camel-k", 
created.getMetadata().getAnnotations().get(KubeCommand.OPERATOR_ID_LABEL));
+    }
+
     @Test
     public void shouldBindWithDefaultOperatorId() throws Exception {
-        Bind command = new Bind(new CamelJBangMain().withPrinter(printer));
-        command.name = "timer-to-log";
-        command.source = "timer-source";
-        command.sink = "log-sink";
-        command.output = "yaml";
+        Bind command = createCommand("timer", "log");
 
         command.doCall();
 
@@ -61,11 +90,7 @@ class BindTest extends KubeBaseTest {
 
     @Test
     public void shouldBindWithAnnotations() throws Exception {
-        Bind command = new Bind(new CamelJBangMain().withPrinter(printer));
-        command.name = "timer-to-log";
-        command.source = "timer-source";
-        command.sink = "log-sink";
-        command.output = "yaml";
+        Bind command = createCommand("timer", "log");
 
         command.annotations = new String[] {
                 "app=camel-k"
@@ -102,11 +127,7 @@ class BindTest extends KubeBaseTest {
 
     @Test
     public void shouldBindWithTraits() throws Exception {
-        Bind command = new Bind(new CamelJBangMain().withPrinter(printer));
-        command.name = "timer-to-log";
-        command.source = "timer-source";
-        command.sink = "log-sink";
-        command.output = "yaml";
+        Bind command = createCommand("timer", "log");
 
         command.traits = new String[] {
                 "mount.configs=configmap:my-cm",
@@ -154,11 +175,7 @@ class BindTest extends KubeBaseTest {
 
     @Test
     public void shouldBindWithServiceBindings() throws Exception {
-        Bind command = new Bind(new CamelJBangMain().withPrinter(printer));
-        command.name = "timer-to-log";
-        command.source = "timer-source";
-        command.sink = "log-sink";
-        command.output = "yaml";
+        Bind command = createCommand("timer", "http");
 
         command.connects = new String[] {
                 "serving.knative.dev/v1:Service:my-service"
@@ -171,7 +188,7 @@ class BindTest extends KubeBaseTest {
                 apiVersion: camel.apache.org/v1
                 kind: Pipe
                 metadata:
-                  name: timer-to-log
+                  name: timer-to-http
                   annotations:
                     camel.apache.org/operator.id: camel-k
                 spec:
@@ -192,19 +209,15 @@ class BindTest extends KubeBaseTest {
                     ref:
                       kind: Kamelet
                       apiVersion: camel.apache.org/v1
-                      name: log-sink
-                    #properties:
-                      #key: "value"
+                      name: http-sink
+                    properties:
+                      url: "https://my-service/path";
                 """.trim(), output);
     }
 
     @Test
     public void shouldFailWithMissingOperatorId() throws Exception {
-        Bind command = new Bind(new CamelJBangMain().withPrinter(printer));
-        command.name = "timer-to-log";
-        command.source = "timer-source";
-        command.sink = "log-sink";
-        command.output = "yaml";
+        Bind command = createCommand("timer:tick", "log");
 
         command.operatorId = "";
 
@@ -212,4 +225,36 @@ class BindTest extends KubeBaseTest {
 
         Assertions.assertEquals("Operator id must be set", 
printer.getOutput());
     }
+
+    private Bind createCommand(String source, String sink) {
+        Bind command = new Bind(new CamelJBangMain().withPrinter(printer));
+        command.withClient(kubernetesClient);
+
+        String sourceName;
+        String sourceUri;
+        if (source.contains(":")) {
+            sourceName = StringHelper.before(source, ":");
+            sourceUri = source;
+        } else {
+            sourceName = source;
+            sourceUri = source + "-source";
+        }
+
+        String sinkName;
+        String sinkUri;
+        if (sink.contains(":")) {
+            sinkName = StringHelper.before(sink, ":");
+            sinkUri = sink;
+        } else {
+            sinkName = sink;
+            sinkUri = sink + "-sink";
+        }
+
+        command.name = sourceName + "-to-" + sinkName + ".yaml";
+        command.source = sourceUri;
+        command.sink = sinkUri;
+        command.output = "yaml";
+
+        return command;
+    }
 }
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-k/src/test/java/org/apache/camel/dsl/jbang/core/commands/k/IntegrationRunTest.java
 
b/dsl/camel-jbang/camel-jbang-plugin-k/src/test/java/org/apache/camel/dsl/jbang/core/commands/k/IntegrationRunTest.java
index 0a6a0e3df7c..318bae74621 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-k/src/test/java/org/apache/camel/dsl/jbang/core/commands/k/IntegrationRunTest.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-k/src/test/java/org/apache/camel/dsl/jbang/core/commands/k/IntegrationRunTest.java
@@ -656,6 +656,18 @@ class IntegrationRunTest extends KubeBaseTest {
         Assertions.assertEquals("Operator id must be set", 
printer.getOutput());
     }
 
+    @Test
+    public void shouldHandleUnsupportedOutputFormat() throws Exception {
+        IntegrationRun command = createCommand();
+        command.filePaths = new String[] { "classpath:route.yaml" };
+        command.useFlows = false;
+        command.output = "wrong";
+
+        Assertions.assertEquals(-1, command.doCall());
+
+        Assertions.assertEquals("Unsupported output format 'wrong' (supported: 
yaml, json)", printer.getOutput());
+    }
+
     private IntegrationRun createCommand() {
         IntegrationRun command = new IntegrationRun(new 
CamelJBangMain().withPrinter(printer));
         command.withClient(kubernetesClient);
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-k/src/test/java/org/apache/camel/dsl/jbang/core/commands/k/KubeBaseTest.java
 
b/dsl/camel-jbang/camel-jbang-plugin-k/src/test/java/org/apache/camel/dsl/jbang/core/commands/k/KubeBaseTest.java
index ab3dde9278f..c4df9d8ec17 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-k/src/test/java/org/apache/camel/dsl/jbang/core/commands/k/KubeBaseTest.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-k/src/test/java/org/apache/camel/dsl/jbang/core/commands/k/KubeBaseTest.java
@@ -32,6 +32,8 @@ import org.apache.camel.dsl.jbang.core.common.PluginType;
 import org.apache.camel.util.IOHelper;
 import org.apache.camel.v1.Integration;
 import org.apache.camel.v1.IntegrationSpec;
+import org.apache.camel.v1.Pipe;
+import org.apache.camel.v1.PipeSpec;
 import org.junit.jupiter.api.AfterAll;
 import org.junit.jupiter.api.BeforeAll;
 import org.junit.jupiter.api.BeforeEach;
@@ -41,6 +43,7 @@ import org.junit.jupiter.api.TestInstance;
 public class KubeBaseTest {
 
     protected static Integration integration;
+    protected static Pipe pipe;
 
     private KubernetesMockServer k8sServer;
 
@@ -90,4 +93,26 @@ public class KubeBaseTest {
         return created;
     }
 
+    protected Pipe createPipe() throws IOException {
+        return createPipe("pipe");
+    }
+
+    protected Pipe createPipe(String name) throws IOException {
+        if (pipe == null) {
+            pipe = KubernetesHelper.yaml().loadAs(
+                    
IOHelper.loadText(KubeBaseTest.class.getResourceAsStream("pipe.yaml")), 
Pipe.class);
+        }
+
+        Pipe created = new Pipe();
+        created.getMetadata().setName(name);
+        created.setSpec(new PipeSpec());
+        created.getSpec().setSource(pipe.getSpec().getSource());
+        created.getSpec().setSink(pipe.getSpec().getSink());
+        created.getSpec().setSteps(pipe.getSpec().getSteps());
+        created.getSpec().setErrorHandler(pipe.getSpec().getErrorHandler());
+        created.getSpec().setIntegration(pipe.getSpec().getIntegration());
+
+        return created;
+    }
+
 }
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-k/src/test/resources/org/apache/camel/dsl/jbang/core/commands/k/pipe.yaml
 
b/dsl/camel-jbang/camel-jbang-plugin-k/src/test/resources/org/apache/camel/dsl/jbang/core/commands/k/pipe.yaml
new file mode 100644
index 00000000000..5ad3d12a255
--- /dev/null
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-k/src/test/resources/org/apache/camel/dsl/jbang/core/commands/k/pipe.yaml
@@ -0,0 +1,38 @@
+#
+# 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: Pipe
+metadata:
+  name: timer-to-log
+  annotations:
+    camel.apache.org/operator.id: camel-k
+spec:
+  source:
+    ref:
+      kind: Kamelet
+      apiVersion: camel.apache.org/v1
+      name: timer-source
+    properties:
+      message: "hello world"
+  sink:
+    ref:
+      kind: Kamelet
+      apiVersion: camel.apache.org/v1
+      name: log-sink
+      properties:
+        showHeaders: true

Reply via email to