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

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

commit 38aa87044f68b16c07d44664dd52e3b4075fc316
Author: Pasquale Congiusti <[email protected]>
AuthorDate: Sat Jun 13 09:15:06 2026 +0200

    feat(install): multi namespace watching
    
    * Enable the possibility to watch another namespace, beside the own 
namespace
    * Enable the possibility to watch multiple namespaces
    * Deprecate operator.id annotation
    * Change install names to clarify each overlay intent
    
    Ref #6616
---
 docs/modules/ROOT/pages/pipes/bind-cli.adoc        |   2 -
 docs/modules/ROOT/pages/running/running-cli.adoc   |   2 -
 e2e/advanced/operator_id_filtering_test.go         |   2 +-
 e2e/common/misc/pipe_test.go                       |  47 -------
 .../{setup_test.go => all_namespaces_test.go}      |  64 ---------
 e2e/install/kustomize/own_namespace_test.go        |  83 ++++++++++++
 e2e/install/kustomize/single_namespace_test.go     |  87 ++++++++++++
 e2e/support/test_support.go                        |  13 +-
 .../kustomization.yaml                             |   0
 .../patch-log-level.yaml                           |   0
 .../patch-node-selector.yaml                       |   0
 .../patch-operator-id.yaml                         |   0
 .../{namespaced => own-namespace}/patch-ports.yaml |   0
 .../patch-resource-requirements.yaml               |   0
 .../patch-toleration.yaml                          |   0
 .../kustomization.yaml}                            |   8 +-
 .../single-namespace/operator}/kustomization.yaml  |  14 +-
 .../operator/patch-envvars.yaml}                   |  19 +--
 .../operator/remove-watch-ns.yaml}                 |   5 +-
 .../tenant-a-ns-rbac}/kustomization.yaml           |  15 +--
 .../patch-rolebinding-subjects.yaml}               |   9 +-
 pkg/apis/camel/v1/common_types.go                  |   2 +
 pkg/cmd/bind.go                                    |  24 ++--
 pkg/cmd/bind_test.go                               |  18 +--
 pkg/cmd/operator/operator.go                       |  54 ++++----
 pkg/cmd/operator/operator_test.go                  | 150 +++++++++++++++++++++
 pkg/cmd/promote_test.go                            |  12 +-
 pkg/cmd/run.go                                     |  10 +-
 pkg/cmd/run_test.go                                |  14 --
 pkg/controller/integration/build.go                |   1 +
 .../integration/integration_controller.go          |   9 +-
 pkg/controller/integrationkit/build.go             |   1 +
 .../integrationkit/integrationkit_controller.go    |   1 +
 pkg/install/common.go                              |  50 -------
 pkg/install/optional.go                            |   2 +-
 pkg/platform/defaults.go                           |  13 +-
 pkg/platform/operator.go                           |  15 +--
 pkg/resources/config/manager/kustomization.yaml    |  10 ++
 .../config/manager/operator-deployment.yaml        |   6 +
 pkg/util/gitops/gitops.go                          |   4 +
 script/Makefile                                    |   2 +-
 41 files changed, 460 insertions(+), 308 deletions(-)

diff --git a/docs/modules/ROOT/pages/pipes/bind-cli.adoc 
b/docs/modules/ROOT/pages/pipes/bind-cli.adoc
index 5133b22a6..fd48effbe 100644
--- a/docs/modules/ROOT/pages/pipes/bind-cli.adoc
+++ b/docs/modules/ROOT/pages/pipes/bind-cli.adoc
@@ -65,8 +65,6 @@ As an example, take the option available on the `kamel bind 
timer-source log-sin
 apiVersion: camel.apache.org/v1
 kind: Pipe
 metadata:
-  annotations:
-    camel.apache.org/operator.id: camel-k
   name: timer-source-to-log-sink
   namespace: camel-k
 spec:
diff --git a/docs/modules/ROOT/pages/running/running-cli.adoc 
b/docs/modules/ROOT/pages/running/running-cli.adoc
index 67c8d14d9..d4d871ebe 100644
--- a/docs/modules/ROOT/pages/running/running-cli.adoc
+++ b/docs/modules/ROOT/pages/running/running-cli.adoc
@@ -67,8 +67,6 @@ As an example, take the option available on the `kamel run 
test.yaml -t promethe
 apiVersion: camel.apache.org/v1
 kind: Integration
 metadata:
-  annotations:
-    camel.apache.org/operator.id: camel-k
   name: test
 spec:
   flows:
diff --git a/e2e/advanced/operator_id_filtering_test.go 
b/e2e/advanced/operator_id_filtering_test.go
index 4e2d71412..184603d91 100644
--- a/e2e/advanced/operator_id_filtering_test.go
+++ b/e2e/advanced/operator_id_filtering_test.go
@@ -91,7 +91,7 @@ func TestOperatorIDFiltering(t *testing.T) {
                                                                
v1.IntegrationKitTypeLabel: v1.IntegrationKitTypeExternal,
                                                        },
                                                        Annotations: 
map[string]string{
-                                                               
"camel.apache.org/operator.id": operator2,
+                                                               
v1.OperatorIDAnnotation: operator2,
                                                        },
                                                },
                                                Spec: v1.IntegrationKitSpec{
diff --git a/e2e/common/misc/pipe_test.go b/e2e/common/misc/pipe_test.go
index 4aa7797bd..2402919b6 100644
--- a/e2e/common/misc/pipe_test.go
+++ b/e2e/common/misc/pipe_test.go
@@ -127,53 +127,6 @@ func TestPipe(t *testing.T) {
        })
 }
 
