Repository: apache/camel-k
Pull request #4309 (2023-05-02 08:21:27)

chore(e2e) Upgrade test operators
<!-- Description -->

Upgrade e2e deployed operators:
* otlp collector
* olm

Update defaults for DefaultRuntimeVersion

<!--
Enter your extended release note in the below block. If the PR requires
additional action from users switching to the new release, include the string
"action required". If no release note is required, write "NONE". 

You can (optionally) mark this PR with labels "kind/bug" or "kind/feature" to make sure
the text is added to the right section of the release notes. 
-->

**Release Note**
```release-note
NONE
```



Patch:
diff --git a/.github/actions/automatic-updates/action.yml b/.github/actions/automatic-updates/action.yml
index e52ed092d..d17bae361 100644
--- a/.github/actions/automatic-updates/action.yml
+++ b/.github/actions/automatic-updates/action.yml
@@ -53,9 +53,7 @@ runs:
         CI_USER: "github-actions[bot]"
         CI_EMAIL: "41898282+github-actions[bot]@users.noreply.github.com"
       run: |
-        make generate
-        make update-docs
-        make update-default-camel
+        make update-default-camel generate codegen update-docs
         git add -A && git commit -m 'chore: nightly resource refresh' && echo "refresh=1" >> $GITHUB_ENV || echo "No changes to make update-docs"
     - name: Push changes
       shell: bash
diff --git a/.github/actions/kamel-config-cluster/action.yaml b/.github/actions/kamel-config-cluster/action.yaml
index d73b44e90..deea9af19 100644
--- a/.github/actions/kamel-config-cluster/action.yaml
+++ b/.github/actions/kamel-config-cluster/action.yaml
@@ -164,10 +164,10 @@ runs:
         if [ $? != 0 ]; then
           set -e
           echo "OLM not detected on cluster so downloading and installing"
-          kubectl create -f https://github.com/operator-framework/operator-lifecycle-manager/releases/download/v0.21.2/crds.yaml
+          kubectl create -f https://github.com/operator-framework/operator-lifecycle-manager/releases/download/v0.24.0/crds.yaml
           # wait for a while to be sure CRDs are installed
           sleep 1
-          kubectl create -f https://github.com/operator-framework/operator-lifecycle-manager/releases/download/v0.21.2/olm.yaml
+          kubectl create -f https://github.com/operator-framework/operator-lifecycle-manager/releases/download/v0.24.0/olm.yaml
         fi
         set -e
 