-func TestPipeWithImage(t *testing.T) {
-       WithNewTestNamespace(t, func(ctx context.Context, g *WithT, ns string) {
-               bindingID := "with-image-binding"
-
-               t.Run("run with initial image", func(t *testing.T) {
-                       expectedImage := "quay.io/fuse_qe/echo-server:0.3.2"
-
-                       g.Expect(KamelBind(t, ctx, ns, "my-own-timer-source", 
"my-own-log-sink",
-                               "--trait", "container.image="+expectedImage, 
"--trait", "jvm.enabled=false",
-                               "--trait", "kamelets.enabled=false", "--trait", 
"dependencies.enabled=false",
-                               "--annotation", "test=1", "--name", 
bindingID).Execute()).To(Succeed())
-
-                       g.Eventually(IntegrationGeneration(t, ctx, ns, 
bindingID)).
-                               Should(gstruct.PointTo(BeNumerically("==", 1)))
-                       g.Eventually(Integration(t, ctx, ns, 
bindingID)).Should(WithTransform(Annotations,
-                               HaveKeyWithValue("test", "1"),
-                       ))
-                       g.Eventually(IntegrationStatusImage(t, ctx, ns, 
bindingID)).
-                               Should(Equal(expectedImage))
-                       g.Eventually(IntegrationPodPhase(t, ctx, ns, 
bindingID), TestTimeoutShort).
-                               Should(Equal(corev1.PodRunning))
-                       g.Eventually(IntegrationPodImage(t, ctx, ns, 
bindingID)).
-                               Should(Equal(expectedImage))
-               })
-
-               t.Run("run with new image", func(t *testing.T) {
-                       expectedImage := "quay.io/fuse_qe/echo-server:0.3.3"
-
-                       g.Expect(KamelBind(t, ctx, ns, "my-own-timer-source", 
"my-own-log-sink",
-                               "--trait", "container.image="+expectedImage, 
"--trait", "jvm.enabled=false",
-                               "--trait", "kamelets.enabled=false", "--trait", 
"dependencies.enabled=false",
-                               "--annotation", "test=2", "--name", 
bindingID).Execute()).To(Succeed())
-                       g.Eventually(IntegrationGeneration(t, ctx, ns, 
bindingID)).
-                               Should(gstruct.PointTo(BeNumerically("==", 1)))
-                       g.Eventually(Integration(t, ctx, ns, 
bindingID)).Should(WithTransform(Annotations,
-                               HaveKeyWithValue("test", "2"),
-                       ))
-                       g.Eventually(IntegrationStatusImage(t, ctx, ns, 
bindingID)).
-                               Should(Equal(expectedImage))
-                       g.Eventually(IntegrationPodPhase(t, ctx, ns, 
bindingID), TestTimeoutShort).
-                               Should(Equal(corev1.PodRunning))
-                       g.Eventually(IntegrationPodImage(t, ctx, ns, 
bindingID)).
-                               Should(Equal(expectedImage))
-               })
-       })
-}
-
 func TestPipeScale(t *testing.T) {
        WithNewTestNamespace(t, func(ctx context.Context, g *WithT, ns string) {
                name := RandomizedSuffixName("timer2log")
diff --git a/e2e/install/kustomize/setup_test.go 
b/e2e/install/kustomize/all_namespaces_test.go
similarity index 61%
rename from e2e/install/kustomize/setup_test.go
rename to e2e/install/kustomize/all_namespaces_test.go
index e5740b9d3..9df5e5e8f 100644
--- a/e2e/install/kustomize/setup_test.go
+++ b/e2e/install/kustomize/all_namespaces_test.go
@@ -37,67 +37,6 @@ import (
        . "github.com/onsi/gomega"
 )
 
-func TestKustomizeNamespaced(t *testing.T) {
-       kustomizeDir := testutil.MakeTempCopyDir(t, "../../../install")
-       WithNewTestNamespace(t, func(ctx context.Context, g *WithT, ns string) {
-               // Let's make sure no CRD is yet available in the cluster
-               // as we must make the procedure to install them accordingly
-               g.Eventually(CRDs(t)).Should(BeNil(), "No Camel K CRDs should 
be previously installed for this test")
-               // We must change a few values in the Kustomize config
-               ExpectExecSucceed(t, g,
-                       exec.Command(
-                               "sed",
-                               "-i",
-                               fmt.Sprintf("s/namespace: .*/namespace: %s/", 
ns),
-                               
fmt.Sprintf("%s/overlays/kubernetes/namespaced/kustomization.yaml", 
kustomizeDir),
-                       ))
-               ExpectExecSucceed(t, g, Kubectl(
-                       "apply",
-                       "-k",
-                       fmt.Sprintf("%s/overlays/kubernetes/namespaced", 
kustomizeDir),
-                       "--server-side",
-               ))
-
-               // Refresh the test client to account for the newly installed 
CRDs
-               RefreshClient(t)
-               g.Eventually(OperatorPod(t, ctx, ns)).ShouldNot(BeNil())
-               g.Eventually(OperatorPodPhase(t, ctx, 
ns)).Should(Equal(corev1.PodRunning))
-               // Check if restricted security context has been applied
-               operatorPod := OperatorPod(t, ctx, ns)()
-               
g.Expect(operatorPod.Spec.Containers[0].SecurityContext.RunAsNonRoot).To(
-                       Equal(DefaultOperatorSecurityContext().RunAsNonRoot),
-               )
-               
g.Expect(operatorPod.Spec.Containers[0].SecurityContext.Capabilities).To(
-                       Equal(DefaultOperatorSecurityContext().Capabilities),
-               )
-               
g.Expect(operatorPod.Spec.Containers[0].SecurityContext.SeccompProfile).To(
-                       Equal(DefaultOperatorSecurityContext().SeccompProfile),
-               )
-               
g.Expect(operatorPod.Spec.Containers[0].SecurityContext.AllowPrivilegeEscalation).To(
-                       
Equal(DefaultOperatorSecurityContext().AllowPrivilegeEscalation),
-               )
-
-               // Test a simple integration is running
-               g.Expect(KamelRun(t, ctx, ns, 
"files/yaml.yaml").Execute()).To(Succeed())
-               g.Eventually(IntegrationPodPhase(t, ctx, ns, "yaml"), 
TestTimeoutMedium).Should(Equal(corev1.PodRunning))
-               g.Eventually(IntegrationConditionStatus(t, ctx, ns, "yaml", 
v1.IntegrationConditionReady), 
TestTimeoutShort).Should(Equal(corev1.ConditionTrue))
-               g.Eventually(IntegrationLogs(t, ctx, ns, "yaml"), 
TestTimeoutShort).Should(ContainSubstring("Magicstring!"))
-
-               // Test operator only uninstall
-               UninstallOperator(t, ctx, g, ns, "../../../")
-
-               g.Eventually(OperatorPod(t, ctx, ns)).Should(BeNil())
-               g.Eventually(Integration(t, ctx, ns, "yaml"), 
TestTimeoutShort).ShouldNot(BeNil())
-               g.Eventually(IntegrationConditionStatus(t, ctx, ns, "yaml", 
v1.IntegrationConditionReady), 
TestTimeoutShort).Should(Equal(corev1.ConditionTrue))
-
-               // Test CRD uninstall (will remove Integrations as well)
-               UninstallCRDs(t, ctx, g, "../../../")
-
-               g.Eventually(OperatorPod(t, ctx, ns)).Should(BeNil())
-               g.Eventually(CRDs(t)).Should(BeNil())
-       })
-}
-
 func TestKustomizeDescoped(t *testing.T) {
        kustomizeDir := testutil.MakeTempCopyDir(t, "../../../install")
        WithNewTestNamespace(t, func(ctx context.Context, g *WithT, ns string) {
@@ -119,9 +58,6 @@ func TestKustomizeDescoped(t *testing.T) {
                        "--server-side",
                ))
 
-               // Refresh the test client to account for the newly installed 
CRDs
-               RefreshClient(t)
-
                podFunc := OperatorPod(t, ctx, ns)
                g.Eventually(podFunc).ShouldNot(BeNil())
                g.Eventually(OperatorPodPhase(t, ctx, 
ns)).Should(Equal(corev1.PodRunning))
diff --git a/e2e/install/kustomize/own_namespace_test.go 
b/e2e/install/kustomize/own_namespace_test.go
new file mode 100644
index 000000000..82f0a7161
--- /dev/null
+++ b/e2e/install/kustomize/own_namespace_test.go
@@ -0,0 +1,83 @@
+//go:build integration
+// +build integration
+
+// To enable compilation of this file in Goland, go to "Settings -> Go -> 
Vendoring & Build Tags -> Custom Tags" and add "integration"
+
+/*
+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 kustomize
+
+import (
+       "context"
+       "fmt"
+       "os/exec"
+       "testing"
+
+       corev1 "k8s.io/api/core/v1"
+
+       . "github.com/apache/camel-k/v2/e2e/support"
+       testutil "github.com/apache/camel-k/v2/e2e/support/util"
+       v1 "github.com/apache/camel-k/v2/pkg/apis/camel/v1"
+
+       . "github.com/onsi/gomega"
+)
+
+func TestKustomizeOwnNamespace(t *testing.T) {
+       kustomizeDir := testutil.MakeTempCopyDir(t, "../../../install")
+       WithNewTestNamespace(t, func(ctx context.Context, g *WithT, ns string) {
+               // Let's make sure no CRD is yet available in the cluster
+               // as we must make the procedure to install them accordingly
+               g.Eventually(CRDs(t)).Should(BeNil(), "No Camel K CRDs should 
be previously installed for this test")
+               // We must change a few values in the Kustomize config
+               ExpectExecSucceed(t, g,
+                       exec.Command(
+                               "sed",
+                               "-i",
+                               fmt.Sprintf("s/namespace: .*/namespace: %s/", 
ns),
+                               
fmt.Sprintf("%s/overlays/kubernetes/own-namespace/kustomization.yaml", 
kustomizeDir),
+                       ))
+               ExpectExecSucceed(t, g, Kubectl(
+                       "apply",
+                       "-k",
+                       fmt.Sprintf("%s/overlays/kubernetes/own-namespace", 
kustomizeDir),
+                       "--server-side",
+               ))
+
+               g.Eventually(OperatorPod(t, ctx, ns)).ShouldNot(BeNil())
+               g.Eventually(OperatorPodPhase(t, ctx, 
ns)).Should(Equal(corev1.PodRunning))
+
+               // Test a simple integration is running
+               g.Expect(KamelRun(t, ctx, ns, 
"files/yaml.yaml").Execute()).To(Succeed())
+               g.Eventually(IntegrationPodPhase(t, ctx, ns, "yaml"), 
TestTimeoutMedium).Should(Equal(corev1.PodRunning))
+               g.Eventually(IntegrationConditionStatus(t, ctx, ns, "yaml", 
v1.IntegrationConditionReady), 
TestTimeoutShort).Should(Equal(corev1.ConditionTrue))
+               g.Eventually(IntegrationLogs(t, ctx, ns, "yaml"), 
TestTimeoutShort).Should(ContainSubstring("Magicstring!"))
+
+               // Test operator only uninstall
+               UninstallOperator(t, ctx, g, ns, "../../../")
+
+               g.Eventually(OperatorPod(t, ctx, ns)).Should(BeNil())
+               g.Eventually(Integration(t, ctx, ns, "yaml"), 
TestTimeoutShort).ShouldNot(BeNil())
+               g.Eventually(IntegrationConditionStatus(t, ctx, ns, "yaml", 
v1.IntegrationConditionReady), 
TestTimeoutShort).Should(Equal(corev1.ConditionTrue))
+
+               // Test CRD uninstall (will remove Integrations as well)
+               UninstallCRDs(t, ctx, g, "../../../")
+
+               g.Eventually(OperatorPod(t, ctx, ns)).Should(BeNil())
+               g.Eventually(CRDs(t)).Should(BeNil())
+       })
+}
diff --git a/e2e/install/kustomize/single_namespace_test.go 
b/e2e/install/kustomize/single_namespace_test.go
new file mode 100644
index 000000000..e0229e49a
--- /dev/null
+++ b/e2e/install/kustomize/single_namespace_test.go
@@ -0,0 +1,87 @@
+//go:build integration
+// +build integration
+
+// To enable compilation of this file in Goland, go to "Settings -> Go -> 
Vendoring & Build Tags -> Custom Tags" and add "integration"
+
+/*
+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 kustomize
+
+import (
+       "context"
+       "fmt"
+       "testing"
+       "time"
+
+       corev1 "k8s.io/api/core/v1"
+
+       . "github.com/apache/camel-k/v2/e2e/support"
+       testutil "github.com/apache/camel-k/v2/e2e/support/util"
+       v1 "github.com/apache/camel-k/v2/pkg/apis/camel/v1"
+
+       . "github.com/onsi/gomega"
+)
+
+func TestKustomizeSingleNamespace(t *testing.T) {
+       kustomizeDir := testutil.MakeTempCopyDir(t, "../../../install")
+
+       // The operator is expected to be installed in "operators" namespace
+       // it also expects to reconcile correctly an Integration in namespace 
"tenant-a"
+       // but it won't reconcile in any other namespaces, for example, 
"tenan-b"
+       WithNamedTestNamespace(t, func(ctx context.Context, g *WithT, 
operatorNs string) {
+               WithNamedTestNamespace(t, func(ctx context.Context, g *WithT, 
tenantNs string) {
+                       // Let's make sure no CRD is yet available in the 
cluster
+                       // as we must make the procedure to install them 
accordingly
+                       g.Eventually(CRDs(t)).Should(BeNil(), "No Camel K CRDs 
should be previously installed for this test")
+                       ExpectExecSucceed(t, g, Kubectl(
+                               "apply",
+                               "-k",
+                               
fmt.Sprintf("%s/overlays/kubernetes/single-namespace", kustomizeDir),
+                               "--server-side",
+                       ))
+                       g.Eventually(OperatorPod(t, ctx, 
operatorNs)).ShouldNot(BeNil())
+                       g.Eventually(OperatorPodPhase(t, ctx, 
operatorNs)).Should(Equal(corev1.PodRunning))
+
+                       WithNamedTestNamespace(t, func(ctx context.Context, g 
*WithT, tenantNs string) {
+                               // Test a simple integration in "tenant-b" is 
not reconciled
+                               g.Expect(KamelRun(t, ctx, tenantNs, 
"files/yaml.yaml").Execute()).To(Succeed())
+                               g.Consistently(IntegrationPhase(t, ctx, 
tenantNs, "yaml"), 10*time.Second).Should(BeEmpty())
+                       }, "tenant-b")
+
+                       // Test a simple integration in "tenant-a" is 
reconciled and runs correctly
+                       g.Expect(KamelRun(t, ctx, tenantNs, 
"files/yaml.yaml").Execute()).To(Succeed())
+                       g.Eventually(IntegrationConditionStatus(t, ctx, 
tenantNs, "yaml", v1.IntegrationConditionReady), TestTimeoutMedium).
+                               Should(Equal(corev1.ConditionTrue))
+                       g.Eventually(IntegrationLogs(t, ctx, tenantNs, "yaml"), 
TestTimeoutShort).Should(ContainSubstring("Magicstring!"))
+
+                       // Test operator only uninstall
+                       UninstallOperator(t, ctx, g, operatorNs, "../../../")
+
+                       g.Eventually(OperatorPod(t, ctx, 
operatorNs)).Should(BeNil())
+                       g.Eventually(Integration(t, ctx, "tenant-a", "yaml"), 
TestTimeoutShort).ShouldNot(BeNil())
+                       g.Eventually(IntegrationConditionStatus(t, ctx, 
"tenant-a", "yaml", v1.IntegrationConditionReady), TestTimeoutShort).
+                               Should(Equal(corev1.ConditionTrue))
+
+                       // Test CRD uninstall (will remove Integrations as well)
+                       UninstallCRDs(t, ctx, g, "../../../")
+
+                       g.Eventually(OperatorPod(t, ctx, 
operatorNs)).Should(BeNil())
+                       g.Eventually(CRDs(t)).Should(BeNil())
+               }, "tenant-a")
+       }, "operators")
+}
diff --git a/e2e/support/test_support.go b/e2e/support/test_support.go
index 156f7cde9..e61bbca34 100644
--- a/e2e/support/test_support.go
+++ b/e2e/support/test_support.go
@@ -292,7 +292,10 @@ func kamelCommandWithContext(t *testing.T, ctx 
context.Context, command string,
        cmdMutex.Lock()
        defer cmdMutex.Unlock()
 
-       cmdArgs := []string{command, "-n", namespace, "--operator-id", 
operatorID}
+       cmdArgs := []string{command, "-n", namespace}
+       if operatorID != platform.DefaultPlatformName {
+               cmdArgs = append(cmdArgs, "--operator-id", operatorID)
+       }
        cmdArgs = append(cmdArgs, args...)
        return KamelWithContext(t, ctx, cmdArgs...)
 }
@@ -2539,6 +2542,14 @@ func WithNewTestNamespace(t *testing.T, doRun 
func(context.Context, *gomega.With
        invokeUserTestCode(t, testContext, ns.GetName(), doRun)
 }
 
+func WithNamedTestNamespace(t *testing.T, doRun func(context.Context, 
*gomega.WithT, string), namespace string) {
+       ns := NewNamedTestNamespace(t, testContext, namespace, false)
+       defer deleteTestNamespace(t, testContext, ns)
+       defer userCleanup(t)
+
+       invokeUserTestCode(t, testContext, ns.GetName(), doRun)
+}
+
 func WithNewTestNamespaceWithKnativeBroker(t *testing.T, doRun 
func(context.Context, *gomega.WithT, string)) {
        ns := NewTestNamespace(t, testContext, true)
        defer deleteTestNamespace(t, testContext, ns)
diff --git a/install/overlays/kubernetes/namespaced/kustomization.yaml 
b/install/overlays/kubernetes/own-namespace/kustomization.yaml
similarity index 100%
rename from install/overlays/kubernetes/namespaced/kustomization.yaml
rename to install/overlays/kubernetes/own-namespace/kustomization.yaml
diff --git a/install/overlays/kubernetes/namespaced/patch-log-level.yaml 
b/install/overlays/kubernetes/own-namespace/patch-log-level.yaml
similarity index 100%
rename from install/overlays/kubernetes/namespaced/patch-log-level.yaml
rename to install/overlays/kubernetes/own-namespace/patch-log-level.yaml
diff --git a/install/overlays/kubernetes/namespaced/patch-node-selector.yaml 
b/install/overlays/kubernetes/own-namespace/patch-node-selector.yaml
similarity index 100%
rename from install/overlays/kubernetes/namespaced/patch-node-selector.yaml
rename to install/overlays/kubernetes/own-namespace/patch-node-selector.yaml
diff --git a/install/overlays/kubernetes/namespaced/patch-operator-id.yaml 
b/install/overlays/kubernetes/own-namespace/patch-operator-id.yaml
similarity index 100%
copy from install/overlays/kubernetes/namespaced/patch-operator-id.yaml
copy to install/overlays/kubernetes/own-namespace/patch-operator-id.yaml
diff --git a/install/overlays/kubernetes/namespaced/patch-ports.yaml 
b/install/overlays/kubernetes/own-namespace/patch-ports.yaml
similarity index 100%
rename from install/overlays/kubernetes/namespaced/patch-ports.yaml
rename to install/overlays/kubernetes/own-namespace/patch-ports.yaml
diff --git 
a/install/overlays/kubernetes/namespaced/patch-resource-requirements.yaml 
b/install/overlays/kubernetes/own-namespace/patch-resource-requirements.yaml
similarity index 100%
rename from 
install/overlays/kubernetes/namespaced/patch-resource-requirements.yaml
rename to 
install/overlays/kubernetes/own-namespace/patch-resource-requirements.yaml
diff --git a/install/overlays/kubernetes/namespaced/patch-toleration.yaml 
b/install/overlays/kubernetes/own-namespace/patch-toleration.yaml
similarity index 100%
copy from install/overlays/kubernetes/namespaced/patch-toleration.yaml
copy to install/overlays/kubernetes/own-namespace/patch-toleration.yaml
diff --git a/install/overlays/kubernetes/namespaced/patch-operator-id.yaml 
b/install/overlays/kubernetes/single-namespace/kustomization.yaml
similarity index 89%
copy from install/overlays/kubernetes/namespaced/patch-operator-id.yaml
copy to install/overlays/kubernetes/single-namespace/kustomization.yaml
index 25c150420..bd6a00ee6 100644
--- a/install/overlays/kubernetes/namespaced/patch-operator-id.yaml
+++ b/install/overlays/kubernetes/single-namespace/kustomization.yaml
@@ -14,7 +14,9 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 # ---------------------------------------------------------------------------
+apiVersion: kustomize.config.k8s.io/v1beta1
+kind: Kustomization
 
-- op: add
-  path: /spec/template/spec/containers/0/env/2/value
-  value: camel-k
+resources:
+- operator
+- tenant-a-ns-rbac
diff --git a/pkg/resources/config/manager/kustomization.yaml 
b/install/overlays/kubernetes/single-namespace/operator/kustomization.yaml
similarity index 86%
copy from pkg/resources/config/manager/kustomization.yaml
copy to install/overlays/kubernetes/single-namespace/operator/kustomization.yaml
index a4543333a..62fa2c867 100644
--- a/pkg/resources/config/manager/kustomization.yaml
+++ b/install/overlays/kubernetes/single-namespace/operator/kustomization.yaml
@@ -14,17 +14,17 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 # ---------------------------------------------------------------------------
-
 apiVersion: kustomize.config.k8s.io/v1beta1
 kind: Kustomization
 
+namespace: operators
+nameSuffix: -tenant-a
+
 resources:
-- operator-deployment.yaml
-- operator-service-account.yaml
-- builder-service-account.yaml
+- ../../own-namespace
 
 patches:
-  - path: add-registry-envvars.yaml
-    target:
+ - target:
       kind: Deployment
-      name: camel-k-operator
+   path: remove-watch-ns.yaml
+ - path: patch-envvars.yaml
diff --git a/install/overlays/kubernetes/namespaced/patch-toleration.yaml 
b/install/overlays/kubernetes/single-namespace/operator/patch-envvars.yaml
similarity index 78%
rename from install/overlays/kubernetes/namespaced/patch-toleration.yaml
rename to 
install/overlays/kubernetes/single-namespace/operator/patch-envvars.yaml
index 71b7f1228..b5ceec673 100644
--- a/install/overlays/kubernetes/namespaced/patch-toleration.yaml
+++ b/install/overlays/kubernetes/single-namespace/operator/patch-envvars.yaml
@@ -22,11 +22,14 @@ metadata:
 spec:
   template:
     spec:
-      tolerations:
-#
-# Add tolerations for configuring the deployment
-# eg.
-#        - key: "key1"
-#          operator: "Equal"
-#          value: "value1"
-#          effect: "NoSchedule"
+      containers:
+        - name: camel-k-operator
+          env:
+            - name: BUILDER_SA
+              value: camel-k-builder-tenant-a
+
+            - name: WATCH_NAMESPACE
+              value: tenant-a
+
+            - name: OPERATOR_ID
+              value: camel-k-tenant-a
\ No newline at end of file
diff --git a/install/overlays/kubernetes/namespaced/patch-operator-id.yaml 
b/install/overlays/kubernetes/single-namespace/operator/remove-watch-ns.yaml
similarity index 92%
rename from install/overlays/kubernetes/namespaced/patch-operator-id.yaml
rename to 
install/overlays/kubernetes/single-namespace/operator/remove-watch-ns.yaml
index 25c150420..6a9dc9b98 100644
--- a/install/overlays/kubernetes/namespaced/patch-operator-id.yaml
+++ b/install/overlays/kubernetes/single-namespace/operator/remove-watch-ns.yaml
@@ -15,6 +15,5 @@
 # limitations under the License.
 # ---------------------------------------------------------------------------
 
-- op: add
-  path: /spec/template/spec/containers/0/env/2/value
-  value: camel-k
+- op: remove
+  path: /spec/template/spec/containers/0/env/0
\ No newline at end of file
diff --git a/pkg/resources/config/manager/kustomization.yaml 
b/install/overlays/kubernetes/single-namespace/tenant-a-ns-rbac/kustomization.yaml
similarity index 84%
copy from pkg/resources/config/manager/kustomization.yaml
copy to 
install/overlays/kubernetes/single-namespace/tenant-a-ns-rbac/kustomization.yaml
index a4543333a..0b1aafa9b 100644
--- a/pkg/resources/config/manager/kustomization.yaml
+++ 
b/install/overlays/kubernetes/single-namespace/tenant-a-ns-rbac/kustomization.yaml
@@ -14,17 +14,16 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 # ---------------------------------------------------------------------------
-
 apiVersion: kustomize.config.k8s.io/v1beta1
 kind: Kustomization
 
+namespace: tenant-a
+nameSuffix: -tenant-a
+
 resources:
-- operator-deployment.yaml
-- operator-service-account.yaml
-- builder-service-account.yaml
+- ../../../../base/config/rbac/namespaced
 
 patches:
-  - path: add-registry-envvars.yaml
-    target:
-      kind: Deployment
-      name: camel-k-operator
+ - target:
+      kind: RoleBinding
+   path: patch-rolebinding-subjects.yaml
\ No newline at end of file
diff --git 
a/install/overlays/kubernetes/namespaced/patch-install-default-kamelets.yaml 
b/install/overlays/kubernetes/single-namespace/tenant-a-ns-rbac/patch-rolebinding-subjects.yaml
similarity index 88%
rename from 
install/overlays/kubernetes/namespaced/patch-install-default-kamelets.yaml
rename to 
install/overlays/kubernetes/single-namespace/tenant-a-ns-rbac/patch-rolebinding-subjects.yaml
index fa3012076..d60bd2220 100644
--- a/install/overlays/kubernetes/namespaced/patch-install-default-kamelets.yaml
+++ 
b/install/overlays/kubernetes/single-namespace/tenant-a-ns-rbac/patch-rolebinding-subjects.yaml
@@ -16,7 +16,8 @@
 # ---------------------------------------------------------------------------
 
 - op: add
-  path: /spec/template/spec/containers/0/env/-
-  value:
-    name: KAMEL_INSTALL_DEFAULT_KAMELETS
-    value: "false"
+  path: /subjects/0/namespace
+  value: "operators"
+- op: replace
+  path: /subjects/0/name
+  value: camel-k-operator-tenant-a
\ No newline at end of file
diff --git a/pkg/apis/camel/v1/common_types.go 
b/pkg/apis/camel/v1/common_types.go
index 1a7cf947a..826a6099b 100644
--- a/pkg/apis/camel/v1/common_types.go
+++ b/pkg/apis/camel/v1/common_types.go
@@ -29,6 +29,8 @@ const (
        // Deprecated: use .spec.traits instead.
        TraitAnnotationPrefix = "trait.camel.apache.org/"
        // OperatorIDAnnotation operator id annotation label.
+       //
+       // Deprecated: will be removed in the future.
        OperatorIDAnnotation = "camel.apache.org/operator.id"
        // PlatformSelectorAnnotation platform id annotation label.
        PlatformSelectorAnnotation = "camel.apache.org/platform.id"
diff --git a/pkg/cmd/bind.go b/pkg/cmd/bind.go
index e5b991196..1e775567d 100644
--- a/pkg/cmd/bind.go
+++ b/pkg/cmd/bind.go
@@ -62,7 +62,7 @@ func newCmdBind(rootCmdOptions *RootCmdOptions) 
(*cobra.Command, *bindCmdOptions
        cmd.Flags().Bool("skip-checks", false, "Do not verify the binding for 
compliance with Kamelets and other Kubernetes resources")
        cmd.Flags().StringArray("step", nil, `Add binding steps as Kubernetes 
resources. Endpoints are expected in the format 
"[[apigroup/]version:]kind:[namespace/]name", plain Camel URIs or Kamelet 
name.`)
        cmd.Flags().StringArrayP("trait", "t", nil, `Add a trait to the 
corresponding Integration.`)
-       cmd.Flags().StringP("operator-id", "x", "camel-k", "Operator id 
selected to manage this Pipe.")
+       cmd.Flags().StringP("operator-id", "x", "", "Deprecated. Operator id 
selected to manage this Pipe.")
        cmd.Flags().StringArray("annotation", nil, "Add an annotation to the 
Pipe. E.g. \"--annotation my.company=hello\"")
        cmd.Flags().String("service-account", "", "The SA to use to run this 
binding")
        cmd.Flags().StringArrayP("dependency", "d", nil, `A dependency that 
should be included, e.g., "camel:mail" for a Camel component, 
"mvn:org.my:app:1.0" for a Maven dependency`)
@@ -120,10 +120,6 @@ func (o *bindCmdOptions) validate(cmd *cobra.Command, args 
[]string) error {
                return errors.New("source or sink arguments are missing")
        }
 
-       if o.OperatorID == "" {
-               return errors.New("cannot use empty operator id")
-       }
-
        for _, annotation := range o.Annotations {
                parts := strings.SplitN(annotation, "=", 2)
                if len(parts) != 2 {
@@ -245,12 +241,20 @@ func (o *bindCmdOptions) run(cmd *cobra.Command, args 
[]string) error {
        }
 
        // --operator-id={id} is a syntax sugar for '--annotation 
camel.apache.org/operator.id={id}'
-       pipe.SetOperatorID(strings.TrimSpace(o.OperatorID))
+       if o.OperatorID != "" {
+               pipe.SetOperatorID(strings.TrimSpace(o.OperatorID))
+       }
 
-       for _, annotation := range o.Annotations {
-               parts := strings.SplitN(annotation, "=", 2)
-               if len(parts) == 2 {
-                       pipe.Annotations[parts[0]] = parts[1]
+       if o.Annotations != nil {
+               if pipe.Annotations == nil {
+                       pipe.Annotations = map[string]string{}
+               }
+
+               for _, annotation := range o.Annotations {
+                       parts := strings.SplitN(annotation, "=", 2)
+                       if len(parts) == 2 {
+                               pipe.Annotations[parts[0]] = parts[1]
+                       }
                }
        }
 
diff --git a/pkg/cmd/bind_test.go b/pkg/cmd/bind_test.go
index 1e0396e9d..a6627d666 100644
--- a/pkg/cmd/bind_test.go
+++ b/pkg/cmd/bind_test.go
@@ -55,7 +55,7 @@ func TestBindOutputJSON(t *testing.T) {
        assert.Equal(t, "json", buildCmdOptions.OutputFormat)
 
        require.NoError(t, err)
-       assert.Equal(t, 
`{"kind":"Pipe","apiVersion":"camel.apache.org/v1","metadata":{"name":"my-to-my","annotations":{"camel.apache.org/operator.id":"camel-k"}},"spec":{"source":{"uri":"my:src"},"sink":{"uri":"my:dst"}},"status":{}}`,
 output)
+       assert.Equal(t, 
`{"kind":"Pipe","apiVersion":"camel.apache.org/v1","metadata":{"name":"my-to-my"},"spec":{"source":{"uri":"my:src"},"sink":{"uri":"my:dst"}},"status":{}}`,
 output)
 }
 
 func TestBindOutputYAML(t *testing.T) {
@@ -67,8 +67,6 @@ func TestBindOutputYAML(t *testing.T) {
        assert.Equal(t, `apiVersion: camel.apache.org/v1
 kind: Pipe
 metadata:
-  annotations:
-    camel.apache.org/operator.id: camel-k
   name: my-to-my
 spec:
   sink:
@@ -97,8 +95,6 @@ func TestBindErrorHandlerDLCKamelet(t *testing.T) {
        assert.Equal(t, `apiVersion: camel.apache.org/v1
 kind: Pipe
 metadata:
-  annotations:
-    camel.apache.org/operator.id: camel-k
   name: my-to-my
 spec:
   errorHandler:
@@ -128,8 +124,6 @@ func TestBindErrorHandlerNone(t *testing.T) {
        assert.Equal(t, `apiVersion: camel.apache.org/v1
 kind: Pipe
 metadata:
-  annotations:
-    camel.apache.org/operator.id: camel-k
   name: my-to-my
 spec:
   errorHandler:
@@ -152,8 +146,6 @@ func TestBindErrorHandlerLog(t *testing.T) {
        assert.Equal(t, `apiVersion: camel.apache.org/v1
 kind: Pipe
 metadata:
-  annotations:
-    camel.apache.org/operator.id: camel-k
   name: my-to-my
 spec:
   errorHandler:
@@ -176,8 +168,6 @@ func TestBindTraits(t *testing.T) {
        assert.Equal(t, `apiVersion: camel.apache.org/v1
 kind: Pipe
 metadata:
-  annotations:
-    camel.apache.org/operator.id: camel-k
   name: my-to-my
 spec:
   sink:
@@ -202,8 +192,6 @@ func TestBindTraitsArray(t *testing.T) {
        assert.Equal(t, `apiVersion: camel.apache.org/v1
 kind: Pipe
 metadata:
-  annotations:
-    camel.apache.org/operator.id: camel-k
   name: my-to-my
 spec:
   sink:
@@ -231,8 +219,6 @@ func TestBindSteps(t *testing.T) {
        assert.Equal(t, `apiVersion: camel.apache.org/v1
 kind: Pipe
 metadata:
-  annotations:
-    camel.apache.org/operator.id: camel-k
   name: my-to-my
 spec:
   sink:
@@ -283,8 +269,6 @@ func TestBindOutputWithDependencies(t *testing.T) {
        assert.Equal(t, `apiVersion: camel.apache.org/v1
 kind: Pipe
 metadata:
-  annotations:
-    camel.apache.org/operator.id: camel-k
   name: my-to-my
 spec:
   dependencies:
diff --git a/pkg/cmd/operator/operator.go b/pkg/cmd/operator/operator.go
index 9255c727c..58d11ebbc 100644
--- a/pkg/cmd/operator/operator.go
+++ b/pkg/cmd/operator/operator.go
@@ -19,7 +19,6 @@ package operator
 
 import (
        "context"
-       "errors"
        "flag"
        "fmt"
        "os"
@@ -38,7 +37,6 @@ import (
        appsv1 "k8s.io/api/apps/v1"
        batchv1 "k8s.io/api/batch/v1"
        corev1 "k8s.io/api/core/v1"
-       k8serrors "k8s.io/apimachinery/pkg/api/errors"
        "k8s.io/apimachinery/pkg/labels"
        "k8s.io/apimachinery/pkg/selection"
        "k8s.io/client-go/tools/leaderelection/resourcelock"
@@ -158,8 +156,7 @@ func Run(healthPort, monitoringPort int32, leaderElection 
bool, leaderElectionID
        }
 
        // Set the operator container image if it runs in-container
-       platform.OperatorImage, err = getOperatorImage(ctx, bootstrapClient)
-       exitOnError(err, "cannot get operator container image")
+       platform.OperatorImage = getOperatorImage()
 
        if !leaderElection {
                log.Info("Leader election is disabled!")
@@ -173,11 +170,15 @@ func Run(healthPort, monitoringPort int32, leaderElection 
bool, leaderElectionID
                Label: labelsSelector,
        }
 
+       cacheConfigs := getNamespacesSelector(operatorNamespace, watchNamespace)
        if !platform.IsCurrentOperatorGlobal() {
+               log.Infof("This operator is configured to watch only %s 
namespace(s)", watchNamespace)
                selector = cache.ByObject{
                        Label:      labelsSelector,
-                       Namespaces: getNamespacesSelector(operatorNamespace, 
watchNamespace),
+                       Namespaces: cacheConfigs,
                }
+       } else {
+               log.Info("This operator is global and will watch all 
namespaces!")
        }
 
        selectors := map[ctrl.Object]cache.ByObject{
@@ -186,11 +187,13 @@ func Run(healthPort, monitoringPort int32, leaderElection 
bool, leaderElectionID
                &batchv1.Job{}:       selector,
        }
 
-       if ok, err := kubernetes.IsAPIResourceInstalled(bootstrapClient, 
servingv1.SchemeGroupVersion.String(), 
reflect.TypeFor[servingv1.Service]().Name()); ok && err == nil {
+       if ok, err := kubernetes.IsAPIResourceInstalled(bootstrapClient, 
servingv1.SchemeGroupVersion.String(),
+               reflect.TypeFor[servingv1.Service]().Name()); ok && err == nil {
                selectors[&servingv1.Service{}] = selector
        }
 
-       if ok, err := kubernetes.IsAPIResourceInstalled(bootstrapClient, 
batchv1.SchemeGroupVersion.String(), 
reflect.TypeFor[batchv1.CronJob]().Name()); ok && err == nil {
+       if ok, err := kubernetes.IsAPIResourceInstalled(bootstrapClient, 
batchv1.SchemeGroupVersion.String(),
+               reflect.TypeFor[batchv1.CronJob]().Name()); ok && err == nil {
                selectors[&batchv1.CronJob{}] = selector
        }
 
@@ -199,7 +202,7 @@ func Run(healthPort, monitoringPort int32, leaderElection 
bool, leaderElectionID
        }
 
        if !platform.IsCurrentOperatorGlobal() {
-               options.DefaultNamespaces = 
getNamespacesSelector(operatorNamespace, watchNamespace)
+               options.DefaultNamespaces = cacheConfigs
        }
 
        mgr, err := manager.New(cfg, manager.Options{
@@ -224,7 +227,7 @@ func Run(healthPort, monitoringPort int32, leaderElection 
bool, leaderElectionID
        log.Info("Installing operator resources")
        installCtx, installCancel := context.WithTimeout(ctx, 1*time.Minute)
        defer installCancel()
-       install.OperatorStartupOptionalTools(installCtx, bootstrapClient, 
watchNamespace, operatorNamespace, log)
+       install.OperatorStartupOptionalTools(installCtx, bootstrapClient, log)
 
        synthEnvVal, synth := os.LookupEnv("CAMEL_K_SYNTHETIC_INTEGRATIONS")
        if synth && synthEnvVal == "true" {
@@ -238,10 +241,19 @@ func Run(healthPort, monitoringPort int32, leaderElection 
bool, leaderElectionID
 
 func getNamespacesSelector(operatorNamespace string, watchNamespace string) 
map[string]cache.Config {
        namespacesSelector := map[string]cache.Config{
+               // The same operator namespace is needed while the operator 
stores
+               // Builds and IntegrationKits and CamelCatalogs in its namespace
+               // TODO: remove this when we either remove those CR or we move 
them in the tenant namespace only
                operatorNamespace: {},
        }
-       if operatorNamespace != watchNamespace {
-               namespacesSelector[watchNamespace] = cache.Config{}
+
+       for ns := range strings.SplitSeq(watchNamespace, ",") {
+               ns = strings.TrimSpace(ns)
+               if ns == "" || ns == operatorNamespace {
+                       continue
+               }
+
+               namespacesSelector[ns] = cache.Config{}
        }
 
        return namespacesSelector
@@ -258,24 +270,8 @@ func getWatchNamespace() (string, error) {
 }
 
 // getOperatorImage returns the image currently used by the running operator 
if present (when running out of cluster, it may be absent).
-func getOperatorImage(ctx context.Context, c ctrl.Reader) (string, error) {
-       ns := platform.GetOperatorNamespace()
-       name := platform.GetOperatorPodName()
-       if ns == "" || name == "" {
-               return "", nil
-       }
-
-       pod := corev1.Pod{}
-       if err := c.Get(ctx, ctrl.ObjectKey{Namespace: ns, Name: name}, &pod); 
err != nil && k8serrors.IsNotFound(err) {
-               return "", nil
-       } else if err != nil {
-               return "", err
-       }
-       if len(pod.Spec.Containers) == 0 {
-               return "", errors.New("no containers found in operator pod")
-       }
-
-       return pod.Spec.Containers[0].Image, nil
+func getOperatorImage() string {
+       return os.Getenv("CONTAINER_IMAGE")
 }
 
 func exitOnError(err error, msg string) {
diff --git a/pkg/cmd/operator/operator_test.go 
b/pkg/cmd/operator/operator_test.go
new file mode 100644
index 000000000..00eac0784
--- /dev/null
+++ b/pkg/cmd/operator/operator_test.go
@@ -0,0 +1,150 @@
+/*
+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 operator
+
+import (
+       "testing"
+
+       "github.com/apache/camel-k/v2/pkg/platform"
+       "github.com/stretchr/testify/assert"
+       "github.com/stretchr/testify/require"
+       "sigs.k8s.io/controller-runtime/pkg/cache"
+)
+
+func TestGetNamespacesSelector(t *testing.T) {
+       tests := []struct {
+               name              string
+               operatorNamespace string
+               watchNamespace    string
+               expected          map[string]cache.Config
+       }{
+               {
+                       name:              "same namespace",
+                       operatorNamespace: "operator",
+                       watchNamespace:    "operator",
+                       expected: map[string]cache.Config{
+                               "operator": {},
+                       },
+               },
+               {
+                       name:              "different namespace",
+                       operatorNamespace: "operator",
+                       watchNamespace:    "tenant",
+                       expected: map[string]cache.Config{
+                               "operator": {},
+                               "tenant":   {},
+                       },
+               },
+               {
+                       name:              "csv namespaces",
+                       operatorNamespace: "operator",
+                       watchNamespace:    "tenant-a,tenant-b,tenant-c",
+                       expected: map[string]cache.Config{
+                               "operator": {},
+                               "tenant-a": {},
+                               "tenant-b": {},
+                               "tenant-c": {},
+                       },
+               },
+               {
+                       name:              "trim spaces",
+                       operatorNamespace: "operator",
+                       watchNamespace:    "tenant-a, tenant-b , tenant-c",
+                       expected: map[string]cache.Config{
+                               "operator": {},
+                               "tenant-a": {},
+                               "tenant-b": {},
+                               "tenant-c": {},
+                       },
+               },
+               {
+                       name:              "ignore duplicates",
+                       operatorNamespace: "operator",
+                       watchNamespace:    "tenant-a,tenant-a,tenant-b",
+                       expected: map[string]cache.Config{
+                               "operator": {},
+                               "tenant-a": {},
+                               "tenant-b": {},
+                       },
+               },
+               {
+                       name:              "ignore empty entries",
+                       operatorNamespace: "operator",
+                       watchNamespace:    "tenant-a,,tenant-b,",
+                       expected: map[string]cache.Config{
+                               "operator": {},
+                               "tenant-a": {},
+                               "tenant-b": {},
+                       },
+               },
+               {
+                       name:              "ignore operator namespace in csv",
+                       operatorNamespace: "operator",
+                       watchNamespace:    "tenant-a,operator,tenant-b",
+                       expected: map[string]cache.Config{
+                               "operator": {},
+                               "tenant-a": {},
+                               "tenant-b": {},
+                       },
+               },
+       }
+
+       for _, tt := range tests {
+               t.Run(tt.name, func(t *testing.T) {
+                       actual := getNamespacesSelector(tt.operatorNamespace, 
tt.watchNamespace)
+                       assert.Equal(t, tt.expected, actual)
+               })
+       }
+}
+
+func TestGetWatchNamespace(t *testing.T) {
+       t.Run("env variable set", func(t *testing.T) {
+               t.Setenv(platform.OperatorWatchNamespaceEnvVariable, 
"tenant-a,tenant-b")
+
+               ns, err := getWatchNamespace()
+
+               require.NoError(t, err)
+               assert.Equal(t, "tenant-a,tenant-b", ns)
+       })
+
+       t.Run("env variable not set", func(t *testing.T) {
+               ns, err := getWatchNamespace()
+
+               require.Error(t, err)
+               assert.Empty(t, ns)
+               assert.Contains(t, err.Error(), 
platform.OperatorWatchNamespaceEnvVariable)
+       })
+}
+
+func TestGetOperatorImage(t *testing.T) {
+       t.Run("env variable set", func(t *testing.T) {
+               t.Setenv("CONTAINER_IMAGE", "quay.io/example/operator:latest")
+
+               image := getOperatorImage()
+
+               assert.Equal(t, "quay.io/example/operator:latest", image)
+       })
+
+       t.Run("env variable not set", func(t *testing.T) {
+               t.Setenv("CONTAINER_IMAGE", "")
+
+               image := getOperatorImage()
+
+               assert.Empty(t, image)
+       })
+}
diff --git a/pkg/cmd/promote_test.go b/pkg/cmd/promote_test.go
index 7a128395e..522ec5ab1 100644
--- a/pkg/cmd/promote_test.go
+++ b/pkg/cmd/promote_test.go
@@ -145,8 +145,7 @@ func createTestCamelCatalog(ns string, runtimeProvider 
v1.RuntimeProvider, versi
 func TestIntegrationWithMetadataDryRun(t *testing.T) {
        defaultIntegration, defaultKit := nominalIntegration("my-it-test")
        defaultIntegration.Annotations = map[string]string{
-               "camel.apache.org/operator.id": "camel-k",
-               "my-annotation":                "my-value",
+               "my-annotation": "my-value",
        }
        defaultIntegration.Labels = map[string]string{
                "my-label": "my-value",
@@ -182,8 +181,7 @@ status: {}
 func TestPipeWithMetadataDryRun(t *testing.T) {
        defaultKB := nominalPipe("my-pipe-test")
        defaultKB.Annotations = map[string]string{
-               "camel.apache.org/operator.id": "camel-k",
-               "my-annotation":                "my-value",
+               "my-annotation": "my-value",
        }
        defaultKB.Labels = map[string]string{
                "my-label": "my-value",
@@ -334,8 +332,7 @@ status: {}
 func TestPipeWithSavedTraitsDryRun(t *testing.T) {
        defaultKB := nominalPipe("my-pipe-test")
        defaultKB.Annotations = map[string]string{
-               "camel.apache.org/operator.id": "camel-k",
-               "my-annotation":                "my-value",
+               "my-annotation": "my-value",
        }
        defaultKB.Labels = map[string]string{
                "my-label": "my-value",
@@ -614,8 +611,7 @@ resources:
 func TestPipeGitOps(t *testing.T) {
        defaultPipe := nominalPipe("my-pipe-test")
        defaultPipe.Annotations = map[string]string{
-               "camel.apache.org/operator.id": "camel-k",
-               "my-annotation":                "my-value",
+               "my-annotation": "my-value",
        }
        defaultPipe.Labels = map[string]string{
                "my-label": "my-value",
diff --git a/pkg/cmd/run.go b/pkg/cmd/run.go
index 87ef58382..4a008edf4 100644
--- a/pkg/cmd/run.go
+++ b/pkg/cmd/run.go
@@ -97,7 +97,7 @@ func newCmdRun(rootCmdOptions *RootCmdOptions) 
(*cobra.Command, *runCmdOptions)
        cmd.Flags().Bool("sync", false, "[Deprecated] Synchronize the local 
source file with the cluster, republishing at each change")
        cmd.Flags().Bool("dev", false, "[Deprecated] Enable Dev mode 
(equivalent to \"-w --logs --sync\")")
        cmd.Flags().Bool("use-flows", true, "Write yaml sources as Flow objects 
in the integration custom resource")
-       cmd.Flags().StringP("operator-id", "x", "camel-k", "Operator id 
selected to manage this integration.")
+       cmd.Flags().StringP("operator-id", "x", "", "Operator id selected to 
manage this integration.")
        cmd.Flags().String("profile", "", "Trait profile used for deployment")
        cmd.Flags().String("integration-profile", "", "Integration profile used 
for deployment")
        cmd.Flags().StringArrayP("trait", "t", nil, "Configure a trait. E.g. 
\"-t service.enabled=false\"")
@@ -246,10 +246,6 @@ func (o *runCmdOptions) validateArgs(cmd *cobra.Command, 
args []string) error {
 }
 
 func (o *runCmdOptions) validate(cmd *cobra.Command) error {
-       if o.OperatorID == "" {
-               return errors.New("cannot use empty operator id")
-       }
-
        for _, volume := range o.Volumes {
                volumeConfig := strings.Split(volume, ":")
                if len(volumeConfig) != 2 || 
len(strings.TrimSpace(volumeConfig[0])) == 0 || 
len(strings.TrimSpace(volumeConfig[1])) == 0 {
@@ -719,7 +715,9 @@ func (o *runCmdOptions) applyAnnotations(it 
*v1.Integration) {
        }
 
        // --operator-id={id} is a syntax sugar for '--annotation 
camel.apache.org/operator.id={id}'
-       it.SetOperatorID(strings.TrimSpace(o.OperatorID))
+       if o.OperatorID != "" {
+               it.SetOperatorID(strings.TrimSpace(o.OperatorID))
+       }
 
        // --integration-profile={id} is a syntax sugar for '--annotation 
camel.apache.org/integration-profile.id={id}'
        if o.IntegrationProfile != "" {
diff --git a/pkg/cmd/run_test.go b/pkg/cmd/run_test.go
index 39dc64c5d..73873a7de 100644
--- a/pkg/cmd/run_test.go
+++ b/pkg/cmd/run_test.go
@@ -578,8 +578,6 @@ func TestOutputYaml(t *testing.T) {
        assert.Equal(t, fmt.Sprintf(`apiVersion: camel.apache.org/v1
 kind: Integration
 metadata:
-  annotations:
-    camel.apache.org/operator.id: camel-k
   name: %s
 spec:
   sources:
@@ -612,8 +610,6 @@ func TestTrait(t *testing.T) {
        assert.Equal(t, fmt.Sprintf(`apiVersion: camel.apache.org/v1
 kind: Integration
 metadata:
-  annotations:
-    camel.apache.org/operator.id: camel-k
   name: %s
 spec:
   sources:
@@ -919,8 +915,6 @@ func TestSelfManagedBuildIntegration(t *testing.T) {
        assert.Equal(t, `apiVersion: camel.apache.org/v1
 kind: Integration
 metadata:
-  annotations:
-    camel.apache.org/operator.id: camel-k
   name: my-app-v1
 spec:
   traits:
@@ -942,8 +936,6 @@ func TestGitRepoIntegration(t *testing.T) {
        assert.Equal(t, `apiVersion: camel.apache.org/v1
 kind: Integration
 metadata:
-  annotations:
-    camel.apache.org/operator.id: camel-k
   name: my-it
 spec:
   git:
@@ -962,8 +954,6 @@ func TestGitTagIntegration(t *testing.T) {
        assert.Equal(t, `apiVersion: camel.apache.org/v1
 kind: Integration
 metadata:
-  annotations:
-    camel.apache.org/operator.id: camel-k
   name: my-it
 spec:
   git:
@@ -983,8 +973,6 @@ func TestGitBranchIntegration(t *testing.T) {
        assert.Equal(t, `apiVersion: camel.apache.org/v1
 kind: Integration
 metadata:
-  annotations:
-    camel.apache.org/operator.id: camel-k
   name: my-it
 spec:
   git:
@@ -1004,8 +992,6 @@ func TestGitCommitIntegration(t *testing.T) {
        assert.Equal(t, `apiVersion: camel.apache.org/v1
 kind: Integration
 metadata:
-  annotations:
-    camel.apache.org/operator.id: camel-k
   name: my-it
 spec:
   git:
diff --git a/pkg/controller/integration/build.go 
b/pkg/controller/integration/build.go
index 64cf10133..6c57004e9 100644
--- a/pkg/controller/integration/build.go
+++ b/pkg/controller/integration/build.go
@@ -100,6 +100,7 @@ func (action *buildAction) createBuild(ctx context.Context, 
it *v1.Integration)
 
        operatorID := defaults.OperatorID()
        if operatorID != "" {
+               //nolint:staticcheck
                annotations[v1.OperatorIDAnnotation] = operatorID
        }
 
diff --git a/pkg/controller/integration/integration_controller.go 
b/pkg/controller/integration/integration_controller.go
index 612d30675..10a8c7c35 100644
--- a/pkg/controller/integration/integration_controller.go
+++ b/pkg/controller/integration/integration_controller.go
@@ -149,12 +149,7 @@ func integrationKitEnqueueRequestsFromMapFunc(ctx 
context.Context, c client.Clie
        }
 
        list := &v1.IntegrationList{}
-       // Do global search in case of global operator (it may be using a 
global platform)
-       var opts []ctrl.ListOption
-       if !platform.IsCurrentOperatorGlobal() {
-               opts = append(opts, ctrl.InNamespace(kit.Namespace))
-       }
-       if err := c.List(ctx, list, opts...); err != nil {
+       if err := c.List(ctx, list); err != nil {
                log.Error(err, "Failed to retrieve integration list")
 
                return requests
@@ -452,7 +447,7 @@ func watchKnativeResources(ctx context.Context, c 
client.Client, b *builder.Buil
                b.Owns(&servingv1.Service{}, 
builder.WithPredicates(StatusChangedPredicate{}))
        } else {
                log.Info("KnativeService resources installed in the cluster. 
However Camel K operator has not the required RBAC privileges. " +
-                       "You can't use Knative features.Make sure to apply the 
required RBAC privileges and restart the Camel K Operator Pod to be able " +
+                       "You can't use Knative features. Make sure to apply the 
required RBAC privileges and restart the Camel K Operator Pod to be able " +
                        "to watch for Camel K managed Knative Services.")
        }
 
diff --git a/pkg/controller/integrationkit/build.go 
b/pkg/controller/integrationkit/build.go
index b7f41c83e..ab830f8a9 100644
--- a/pkg/controller/integrationkit/build.go
+++ b/pkg/controller/integrationkit/build.go
@@ -128,6 +128,7 @@ func (action *buildAction) createBuild(ctx context.Context, 
kit *v1.IntegrationK
 
        operatorID := defaults.OperatorID()
        if operatorID != "" {
+               //nolint:staticcheck
                annotations[v1.OperatorIDAnnotation] = operatorID
        }
 
diff --git a/pkg/controller/integrationkit/integrationkit_controller.go 
b/pkg/controller/integrationkit/integrationkit_controller.go
index 5fb5f2fd3..12c9c171d 100644
--- a/pkg/controller/integrationkit/integrationkit_controller.go
+++ b/pkg/controller/integrationkit/integrationkit_controller.go
@@ -153,6 +153,7 @@ func add(_ context.Context, mgr manager.Manager, r 
reconcile.Reconciler) error {
 
                                                        continue
                                                }
+                                               //nolint:staticcheck
                                                if v, ok := 
kit.Annotations[v1.OperatorIDAnnotation]; ok && v != itp.Name {
                                                        // kit waiting for 
another platform to become ready - skip here
                                                        log.Debugf("Integration 
kit %s is waiting for another integration platform '%s' - skip it now", 
kit.Name, v)
diff --git a/pkg/install/common.go b/pkg/install/common.go
deleted file mode 100644
index a2f620be4..000000000
--- a/pkg/install/common.go
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
-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 install
-
-import (
-       networking "k8s.io/api/networking/v1"
-       rbacv1 "k8s.io/api/rbac/v1"
-
-       ctrl "sigs.k8s.io/controller-runtime/pkg/client"
-)
-
-// ResourceCustomizer can be used to inject code that changes the objects 
before they are created.
-type ResourceCustomizer func(object ctrl.Object) ctrl.Object
-
-// IdentityResourceCustomizer is a ResourceCustomizer that does nothing.
-var IdentityResourceCustomizer = func(object ctrl.Object) ctrl.Object {
-       return object
-}
-
-var RemoveIngressRoleCustomizer = func(object ctrl.Object) ctrl.Object {
-       if role, ok := object.(*rbacv1.Role); ok && role.Name == 
"camel-k-operator" {
-       rules:
-               for i, rule := range role.Rules {
-                       for _, group := range rule.APIGroups {
-                               if group == networking.GroupName {
-                                       role.Rules = append(role.Rules[:i], 
role.Rules[i+1:]...)
-
-                                       break rules
-                               }
-                       }
-               }
-       }
-
-       return object
-}
diff --git a/pkg/install/optional.go b/pkg/install/optional.go
index 11b23d97c..764c5ece3 100644
--- a/pkg/install/optional.go
+++ b/pkg/install/optional.go
@@ -27,7 +27,7 @@ import (
 )
 
 // OperatorStartupOptionalTools tries to install optional tools at operator 
startup and warns if something goes wrong.
-func OperatorStartupOptionalTools(ctx context.Context, c client.Client, 
namespace string, operatorNamespace string, log logutil.Logger) {
+func OperatorStartupOptionalTools(ctx context.Context, c client.Client, log 
logutil.Logger) {
        // Try to register the OpenShift CLI Download link if possible
        if err := OpenShiftConsoleDownloadLink(ctx, c); err != nil {
                log.Info("Cannot install OpenShift CLI download link: 
skipping.")
diff --git a/pkg/platform/defaults.go b/pkg/platform/defaults.go
index 28175c98a..22538128e 100644
--- a/pkg/platform/defaults.go
+++ b/pkg/platform/defaults.go
@@ -19,6 +19,7 @@ package platform
 
 import (
        "context"
+       "os"
        "runtime"
        "strings"
        "time"
@@ -39,7 +40,6 @@ import (
 
 const (
        DefaultPlatformName                    = "camel-k"
-       BuilderServiceAccount                  = "camel-k-builder"
        DefaultBuildTimeout                    = 5 * time.Minute
        DefaultBuildStrategy                   = v1.BuildStrategyRoutine
        DefaultBuildOrderStrategy              = 
v1.BuildOrderStrategyDependencies
@@ -49,6 +49,17 @@ const (
        DefaultMaxRunningBuildsRoutineStrategy = 3
 )
 
+var BuilderServiceAccount = getBuilderServiceAccount()
+
+func getBuilderServiceAccount() string {
+       bsa := os.Getenv("BUILDER_SA")
+       if bsa == "" {
+               bsa = "camel-k-builder"
+       }
+
+       return bsa
+}
+
 // ConfigureDefaults fills with default values all missing details about the 
integration platform.
 // Defaults are set in the status fields, not in the spec.
 //
diff --git a/pkg/platform/operator.go b/pkg/platform/operator.go
index ad9275a67..16aa0c863 100644
--- a/pkg/platform/operator.go
+++ b/pkg/platform/operator.go
@@ -150,20 +150,7 @@ func IsOperatorHandler(object ctrl.Object) bool {
                return true
        }
 
-       // check if we are dealing with resource that is missing a proper 
operator id annotation
-       if resourceID == "" {
-               // allow default global operator to handle legacy resources 
(missing proper operator id annotations)
-               if operatorID == DefaultPlatformName {
-                       return true
-               }
-
-               // allow local operators to handle legacy resources (missing 
proper operator id annotations)
-               if !IsCurrentOperatorGlobal() {
-                       return true
-               }
-       }
-
-       return false
+       return resourceID == ""
 }
 
 // IsOperatorHandlerConsideringLock uses normal IsOperatorHandler checks and 
adds additional check for legacy resources
diff --git a/pkg/resources/config/manager/kustomization.yaml 
b/pkg/resources/config/manager/kustomization.yaml
index a4543333a..b067e2c33 100644
--- a/pkg/resources/config/manager/kustomization.yaml
+++ b/pkg/resources/config/manager/kustomization.yaml
@@ -28,3 +28,13 @@ patches:
     target:
       kind: Deployment
       name: camel-k-operator
+
+replacements:
+  - source:
+      kind: Deployment
+      fieldPath: spec.template.spec.containers.[name=camel-k-operator].image
+    targets:
+      - select:
+          kind: Deployment
+        fieldPaths:
+          - 
spec.template.spec.containers.[name=camel-k-operator].env.[name=CONTAINER_IMAGE].value
diff --git a/pkg/resources/config/manager/operator-deployment.yaml 
b/pkg/resources/config/manager/operator-deployment.yaml
index d395d64ab..1331ea35d 100644
--- a/pkg/resources/config/manager/operator-deployment.yaml
+++ b/pkg/resources/config/manager/operator-deployment.yaml
@@ -77,6 +77,12 @@ spec:
             # Note: remove the variable to disable the feature.
             - name: CAMEL_MONITOR_OPERATOR_LABEL
               value: "camel.apache.org/monitor"
+            # Used to query which is the image this operator is running
+            - name: CONTAINER_IMAGE
+              value: ""
+            # You can provide a different SA for builder Pods
+            - name: BUILDER_SA
+              value: "camel-k-builder"
           # Attempt to read bootstrap configuration from configmap or secret
           envFrom:
             - configMapRef:
diff --git a/pkg/util/gitops/gitops.go b/pkg/util/gitops/gitops.go
index 16b4f40d1..c16214216 100644
--- a/pkg/util/gitops/gitops.go
+++ b/pkg/util/gitops/gitops.go
@@ -123,6 +123,7 @@ func cloneAnnotations(ann map[string]string, operatorID 
string) map[string]strin
                if k == "kubectl.kubernetes.io/last-applied-configuration" {
                        continue
                }
+               //nolint:staticcheck
                if k == v1.OperatorIDAnnotation {
                        if operatorID != "" {
                                newMap[v1.OperatorIDAnnotation] = operatorID
@@ -132,6 +133,7 @@ func cloneAnnotations(ann map[string]string, operatorID 
string) map[string]strin
                        newMap[k] = v
                }
        }
+       //nolint:staticcheck
        if !operatorIDAnnotationSet && operatorID != "" {
                newMap[v1.OperatorIDAnnotation] = operatorID
        }
@@ -213,6 +215,7 @@ func AppendKustomizeIntegration(dstIt *v1.Integration, 
destinationDir string, ov
        baseIt := dstIt.DeepCopy()
        baseIt.Namespace = ""
        if baseIt.Annotations != nil {
+               //nolint:staticcheck
                delete(baseIt.Annotations, v1.OperatorIDAnnotation)
        }
        appFolderName := strings.ToLower(baseIt.Name)
@@ -417,6 +420,7 @@ func AppendKustomizePipe(dstPipe *v1.Pipe, destinationDir 
string, overwrite bool
        basePipe := dstPipe.DeepCopy()
        basePipe.Namespace = ""
        if basePipe.Annotations != nil {
+               //nolint:staticcheck
                delete(basePipe.Annotations, v1.OperatorIDAnnotation)
        }
        appFolderName := strings.ToLower(basePipe.Name)
diff --git a/script/Makefile b/script/Makefile
index 8753ad6bf..f1acf5ed8 100644
--- a/script/Makefile
+++ b/script/Makefile
@@ -804,7 +804,7 @@ install-k8s-global: 
KUSTOMIZE_DIR="install/overlays/kubernetes/descoped"
 install-k8s-global: clone-kustomize-dir set-operator-id set-operator-env 
install-operator
 
 install-k8s-ns: DEFAULT_NS="default"
-install-k8s-ns: KUSTOMIZE_DIR="install/overlays/kubernetes/namespaced"
+install-k8s-ns: KUSTOMIZE_DIR="install/overlays/kubernetes/own-namespace"
 install-k8s-ns: clone-kustomize-dir set-operator-id set-operator-env 
install-operator
 
 install-registry: NAMESPACE="camel-k"

Reply via email to