diff --git a/go.mod b/go.mod
index 8e5c51233..3f84c6ee4 100644
--- a/go.mod
+++ b/go.mod
@@ -7,7 +7,7 @@ require (
 	github.com/apache/camel-k/v2/pkg/apis/camel v0.0.0
 	github.com/apache/camel-k/v2/pkg/client/camel v0.0.0
 	github.com/apache/camel-k/v2/pkg/kamelet/repository v0.0.0
-	github.com/container-tools/spectrum v0.6.9
+	github.com/container-tools/spectrum v0.6.10
 	github.com/evanphx/json-patch v4.12.0+incompatible
 	github.com/fatih/camelcase v1.0.0
 	github.com/fatih/structs v1.1.0
@@ -93,7 +93,7 @@ require (
 	github.com/containerd/continuity v0.1.0 // indirect
 	github.com/containerd/stargz-snapshotter/estargz v0.14.1 // indirect
 	github.com/davecgh/go-spew v1.1.1 // indirect
-	github.com/docker/cli v23.0.4+incompatible // indirect
+	github.com/docker/cli v23.0.5+incompatible // indirect
 	github.com/docker/distribution v2.8.1+incompatible // indirect
 	github.com/docker/docker v23.0.3+incompatible // indirect
 	github.com/docker/docker-credential-helpers v0.7.0 // indirect
diff --git a/go.sum b/go.sum
index 749477fb8..2012e6ed6 100644
--- a/go.sum
+++ b/go.sum
@@ -234,8 +234,8 @@ github.com/cockroachdb/errors v1.2.4/go.mod h1:rQD95gz6FARkaKkQXUksEje/d9a6wBJoC
 github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f/go.mod h1:i/u985jwjWRlyHXQbwatDASoW0RMlZ/3i9yJHE2xLkI=
 github.com/container-tools/go-containerregistry v0.7.1-0.20211124090132-40ccc94a466b h1:PnXGj/WjUnNI7O3AAjXIYs+8QXVRzgoxurEgVgsItrU=
 github.com/container-tools/go-containerregistry v0.7.1-0.20211124090132-40ccc94a466b/go.mod h1:6cMIl1RfryEiPzBE67OgtZdEiLWz4myqCQIiBMy3CsM=
-github.com/container-tools/spectrum v0.6.9 h1:b3kVGM7Z+ZNgj3i4v1u0sSfR/Kq6XJNe4TwZFuQ6iL8=
-github.com/container-tools/spectrum v0.6.9/go.mod h1:dGbIq03rtRodtkC5wq0OeygkUL53dhAhurB90c870c0=
+github.com/container-tools/spectrum v0.6.10 h1:2Y3gY0qixeCfW34Kij1mgtqt/Xyc2lSmEG7UIIcp7x8=
+github.com/container-tools/spectrum v0.6.10/go.mod h1:LgoHcknbRBNbs+yT43ooAT9BTdNTkFen1Foq/ppvSAs=
 github.com/containerd/aufs v0.0.0-20200908144142-dab0cbea06f4/go.mod h1:nukgQABAEopAHvB6j7cnP5zJ+/3aVcE7hCYqvIwAHyE=
 github.com/containerd/aufs v0.0.0-20201003224125-76a6863f2989/go.mod h1:AkGGQs9NM2vtYHaUen+NljV0/baGCAPELGm2q9ZXpWU=
 github.com/containerd/aufs v0.0.0-20210316121734-20793ff83c97/go.mod h1:kL5kd6KM5TzQjR79jljyi4olc1Vrx6XBlcyj3gNv2PU=
@@ -367,8 +367,8 @@ github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZm
 github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
 github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E=
 github.com/docker/cli v20.10.11+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
-github.com/docker/cli v23.0.4+incompatible h1:xClB7PsiATttDHj8ce5qvJcikiApNy7teRR1XkoBZGs=
-github.com/docker/cli v23.0.4+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
+github.com/docker/cli v23.0.5+incompatible h1:ufWmAOuD3Vmr7JP2G5K3cyuNC4YZWiAsuDEvFVVDafE=
+github.com/docker/cli v23.0.5+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
 github.com/docker/distribution v0.0.0-20190905152932-14b96e55d84c/go.mod h1:0+TTO4EOBfRPhZXAeF1Vu+W3hHZ8eLp8PgKVZlcvtFY=
 github.com/docker/distribution v2.7.1-0.20190205005809-0d3efadf0154+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
 github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
diff --git a/pkg/cmd/run.go b/pkg/cmd/run.go
index 73708f6ec..2911e87c2 100644
--- a/pkg/cmd/run.go
+++ b/pkg/cmd/run.go
@@ -562,14 +562,26 @@ func (o *runCmdOptions) createOrUpdateIntegration(cmd *cobra.Command, c client.C
 
 	if existing == nil {
 		err = c.Create(o.Context, integration)
+		if err != nil {
+			return nil, err
+		}
 		fmt.Fprintln(cmd.OutOrStdout(), `Integration "`+name+`" created`)
 	} else {
-		err = c.Patch(o.Context, integration, ctrl.MergeFromWithOptions(existing, ctrl.MergeFromWithOptimisticLock{}))
-		fmt.Fprintln(cmd.OutOrStdout(), `Integration "`+name+`" updated`)
-	}
+		patch := ctrl.MergeFrom(existing)
+		d, err := patch.Data(integration)
+		if err != nil {
+			return nil, err
+		}
 
-	if err != nil {
-		return nil, err
+		if string(d) == "{}" {
+			fmt.Fprintln(cmd.OutOrStdout(), `Integration "`+name+`" unchanged`)
+			return integration, nil
+		}
+		err = c.Patch(o.Context, integration, patch)
+		if err != nil {
+			return nil, err
+		}
+		fmt.Fprintln(cmd.OutOrStdout(), `Integration "`+name+`" updated`)
 	}
 
 	return integration, nil
diff --git a/pkg/controller/integrationkit/integrationkit_controller.go b/pkg/controller/integrationkit/integrationkit_controller.go
index 7ed069e96..71d917301 100644
--- a/pkg/controller/integrationkit/integrationkit_controller.go
+++ b/pkg/controller/integrationkit/integrationkit_controller.go
@@ -148,6 +148,11 @@ func add(_ context.Context, mgr manager.Manager, r reconcile.Reconciler) error {
 				}
 
 				for _, kit := range list.Items {
+					if v, ok := kit.Annotations[v1.PlatformSelectorAnnotation]; ok && v != p.Name {
+						log.Infof("Integration kit %s is waiting for selected integration platform '%s' - skip it now", kit.Name, v)
+						continue
+					}
+
 					if kit.Status.Phase == v1.IntegrationKitPhaseWaitingForPlatform {
 						log.Infof("Platform %s ready, wake-up integration kit: %s", p.Name, kit.Name)
 						requests = append(requests, reconcile.Request{
diff --git a/release.adoc b/release.adoc
index 6bb3e72d9..af67561d9 100644
--- a/release.adoc
+++ b/release.adoc
@@ -4,62 +4,7 @@ This procedure describes all the steps required to release a new version of Apac
 It is not intended to be final, but rather a working document that needs to be updated when new requirements are found or
 the technical process changes.
 
-[[releasing-camel-kamelets]]
-== Releasing Camel Kamelets
-
-The https://github.com/apache/camel-kamelets[Camel Kamelets] repository contains some artifacts that need to be pushed into Maven together
-with a Camel K release.
-
-Follow instructions on https://github.com/apache/camel-kamelets[Camel Kamelets] for the commands needed to release it.
-
-Go to https://repository.apache.org/ and close the staging repository.
-
-A URL will be generated for the repository, like: https://repository.apache.org/content/repositories/orgapachecamel-xxxx. The URL needs to be communicated during the voting process.
-
-The source code for the created tag, with sha512sum and signature need to be copied in the Apache dev SVN at https://dist.apache.org/repos/dist/dev/camel/camel-kamelets/<version>.
-
-Commit all files to the SVN.
-
-Voting can start for camel-kamelets, or a combined vote with camel-k can be started later.
-
-[[releasing-camel-k-runtime]]
-== Releasing Camel K Runtime
-
-The https://github.com/apache/camel-k-runtime[Camel K Runtime project] is not a standalone project, it's meant to be used with Camel K. For this reason, it's common to release the runtime artifacts together with a release of Camel K.
-To avoid depending on snapshots, camel-k-runtime can be also released independently.
-
-Follow the https://github.com/apache/camel/blob/main/docs/user-manual/modules/ROOT/pages/release-guide.adoc[Camel release guide] in order to setup the Maven environment and gpg for the release.
-
-To prepare a release and check problems:
-```
-./mvnw release:prepare -DdryRun -Prelease
-```
-
-Check the signatures of the files, then clean and prepare the actual release:
-
-```
-./mvnw release:clean -Prelease
-./mvnw release:prepare -Prelease
-```
-
-Then perform the release:
-
-```
-./mvnw release:perform -Prelease
-```
-
-Go to https://repository.apache.org/ and close the staging repository.
-
-A URL will be generated for the repository, like: https://repository.apache.org/content/repositories/orgapachecamel-xxxx. The URL needs to be communicated during the voting process.
-
-Now run:
-
-```
-cd release-utils/scripts/
-./upload-sources.sh <released_version> <released_version>
-```
-
-Voting can start for camel-k-runtime, or a combined vote with camel-k can be started later.
+NOTE: starting Camel K version 2, the release process of Kamelets catalog and Camel K Runtime is independent.
 
 [[releasing-camel-k-crds]]
 == Releasing Camel K CRD dependency
@@ -97,13 +42,12 @@ Releasing the Camel K main artifacts require some manual steps.
 
 First thing is to checkout main branch and sync with upstream.
 
-Next, the version of the runtime libraries to use need to be aligned in the Makefile (use the just released camel-k-runtime version if available).
+Next, the version of the runtime libraries to use need to be aligned in the Makefile.
 
 The following tasks need to be done:
 
 - Align `KAMELET_CATALOG_REPO_TAG` in Makefile to latest released tag of the camel-kamelets repository
-- Align `RUNTIME_VERSION` in Makefile to latest runtime release
-- Set `STAGING_RUNTIME_REPO` to the URL of the staging repo (if using a staged camel-k-runtime version)
+- Align `RUNTIME_VERSION` in Makefile to latest Camel K Runtime release
 - Ensure `LAST_RELEASED_VERSION` points to latest released version of Camel K
 
 ```
@@ -175,10 +119,9 @@ A docker image `camelk/camel-k:<version>` will be pushed to the staging organiza
 A combined release can be immediately tested with the following install command:
 
 ```
-kamel install --operator-image=camelk/camel-k:$CAMEL_K_VERSION --maven-repository=https://repository.apache.org/content/repositories/orgapachecamel-$APACHE_NEXUS_RUNTIME_REPO_ID
+kamel install --operator-image=camelk/camel-k:$CAMEL_K_VERSION
 ```
 
-
 [[release-notes]]
 == Release notes
 
@@ -216,8 +159,6 @@ An email should be sent to dev@camel.apache.org asking to test the staged artifa
 
 After the voting is complete with success, the artifacts can be released.
 
-Release the staging repository at: https://repository.apache.org.
-
 Republish docker image in the Apache org on Docker Hub:
 
 ```
@@ -227,12 +168,7 @@ docker tag camelk/camel-k:$VERSION apache/camel-k:$VERSION
 docker push apache/camel-k:$VERSION
 ```
 
-Artifacts committed on https://dist.apache.org/repos/dist/dev/ before the voting process need to be copied to the
-Apache dist repository on: https://dist.apache.org/repos/dist/release/camel.
-
-The same should be done for camel-kamelets and camel-k-runtime in case of combined vote.
-
-Camel-kamelets, camel-k-runtime and camel-k contains a script to promote the release from dev to release
+Artifacts committed on https://dist.apache.org/repos/dist/dev/ before the voting process need to be copied to the Apache dist repository on: https://dist.apache.org/repos/dist/release/camel.
 
 ```
 cd release-utils/scripts/
@@ -245,8 +181,7 @@ Binary files can be now released on Github, together with release notes generate
 Files need to be uploaded manually to a new Github release.
 
 Before announcing the release, a simple test should be done to verify that everything is in place (running a "Hello World" integration
-after an installation done with a simple `kamel install`).
-Do a simple final test.
+after an installation done with a simple `kamel install`). Do a simple final test.
 
 The release can be now announced to dev@camel.apache.org and users@camel.apache.org.
 


Test patch:
diff --git a/.github/actions/e2e-telemetry/action.yml b/.github/actions/e2e-telemetry/action.yml
index 000ddde2d..b7af6187b 100644
--- a/.github/actions/e2e-telemetry/action.yml
+++ b/.github/actions/e2e-telemetry/action.yml
@@ -103,7 +103,7 @@ runs:
     uses: ./.github/actions/kamel-install-otlp-collector
     with:
       otlp-collector-image-name: otel/opentelemetry-collector
-      otlp-collector-image-version: 0.68.0
+      otlp-collector-image-version: 0.75.0
 
   - id: report-problematic
     name: List Tests Marked As Problematic
diff --git a/e2e/builder/build_test.go b/e2e/builder/build_test.go
index 0ce00e9ca..7a7d1b21d 100644
--- a/e2e/builder/build_test.go
+++ b/e2e/builder/build_test.go
@@ -57,16 +57,6 @@ func TestKitMaxBuildLimit(t *testing.T) {
 		buildB := "integration-b"
 		buildC := "integration-c"
 
-		doKitBuildInNamespace(buildA, ns, TestTimeoutShort, kitOptions{
-			operatorID: fmt.Sprintf("camel-k-%s", ns),
-			dependencies: []string{
-				"camel:timer", "camel:log",
-			},
-			traits: []string{
-				"builder.properties=build-property=A",
-			},
-		}, v1.BuildPhaseRunning, v1.IntegrationKitPhaseBuildRunning)
-
 		ns1 := NewTestNamespace(false).GetName()
 		defer DumpNamespace(t, ns1)
 		defer DeleteNamespace(t, ns1)
@@ -80,15 +70,7 @@ func TestKitMaxBuildLimit(t *testing.T) {
 			t.FailNow()
 		}
 
-		doKitBuildInNamespace(buildB, ns1, TestTimeoutShort, kitOptions{
-			operatorID: fmt.Sprintf("camel-k-%s", ns),
-			dependencies: []string{
-				"camel:timer", "camel:log",
-			},
-			traits: []string{
-				"builder.properties=build-property=B",
-			},
-		}, v1.BuildPhaseRunning, v1.IntegrationKitPhaseBuildRunning)
+		Eventually(PlatformPhase(ns1), TestTimeoutMedium).Should(Equal(v1.IntegrationPlatformPhaseReady))
 
 		ns2 := NewTestNamespace(false).GetName()
 		defer DumpNamespace(t, ns2)
@@ -103,6 +85,28 @@ func TestKitMaxBuildLimit(t *testing.T) {
 			t.FailNow()
 		}
 
+		Eventually(PlatformPhase(ns2), TestTimeoutMedium).Should(Equal(v1.IntegrationPlatformPhaseReady))
+
+		doKitBuildInNamespace(buildA, ns, TestTimeoutShort, kitOptions{
+			operatorID: fmt.Sprintf("camel-k-%s", ns),
+			dependencies: []string{
+				"camel:timer", "camel:log",
+			},
+			traits: []string{
+				"builder.properties=build-property=A",
+			},
+		}, v1.BuildPhaseRunning, v1.IntegrationKitPhaseBuildRunning)
+
+		doKitBuildInNamespace(buildB, ns1, TestTimeoutShort, kitOptions{
+			operatorID: fmt.Sprintf("camel-k-%s", ns),
+			dependencies: []string{
+				"camel:timer", "camel:log",
+			},
+			traits: []string{
+				"builder.properties=build-property=B",
+			},
+		}, v1.BuildPhaseRunning, v1.IntegrationKitPhaseBuildRunning)
+
 		doKitBuildInNamespace(buildC, ns2, TestTimeoutShort, kitOptions{
 			operatorID: fmt.Sprintf("camel-k-%s", ns),
 			dependencies: []string{
@@ -178,6 +182,7 @@ func createOperator(ns string, memoryLimit string, buildTimeout string, installA
 
 	operatorID := fmt.Sprintf("camel-k-%s", ns)
 	Expect(KamelInstallWithID(operatorID, ns, args...).Execute()).To(Succeed())
+	Eventually(PlatformPhase(ns), TestTimeoutMedium).Should(Equal(v1.IntegrationPlatformPhaseReady))
 }
 
 func doKitBuildInNamespace(name string, ns string, testTimeout time.Duration, options kitOptions, buildPhase v1.BuildPhase, kitPhase v1.IntegrationKitPhase) {
diff --git a/e2e/commonwithcustominstall/secondary_platform_test.go b/e2e/commonwithcustominstall/secondary_platform_test.go
index c5aebb560..6cabfb305 100644
--- a/e2e/commonwithcustominstall/secondary_platform_test.go
+++ b/e2e/commonwithcustominstall/secondary_platform_test.go
@@ -38,16 +38,15 @@ func TestSecondaryPlatform(t *testing.T) {
 	WithNewTestNamespace(t, func(ns string) {
 		operatorID := "camel-k-platform-secondary"
 		Expect(KamelInstallWithID(operatorID, ns).Execute()).To(Succeed())
+		Eventually(PlatformPhase(ns), TestTimeoutMedium).Should(Equal(v1.IntegrationPlatformPhaseReady))
+
 		Expect(ConfigureSecondaryPlatformWith(ns, func(p *v1.IntegrationPlatform) {
 			p.Name = "secondary"
 			p.Spec.Traits.Container = &traitv1.ContainerTrait{
-				Trait: traitv1.Trait{
-					Configuration: AsTraitConfiguration(map[string]string{
-						"limitCPU": "0.1",
-					}),
-				},
+				LimitCPU: "0.1",
 			}
 		})).To(Succeed())
+		Eventually(SelectedPlatformPhase(ns, "secondary"), TestTimeoutMedium).Should(Equal(v1.IntegrationPlatformPhaseReady))
 
 		Expect(KamelRunWithID(operatorID, ns, "--name", "limited", "--annotation", "camel.apache.org/platform.id=secondary", "files/yaml.yaml").Execute()).To(Succeed())
 
diff --git a/e2e/support/test_support.go b/e2e/support/test_support.go
index 57697449c..5cd425710 100644
--- a/e2e/support/test_support.go
+++ b/e2e/support/test_support.go
@@ -1661,6 +1661,21 @@ func Platform(ns string) func() *v1.IntegrationPlatform {
 	}
 }
 
+func PlatformByName(ns string, name string) func() *v1.IntegrationPlatform {
+	return func() *v1.IntegrationPlatform {
+		lst := v1.NewIntegrationPlatformList()
+		if err := TestClient().List(TestContext, &lst, ctrl.InNamespace(ns)); err != nil {
+			failTest(err)
+		}
+		for _, p := range lst.Items {
+			if p.Name == name {
+				return &p
+			}
+		}
+		return nil
+	}
+}
+
 func CamelCatalog(ns, name string) func() *v1.CamelCatalog {
 	return func() *v1.CamelCatalog {
 		cat := v1.CamelCatalog{}
@@ -1774,6 +1789,16 @@ func PlatformPhase(ns string) func() v1.IntegrationPlatformPhase {
 	}
 }
 
+func SelectedPlatformPhase(ns string, name string) func() v1.IntegrationPlatformPhase {
+	return func() v1.IntegrationPlatformPhase {
+		p := PlatformByName(ns, name)()
+		if p == nil {
+			return ""
+		}
+		return p.Status.Phase
+	}
+}
+
 func PlatformCondition(
 	ns string,
 	conditionType v1.IntegrationPlatformConditionType,
diff --git a/pkg/cmd/run_test.go b/pkg/cmd/run_test.go
index 5322319c8..f31330f7f 100644
--- a/pkg/cmd/run_test.go
+++ b/pkg/cmd/run_test.go
@@ -22,12 +22,14 @@ import (
 	"fmt"
 	"os"
 	"path/filepath"
+	"strings"
 	"testing"
 
 	v1 "github.com/apache/camel-k/v2/pkg/apis/camel/v1"
 	traitv1 "github.com/apache/camel-k/v2/pkg/apis/camel/v1/trait"
 	"github.com/apache/camel-k/v2/pkg/platform"
 	"github.com/apache/camel-k/v2/pkg/trait"
+	"github.com/apache/camel-k/v2/pkg/util/defaults"
 	"github.com/apache/camel-k/v2/pkg/util/test"
 
 	"github.com/spf13/cobra"
@@ -38,6 +40,17 @@ import (
 const (
 	cmdRun            = "run"
 	integrationSource = "example.js"
+	yamlIntegration   = `# camel-k: language=yaml
+
+- from:
+    uri: "timer:yaml"
+    parameters:
+      period: "1000"
+    steps:
+      - setBody:
+          constant: "Hello Camel K from yaml"
+      - to: "log:info"
+`
 )
 
 // nolint: unparam
@@ -54,9 +67,10 @@ func initializeRunCmdOptions(t *testing.T) (*runCmdOptions, *cobra.Command, Root
 // nolint: unparam
 func initializeRunCmdOptionsWithOutput(t *testing.T) (*runCmdOptions, *cobra.Command, RootCmdOptions) {
 	t.Helper()
-
 	defaultIntegrationPlatform := v1.NewIntegrationPlatform("default", platform.DefaultPlatformName)
-	fakeClient, _ := test.NewFakeClient(&defaultIntegrationPlatform)
+	c := v1.NewCamelCatalog(defaultIntegrationPlatform.Namespace, defaults.DefaultRuntimeVersion)
+	c.Spec = v1.CamelCatalogSpec{Runtime: v1.RuntimeSpec{Provider: defaultIntegrationPlatform.Status.Build.RuntimeProvider, Version: defaultIntegrationPlatform.Status.Build.RuntimeVersion}}
+	fakeClient, _ := test.NewFakeClient(&defaultIntegrationPlatform, &c)
 
 	options, rootCmd := kamelTestPreAddCommandInitWithClient(fakeClient)
 	runCmdOptions := addTestRunCmdWithOutput(*options, rootCmd)
@@ -773,3 +787,33 @@ func TestPropertyShouldNotExpand(t *testing.T) {
 	assert.Contains(t, output, "buildProp = ${value}")
 	assert.Contains(t, output, "key = ${value}")
 }
+
+func TestRunOutput(t *testing.T) {
+	var tmpFile1 *os.File
+	var err error
+	if tmpFile1, err = os.CreateTemp("", "camel-k-*.yaml"); err != nil {
+		t.Error(err)
+	}
+	defer tmpFile1.Close()
+
+	assert.Nil(t, tmpFile1.Sync())
+	assert.Nil(t, os.WriteFile(tmpFile1.Name(), []byte(yamlIntegration), 0o400))
+
+	_, rootCmd, _ := initializeRunCmdOptionsWithOutput(t)
+	output, err := test.ExecuteCommand(rootCmd, cmdRun, tmpFile1.Name())
+	_, fileName := filepath.Split(tmpFile1.Name())
+	integrationName := strings.TrimSuffix(fileName, filepath.Ext(fileName))
+	assert.Nil(t, err)
+	assert.Equal(t, fmt.Sprintf("Integration \"%s\" created\n", integrationName), output)
+
+	output, err = test.ExecuteCommand(rootCmd, cmdRun, tmpFile1.Name())
+	assert.Nil(t, err)
+	assert.Equal(t, fmt.Sprintf("Integration \"%s\" unchanged\n", integrationName), output)
+
+	assert.Nil(t, os.WriteFile(tmpFile1.Name(), []byte(strings.Replace(yamlIntegration, "Hello", "Hi", 1)), 0o400))
+	assert.Nil(t, tmpFile1.Sync())
+	output, err = test.ExecuteCommand(rootCmd, cmdRun, tmpFile1.Name())
+	assert.Nil(t, err)
+	assert.Equal(t, fmt.Sprintf("Integration \"%s\" updated\n", integrationName), output)
+
+}
diff --git a/pkg/util/test/client.go b/pkg/util/test/client.go
index adbb8eaa2..7fc121389 100644
--- a/pkg/util/test/client.go
+++ b/pkg/util/test/client.go
@@ -150,7 +150,11 @@ func (c *FakeClient) GetCurrentNamespace(kubeConfig string) (string, error) {
 
 // Patch mimicks patch for server-side apply and simply creates the obj.
 func (c *FakeClient) Patch(ctx context.Context, obj controller.Object, patch controller.Patch, opts ...controller.PatchOption) error {
-	return c.Create(ctx, obj)
+	if err := c.Create(ctx, obj); err != nil {
+		// Create fails if object already exists. Try to update it.
+		return c.Update(ctx, obj)
+	}
+	return nil
 }
 
 func (c *FakeClient) DisableAPIGroupDiscovery(group string) {