From 46d02314aaf4ba012da672381ed2db7f15d16d4d Mon Sep 17 00:00:00 2001 From: Pasquale Congiusti Date: Sat, 13 Jun 2026 09:15:06 +0200 Subject: [PATCH 1/2] 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 - .../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 ++++++++++ .../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 .../patch-ports.yaml | 0 .../patch-resource-requirements.yaml | 0 .../patch-toleration.yaml | 0 .../single-namespace/kustomization.yaml | 22 +++ .../operator/kustomization.yaml | 30 ++++ .../operator/patch-envvars.yaml | 35 ++++ .../operator/remove-watch-ns.yaml | 19 +++ .../tenant-a-ns-rbac/kustomization.yaml | 29 ++++ .../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_controller.go | 1 + pkg/install/common.go | 50 ------ pkg/install/optional.go | 2 +- pkg/platform/defaults.go | 13 +- pkg/platform/operator.go | 15 +- .../config/manager/kustomization.yaml | 10 ++ .../config/manager/operator-deployment.yaml | 6 + pkg/util/gitops/gitops.go | 4 + script/Makefile | 2 +- 41 files changed, 563 insertions(+), 279 deletions(-) rename e2e/install/kustomize/{setup_test.go => all_namespaces_test.go} (61%) create mode 100644 e2e/install/kustomize/own_namespace_test.go create mode 100644 e2e/install/kustomize/single_namespace_test.go rename install/overlays/kubernetes/{namespaced => own-namespace}/kustomization.yaml (100%) rename install/overlays/kubernetes/{namespaced => own-namespace}/patch-log-level.yaml (100%) rename install/overlays/kubernetes/{namespaced => own-namespace}/patch-node-selector.yaml (100%) rename install/overlays/kubernetes/{namespaced => own-namespace}/patch-operator-id.yaml (100%) rename install/overlays/kubernetes/{namespaced => own-namespace}/patch-ports.yaml (100%) rename install/overlays/kubernetes/{namespaced => own-namespace}/patch-resource-requirements.yaml (100%) rename install/overlays/kubernetes/{namespaced => own-namespace}/patch-toleration.yaml (100%) create mode 100644 install/overlays/kubernetes/single-namespace/kustomization.yaml create mode 100644 install/overlays/kubernetes/single-namespace/operator/kustomization.yaml create mode 100644 install/overlays/kubernetes/single-namespace/operator/patch-envvars.yaml create mode 100644 install/overlays/kubernetes/single-namespace/operator/remove-watch-ns.yaml create mode 100644 install/overlays/kubernetes/single-namespace/tenant-a-ns-rbac/kustomization.yaml rename install/overlays/kubernetes/{namespaced/patch-install-default-kamelets.yaml => single-namespace/tenant-a-ns-rbac/patch-rolebinding-subjects.yaml} (88%) create mode 100644 pkg/cmd/operator/operator_test.go delete mode 100644 pkg/install/common.go diff --git a/docs/modules/ROOT/pages/pipes/bind-cli.adoc b/docs/modules/ROOT/pages/pipes/bind-cli.adoc index 5133b22a61..fd48effbe4 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 67c8d14d9c..d4d871ebe8 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 4e2d714124..184603d91a 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 4aa7797bdb..2402919b67 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 e5740b9d33..9df5e5e8ff 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 0000000000..82f0a7161a --- /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 0000000000..e0229e49a3 --- /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 156f7cde9f..e61bbca34e 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% rename from install/overlays/kubernetes/namespaced/patch-operator-id.yaml rename 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% rename from install/overlays/kubernetes/namespaced/patch-toleration.yaml rename to install/overlays/kubernetes/own-namespace/patch-toleration.yaml diff --git a/install/overlays/kubernetes/single-namespace/kustomization.yaml b/install/overlays/kubernetes/single-namespace/kustomization.yaml new file mode 100644 index 0000000000..bd6a00ee63 --- /dev/null +++ b/install/overlays/kubernetes/single-namespace/kustomization.yaml @@ -0,0 +1,22 @@ +# --------------------------------------------------------------------------- +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# --------------------------------------------------------------------------- +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +resources: +- operator +- tenant-a-ns-rbac diff --git a/install/overlays/kubernetes/single-namespace/operator/kustomization.yaml b/install/overlays/kubernetes/single-namespace/operator/kustomization.yaml new file mode 100644 index 0000000000..62fa2c8679 --- /dev/null +++ b/install/overlays/kubernetes/single-namespace/operator/kustomization.yaml @@ -0,0 +1,30 @@ +# --------------------------------------------------------------------------- +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# --------------------------------------------------------------------------- +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +namespace: operators +nameSuffix: -tenant-a + +resources: +- ../../own-namespace + +patches: + - target: + kind: Deployment + path: remove-watch-ns.yaml + - path: patch-envvars.yaml diff --git a/install/overlays/kubernetes/single-namespace/operator/patch-envvars.yaml b/install/overlays/kubernetes/single-namespace/operator/patch-envvars.yaml new file mode 100644 index 0000000000..b5ceec6732 --- /dev/null +++ b/install/overlays/kubernetes/single-namespace/operator/patch-envvars.yaml @@ -0,0 +1,35 @@ +# --------------------------------------------------------------------------- +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# --------------------------------------------------------------------------- + +apiVersion: apps/v1 +kind: Deployment +metadata: + name: camel-k-operator +spec: + template: + spec: + 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/single-namespace/operator/remove-watch-ns.yaml b/install/overlays/kubernetes/single-namespace/operator/remove-watch-ns.yaml new file mode 100644 index 0000000000..6a9dc9b987 --- /dev/null +++ b/install/overlays/kubernetes/single-namespace/operator/remove-watch-ns.yaml @@ -0,0 +1,19 @@ +# --------------------------------------------------------------------------- +# 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. +# --------------------------------------------------------------------------- + +- op: remove + path: /spec/template/spec/containers/0/env/0 \ No newline at end of file diff --git a/install/overlays/kubernetes/single-namespace/tenant-a-ns-rbac/kustomization.yaml b/install/overlays/kubernetes/single-namespace/tenant-a-ns-rbac/kustomization.yaml new file mode 100644 index 0000000000..0b1aafa9b3 --- /dev/null +++ b/install/overlays/kubernetes/single-namespace/tenant-a-ns-rbac/kustomization.yaml @@ -0,0 +1,29 @@ +# --------------------------------------------------------------------------- +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# --------------------------------------------------------------------------- +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +namespace: tenant-a +nameSuffix: -tenant-a + +resources: +- ../../../../base/config/rbac/namespaced + +patches: + - 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 fa3012076f..d60bd2220f 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 1a7cf947a5..826a6099b8 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 e5b9911965..1e775567d1 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 1e0396e9dc..a6627d666f 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 9255c727c8..58d11ebbc9 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 0000000000..00eac07847 --- /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 7a128395ed..522ec5ab14 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 87ef583821..4a008edf49 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 39dc64c5d1..73873a7de0 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 64cf101330..6c57004e9b 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 612d306758..10a8c7c351 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 b7f41c83e5..ab830f8a97 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 5fb5f2fd37..12c9c171db 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 a2f620be4c..0000000000 --- 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 11b23d97c5..764c5ece30 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 28175c98a7..22538128ec 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 ad9275a670..16aa0c8631 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 a4543333a5..b067e2c339 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 d395d64abe..1331ea35d1 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 16b4f40d16..c162142161 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 8753ad6bf3..f1acf5ed84 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" From 8a9a278f6c8cbd172fc70297d2242bfa1c9b2f72 Mon Sep 17 00:00:00 2001 From: Pasquale Congiusti Date: Sat, 20 Jun 2026 09:18:54 +0200 Subject: [PATCH 2/2] chore(install): multi namespace support Closes #6616 --- .github/actions/release-nightly/action.yml | 6 +- .github/workflows/common.yml | 2 +- .github/workflows/nightly-multi.yml | 4 +- .../installation/advanced/jdk-version.adoc | 2 +- .../ROOT/pages/installation/installation.adoc | 18 +++- .../ROOT/pages/installation/upgrade.adoc | 4 +- e2e/install/kustomize/all_namespaces_test.go | 4 +- e2e/install/kustomize/multi_namespace_test.go | 98 +++++++++++++++++++ e2e/install/kustomize/own_namespace_test.go | 4 +- .../kustomize/single_namespace_test.go | 4 +- e2e/install/upgrade/upgrade_test.go | 2 +- .../kustomization.yaml | 4 +- .../patch-operator-id.yaml | 0 .../patch-watch-namespace-global.yaml | 0 .../multi-namespace/kustomization.yaml | 23 +++++ .../operator/kustomization.yaml | 30 ++++++ .../operator-role-binding-events.yaml | 30 ++++++ .../operator/operator-role-events.yaml | 36 +++++++ .../operator/patch-envvars.yaml | 22 +++++ .../tenant-a-ns-rbac/kustomization.yaml | 2 +- .../patch-rolebinding-subjects.yaml | 23 +++++ .../tenant-b-ns-rbac/kustomization.yaml | 29 ++++++ .../patch-rolebinding-subjects.yaml | 23 +++++ .../own-namespace/kustomization.yaml | 4 +- .../own-namespace/patch-log-level.yaml | 0 .../own-namespace/patch-node-selector.yaml | 0 .../own-namespace/patch-operator-id.yaml | 0 .../own-namespace/patch-ports.yaml | 0 .../patch-resource-requirements.yaml | 0 .../own-namespace/patch-toleration.yaml | 0 .../single-namespace/kustomization.yaml | 0 .../operator/kustomization.yaml | 0 .../operator/patch-envvars.yaml | 0 .../operator/remove-watch-ns.yaml | 0 .../tenant-a-ns-rbac/kustomization.yaml | 29 ++++++ .../patch-rolebinding-subjects.yaml | 0 .../integration/integration_controller.go | 31 ++---- release.adoc | 6 +- script/Makefile | 6 +- 39 files changed, 395 insertions(+), 51 deletions(-) create mode 100644 e2e/install/kustomize/multi_namespace_test.go rename install/overlays/{kubernetes/descoped => all-namespaces}/kustomization.yaml (95%) rename install/overlays/{kubernetes/descoped => all-namespaces}/patch-operator-id.yaml (100%) rename install/overlays/{kubernetes/descoped => all-namespaces}/patch-watch-namespace-global.yaml (100%) create mode 100644 install/overlays/multi-namespace/kustomization.yaml create mode 100644 install/overlays/multi-namespace/operator/kustomization.yaml create mode 100644 install/overlays/multi-namespace/operator/operator-role-binding-events.yaml create mode 100644 install/overlays/multi-namespace/operator/operator-role-events.yaml create mode 100644 install/overlays/multi-namespace/operator/patch-envvars.yaml rename install/overlays/{kubernetes/single-namespace => multi-namespace}/tenant-a-ns-rbac/kustomization.yaml (96%) create mode 100644 install/overlays/multi-namespace/tenant-a-ns-rbac/patch-rolebinding-subjects.yaml create mode 100644 install/overlays/multi-namespace/tenant-b-ns-rbac/kustomization.yaml create mode 100644 install/overlays/multi-namespace/tenant-b-ns-rbac/patch-rolebinding-subjects.yaml rename install/overlays/{kubernetes => }/own-namespace/kustomization.yaml (96%) rename install/overlays/{kubernetes => }/own-namespace/patch-log-level.yaml (100%) rename install/overlays/{kubernetes => }/own-namespace/patch-node-selector.yaml (100%) rename install/overlays/{kubernetes => }/own-namespace/patch-operator-id.yaml (100%) rename install/overlays/{kubernetes => }/own-namespace/patch-ports.yaml (100%) rename install/overlays/{kubernetes => }/own-namespace/patch-resource-requirements.yaml (100%) rename install/overlays/{kubernetes => }/own-namespace/patch-toleration.yaml (100%) rename install/overlays/{kubernetes => }/single-namespace/kustomization.yaml (100%) rename install/overlays/{kubernetes => }/single-namespace/operator/kustomization.yaml (100%) rename install/overlays/{kubernetes => }/single-namespace/operator/patch-envvars.yaml (100%) rename install/overlays/{kubernetes => }/single-namespace/operator/remove-watch-ns.yaml (100%) create mode 100644 install/overlays/single-namespace/tenant-a-ns-rbac/kustomization.yaml rename install/overlays/{kubernetes => }/single-namespace/tenant-a-ns-rbac/patch-rolebinding-subjects.yaml (100%) diff --git a/.github/actions/release-nightly/action.yml b/.github/actions/release-nightly/action.yml index d53f4d0802..1c86b5a791 100644 --- a/.github/actions/release-nightly/action.yml +++ b/.github/actions/release-nightly/action.yml @@ -60,13 +60,13 @@ runs: - name: Set up QEMU (required by multi platform build) uses: docker/setup-qemu-action@06116385d9baf250c9f4dcb4858b16962ea869c3 - # + # # IMPORTANT: # Do we really want to run smoke test? # We can skip unless we see the usage of the nightly is widespread and it makes sense to run some smoke # test before releasing # - + # - name: Infra setting # uses: ./.github/actions/infra-setting @@ -178,7 +178,7 @@ runs: Apache Camel K ${{ env.VERSION }} build for testing purposes only (unstable). This nightly release is using an **unsupported** operator image published as `${{ env.IMAGE_NAME }}:${{ env.VERSION }}`. The available platforms are AMD64 and ARM64. ## Kubectl ``` - kubectl apply -k github.com/apache/camel-k/install/overlays/kubernetes/descoped?ref=${{ env.TAG }} + kubectl apply -k github.com/apache/camel-k/install/overlays/all-namespaces?ref=${{ env.TAG }} ``` ## Helm ``` diff --git a/.github/workflows/common.yml b/.github/workflows/common.yml index 65710e118a..a6023d5d3b 100644 --- a/.github/workflows/common.yml +++ b/.github/workflows/common.yml @@ -101,7 +101,7 @@ jobs: shell: bash run: | kubectl create ns camel-k - kubectl apply -k install/overlays/kubernetes/descoped/ --server-side --force-conflicts + kubectl apply -k install/overlays/all-namespaces/ --server-side --force-conflicts kubectl wait --for=condition=available deployment/camel-k-operator -n camel-k --timeout=60s # Install Apache Kamelets catalog mvn -q dependency:copy -Dartifact=org.apache.camel.kamelets:camel-kamelets:4.18.1:jar -Dmdep.useBaseVersion=true -DoutputDirectory=/tmp diff --git a/.github/workflows/nightly-multi.yml b/.github/workflows/nightly-multi.yml index bb3acc8eeb..bca149535e 100644 --- a/.github/workflows/nightly-multi.yml +++ b/.github/workflows/nightly-multi.yml @@ -55,7 +55,7 @@ jobs: VERSION="$(make get-version | sed s/-SNAPSHOT//)-nightly" sed -i "s#apache/camel-k:$THIS_VERSION#testcamelk/camel-k:$VERSION#g" install/base/config/manager/operator-deployment.yaml kubectl create ns camel-k - kubectl apply -k install/overlays/kubernetes/descoped/ --server-side --force-conflicts + kubectl apply -k install/overlays/all-namespaces/ --server-side --force-conflicts kubectl wait --for=condition=available deployment/camel-k-operator -n camel-k --timeout=60s # Install Apache Kamelets catalog mvn -q dependency:copy -Dartifact=org.apache.camel.kamelets:camel-kamelets:4.18.1:jar -Dmdep.useBaseVersion=true -DoutputDirectory=/tmp @@ -92,7 +92,7 @@ jobs: VERSION="$(make get-version | sed s/-SNAPSHOT//)-nightly-21-jdk" CUSTOM_IMAGE=testcamelk/camel-k CUSTOM_VERSION=$VERSION make bundle kubectl create ns camel-k - kubectl apply -k install/overlays/kubernetes/descoped/ --server-side --force-conflicts + kubectl apply -k install/overlays/all-namespaces/ --server-side --force-conflicts kubectl wait --for=condition=available deployment/camel-k-operator -n camel-k --timeout=60s # Install Apache Kamelets catalog mvn -q dependency:copy -Dartifact=org.apache.camel.kamelets:camel-kamelets:4.18.1:jar -Dmdep.useBaseVersion=true -DoutputDirectory=/tmp diff --git a/docs/modules/ROOT/pages/installation/advanced/jdk-version.adoc b/docs/modules/ROOT/pages/installation/advanced/jdk-version.adoc index 1c5b93d53a..8a7ad980e8 100644 --- a/docs/modules/ROOT/pages/installation/advanced/jdk-version.adoc +++ b/docs/modules/ROOT/pages/installation/advanced/jdk-version.adoc @@ -10,7 +10,7 @@ The list of available container images is published in (https://hub.docker.com/r If you want to use a different container image instead of the default, you will need to edit your operator Deployment by replacing the default image with the one you're choosing. You can use the following script in order to change on the fly the value and install a Camel K JDK 21 based operator: ```bash -kustomize build github.com/apache/camel-k/install/overlays/kubernetes/descoped?ref=v2.6.0 | sed 's#docker.io/apache/camel-k:2.6.0#docker.io/apache/camel-k:2.6.0-21-jdk#g' | kubectl apply -f - --server-side -n camel-k +kustomize build github.com/apache/camel-k/install/overlays/all-namespaces?ref=v2.6.0 | sed 's#docker.io/apache/camel-k:2.6.0#docker.io/apache/camel-k:2.6.0-21-jdk#g' | kubectl apply -f - --server-side -n camel-k ``` During the build, you should see the version used in the Maven building log traces: diff --git a/docs/modules/ROOT/pages/installation/installation.adoc b/docs/modules/ROOT/pages/installation/installation.adoc index b848ec1c8a..47d74ce7d2 100644 --- a/docs/modules/ROOT/pages/installation/installation.adoc +++ b/docs/modules/ROOT/pages/installation/installation.adoc @@ -13,7 +13,7 @@ https://kustomize.io[Kustomize] provides a declarative approach to the configura [subs=attributes+] ---- $ kubectl create ns camel-k -$ kubectl apply -k github.com/apache/camel-k/install/overlays/kubernetes/descoped?ref=v{last-released-version} --server-side +$ kubectl apply -k github.com/apache/camel-k/install/overlays/all-namespaces?ref=v{last-released-version} --server-side ---- You can specify as `ref` parameter the version you're willing to install (ie, `v{last-released-version}`). The command above will install a descoped (global) operator in the camel-k namespace. This is the suggested configuration in order to manage Integrations in all namespaces. @@ -43,6 +43,22 @@ You can edit the `Subscription` custom resource, setting the channel you want to NOTE: Some Kubernetes clusters such as Openshift may let you to perform the same operation from a GUI as well. Refer to the cluster instruction to learn how to perform such action from user interface. +[[installation-topology]] +== Installation topology + +When you decide to install the operator, you can decide to install the following topology: + +* Global operator: a single global operator watching all namespaces. +* Own namespace operator: a namespaces operator watching its own namespace only. +* Single namespace operator: an operator installed in a namespace and watching another namespace. +* Multi namespace operator: an operator installed in a namespace and watching multiple namespaces. + +The namespace(s) to watch is configured via `WATCH_NAMESPACE` variable in the operator `Deployment` resource. You can provide an empty value (watch all namespaces), a single value (watch either the own namespace or any other namespace) or a comma separated value (watching as many namespaces as provided). + +It's important to notice that when running the single or multiple namespace operator, you will need to provide the RBACs which are expected by the operator to run properly. For such a configuration you can take as a reference the `Kustomize` examples available in `/install/overlays/single-namespace/` and `/install/overlays/multi-namespace/`. The last topology is probably the most secure as it will avoid the operator to access to any resource outside those namespaces for which you've provided the proper security rules. + +NOTE: OLM only allows own and global installation mode. + [[bootstrap-configuration]] == Setup the operator configuration diff --git a/docs/modules/ROOT/pages/installation/upgrade.adoc b/docs/modules/ROOT/pages/installation/upgrade.adoc index d5e1624e80..01a908c36f 100644 --- a/docs/modules/ROOT/pages/installation/upgrade.adoc +++ b/docs/modules/ROOT/pages/installation/upgrade.adoc @@ -12,7 +12,7 @@ If you want to upgrade via https://kustomize.io[Kustomize] you'll need to execut [subs=attributes+] ---- -$ kubectl apply -k github.com/apache/camel-k/install/overlays/kubernetes/descoped?ref=v{last-released-version} --server-side --force-conflicts +$ kubectl apply -k github.com/apache/camel-k/install/overlays/all-namespaces?ref=v{last-released-version} --server-side --force-conflicts ---- [[operatorhub]] @@ -50,7 +50,7 @@ Since Camel K version 2, we're able to run any Camel K runtime version from the ---- kamel run /tmp/Test.java -t camel.runtime-version=1.17.0 -kubectl apply -k github.com/apache/camel-k/install/overlays/kubernetes/descoped?ref=v2.4.0 --server-side --force-conflicts //ie, version 2.4.0 +kubectl apply -k github.com/apache/camel-k/install/overlays/all-namespaces?ref=v2.4.0 --server-side --force-conflicts //ie, version 2.4.0 kamel rebuild test kamel logs test [1] 2023-04-13 13:38:43,648 INFO [org.apa.cam.k.Runtime] (main) Apache Camel K Runtime 1.17.0 diff --git a/e2e/install/kustomize/all_namespaces_test.go b/e2e/install/kustomize/all_namespaces_test.go index 9df5e5e8ff..9f25ceb069 100644 --- a/e2e/install/kustomize/all_namespaces_test.go +++ b/e2e/install/kustomize/all_namespaces_test.go @@ -49,12 +49,12 @@ func TestKustomizeDescoped(t *testing.T) { "sed", "-i", fmt.Sprintf("s/namespace: .*/namespace: %s/", ns), - fmt.Sprintf("%s/overlays/kubernetes/descoped/kustomization.yaml", kustomizeDir), + fmt.Sprintf("%s/overlays/all-namespaces/kustomization.yaml", kustomizeDir), )) ExpectExecSucceed(t, g, Kubectl( "apply", "-k", - fmt.Sprintf("%s/overlays/kubernetes/descoped", kustomizeDir), + fmt.Sprintf("%s/overlays/all-namespaces", kustomizeDir), "--server-side", )) diff --git a/e2e/install/kustomize/multi_namespace_test.go b/e2e/install/kustomize/multi_namespace_test.go new file mode 100644 index 0000000000..735d4b3e05 --- /dev/null +++ b/e2e/install/kustomize/multi_namespace_test.go @@ -0,0 +1,98 @@ +//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 TestKustomizeMultiNamespace(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" and "tenant-b" + // but it won't reconcile in any other namespaces, for example, "tenant-z" + WithNamedTestNamespace(t, func(ctx context.Context, g *WithT, operatorNs string) { + WithNamedTestNamespace(t, func(ctx context.Context, g *WithT, tenantANs string) { + WithNamedTestNamespace(t, func(ctx context.Context, g *WithT, tenantBNs 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/multi-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-z" is not reconciled + g.Expect(KamelRun(t, ctx, tenantNs, "files/yaml.yaml").Execute()).To(Succeed()) + g.Consistently(IntegrationPhase(t, ctx, tenantNs, "yaml"), 30*time.Second).Should(BeEmpty()) + }, "tenant-z") + + // Test a simple integration in "tenant-a" is reconciled and runs correctly + g.Expect(KamelRun(t, ctx, tenantANs, "files/yaml.yaml").Execute()).To(Succeed()) + g.Eventually(IntegrationConditionStatus(t, ctx, tenantANs, "yaml", v1.IntegrationConditionReady), TestTimeoutMedium). + Should(Equal(corev1.ConditionTrue)) + g.Eventually(IntegrationLogs(t, ctx, tenantANs, "yaml"), TestTimeoutShort).Should(ContainSubstring("Magicstring!")) + + // Test a simple integration in "tenant-b" is reconciled and runs correctly + g.Expect(KamelRun(t, ctx, tenantBNs, "files/yaml.yaml").Execute()).To(Succeed()) + g.Eventually(IntegrationConditionStatus(t, ctx, tenantBNs, "yaml", v1.IntegrationConditionReady), TestTimeoutMedium). + Should(Equal(corev1.ConditionTrue)) + g.Eventually(IntegrationLogs(t, ctx, tenantBNs, "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)) + g.Eventually(Integration(t, ctx, "tenant-b", "yaml"), TestTimeoutShort).ShouldNot(BeNil()) + g.Eventually(IntegrationConditionStatus(t, ctx, "tenant-b", "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-b") + }, "tenant-a") + }, "operators") +} diff --git a/e2e/install/kustomize/own_namespace_test.go b/e2e/install/kustomize/own_namespace_test.go index 82f0a7161a..00ab1f95b1 100644 --- a/e2e/install/kustomize/own_namespace_test.go +++ b/e2e/install/kustomize/own_namespace_test.go @@ -49,12 +49,12 @@ func TestKustomizeOwnNamespace(t *testing.T) { "sed", "-i", fmt.Sprintf("s/namespace: .*/namespace: %s/", ns), - fmt.Sprintf("%s/overlays/kubernetes/own-namespace/kustomization.yaml", kustomizeDir), + fmt.Sprintf("%s/overlays/own-namespace/kustomization.yaml", kustomizeDir), )) ExpectExecSucceed(t, g, Kubectl( "apply", "-k", - fmt.Sprintf("%s/overlays/kubernetes/own-namespace", kustomizeDir), + fmt.Sprintf("%s/overlays/own-namespace", kustomizeDir), "--server-side", )) diff --git a/e2e/install/kustomize/single_namespace_test.go b/e2e/install/kustomize/single_namespace_test.go index e0229e49a3..158401a6f3 100644 --- a/e2e/install/kustomize/single_namespace_test.go +++ b/e2e/install/kustomize/single_namespace_test.go @@ -51,7 +51,7 @@ func TestKustomizeSingleNamespace(t *testing.T) { ExpectExecSucceed(t, g, Kubectl( "apply", "-k", - fmt.Sprintf("%s/overlays/kubernetes/single-namespace", kustomizeDir), + fmt.Sprintf("%s/overlays/single-namespace", kustomizeDir), "--server-side", )) g.Eventually(OperatorPod(t, ctx, operatorNs)).ShouldNot(BeNil()) @@ -60,7 +60,7 @@ func TestKustomizeSingleNamespace(t *testing.T) { 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()) + g.Consistently(IntegrationPhase(t, ctx, tenantNs, "yaml"), 30*time.Second).Should(BeEmpty()) }, "tenant-b") // Test a simple integration in "tenant-a" is reconciled and runs correctly diff --git a/e2e/install/upgrade/upgrade_test.go b/e2e/install/upgrade/upgrade_test.go index 24c2b1b801..6522791ace 100644 --- a/e2e/install/upgrade/upgrade_test.go +++ b/e2e/install/upgrade/upgrade_test.go @@ -87,7 +87,7 @@ func TestUpgrade(t *testing.T) { // kustomizeCmd := exec.Command( // "kubectl", // "kustomize", - // "github.com/apache/camel-k/install/overlays/kubernetes/descoped?ref=v"+lastVersion, + // "github.com/apache/camel-k/install/overlays/all-namespaces?ref=v"+lastVersion, // ) // output, err := kustomizeCmd.Output() // g.Expect(err).To(BeNil()) diff --git a/install/overlays/kubernetes/descoped/kustomization.yaml b/install/overlays/all-namespaces/kustomization.yaml similarity index 95% rename from install/overlays/kubernetes/descoped/kustomization.yaml rename to install/overlays/all-namespaces/kustomization.yaml index 8ebdff72d8..223739fef5 100644 --- a/install/overlays/kubernetes/descoped/kustomization.yaml +++ b/install/overlays/all-namespaces/kustomization.yaml @@ -18,8 +18,8 @@ apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization resources: -- ../../../base -- ../../../base/config/rbac/descoped +- ../../base +- ../../base/config/rbac/descoped namespace: camel-k diff --git a/install/overlays/kubernetes/descoped/patch-operator-id.yaml b/install/overlays/all-namespaces/patch-operator-id.yaml similarity index 100% rename from install/overlays/kubernetes/descoped/patch-operator-id.yaml rename to install/overlays/all-namespaces/patch-operator-id.yaml diff --git a/install/overlays/kubernetes/descoped/patch-watch-namespace-global.yaml b/install/overlays/all-namespaces/patch-watch-namespace-global.yaml similarity index 100% rename from install/overlays/kubernetes/descoped/patch-watch-namespace-global.yaml rename to install/overlays/all-namespaces/patch-watch-namespace-global.yaml diff --git a/install/overlays/multi-namespace/kustomization.yaml b/install/overlays/multi-namespace/kustomization.yaml new file mode 100644 index 0000000000..4f0d3d5e64 --- /dev/null +++ b/install/overlays/multi-namespace/kustomization.yaml @@ -0,0 +1,23 @@ +# --------------------------------------------------------------------------- +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# --------------------------------------------------------------------------- +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +resources: +- operator +- tenant-a-ns-rbac +- tenant-b-ns-rbac diff --git a/install/overlays/multi-namespace/operator/kustomization.yaml b/install/overlays/multi-namespace/operator/kustomization.yaml new file mode 100644 index 0000000000..931deaa907 --- /dev/null +++ b/install/overlays/multi-namespace/operator/kustomization.yaml @@ -0,0 +1,30 @@ +# --------------------------------------------------------------------------- +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# --------------------------------------------------------------------------- +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +namespace: operators +nameSuffix: -tenant-ab + +resources: +# The operator still needs to access several resources in its own namespace +- ../../own-namespace + +patches: + - target: + kind: Deployment + path: patch-envvars.yaml diff --git a/install/overlays/multi-namespace/operator/operator-role-binding-events.yaml b/install/overlays/multi-namespace/operator/operator-role-binding-events.yaml new file mode 100644 index 0000000000..263c804ec2 --- /dev/null +++ b/install/overlays/multi-namespace/operator/operator-role-binding-events.yaml @@ -0,0 +1,30 @@ +# --------------------------------------------------------------------------- +# 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. +# --------------------------------------------------------------------------- + +kind: RoleBinding +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: camel-monitor-operator-events + labels: + app: "camel-monitor" +subjects: +- kind: ServiceAccount + name: camel-monitor-operator +roleRef: + kind: Role + name: camel-monitor-operator-events + apiGroup: rbac.authorization.k8s.io diff --git a/install/overlays/multi-namespace/operator/operator-role-events.yaml b/install/overlays/multi-namespace/operator/operator-role-events.yaml new file mode 100644 index 0000000000..b6466fcb48 --- /dev/null +++ b/install/overlays/multi-namespace/operator/operator-role-events.yaml @@ -0,0 +1,36 @@ +# --------------------------------------------------------------------------- +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# --------------------------------------------------------------------------- + +kind: Role +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: camel-monitor-operator-events + labels: + app: "camel-monitor" +rules: +- apiGroups: + # Deprecated: this legacy group is replaced by events.k8s.io from version 0.2.0 onward + - "" + - "events.k8s.io" + resources: + - events + verbs: + - create + - patch + - get + - list + - watch diff --git a/install/overlays/multi-namespace/operator/patch-envvars.yaml b/install/overlays/multi-namespace/operator/patch-envvars.yaml new file mode 100644 index 0000000000..fd349fb781 --- /dev/null +++ b/install/overlays/multi-namespace/operator/patch-envvars.yaml @@ -0,0 +1,22 @@ +# --------------------------------------------------------------------------- +# 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. +# --------------------------------------------------------------------------- + +- op: remove + path: /spec/template/spec/containers/0/env/0/valueFrom +- op: add + path: /spec/template/spec/containers/0/env/0/value + value: "tenant-a,tenant-b" \ No newline at end of file diff --git a/install/overlays/kubernetes/single-namespace/tenant-a-ns-rbac/kustomization.yaml b/install/overlays/multi-namespace/tenant-a-ns-rbac/kustomization.yaml similarity index 96% rename from install/overlays/kubernetes/single-namespace/tenant-a-ns-rbac/kustomization.yaml rename to install/overlays/multi-namespace/tenant-a-ns-rbac/kustomization.yaml index 0b1aafa9b3..74bea0c2a7 100644 --- a/install/overlays/kubernetes/single-namespace/tenant-a-ns-rbac/kustomization.yaml +++ b/install/overlays/multi-namespace/tenant-a-ns-rbac/kustomization.yaml @@ -21,7 +21,7 @@ namespace: tenant-a nameSuffix: -tenant-a resources: -- ../../../../base/config/rbac/namespaced +- ../../../base/config/rbac/namespaced patches: - target: diff --git a/install/overlays/multi-namespace/tenant-a-ns-rbac/patch-rolebinding-subjects.yaml b/install/overlays/multi-namespace/tenant-a-ns-rbac/patch-rolebinding-subjects.yaml new file mode 100644 index 0000000000..917bb1cb87 --- /dev/null +++ b/install/overlays/multi-namespace/tenant-a-ns-rbac/patch-rolebinding-subjects.yaml @@ -0,0 +1,23 @@ +# --------------------------------------------------------------------------- +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# --------------------------------------------------------------------------- + +- op: add + path: /subjects/0/namespace + value: "operators" +- op: replace + path: /subjects/0/name + value: camel-k-operator-tenant-ab \ No newline at end of file diff --git a/install/overlays/multi-namespace/tenant-b-ns-rbac/kustomization.yaml b/install/overlays/multi-namespace/tenant-b-ns-rbac/kustomization.yaml new file mode 100644 index 0000000000..32274eb6f1 --- /dev/null +++ b/install/overlays/multi-namespace/tenant-b-ns-rbac/kustomization.yaml @@ -0,0 +1,29 @@ +# --------------------------------------------------------------------------- +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# --------------------------------------------------------------------------- +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +namespace: tenant-b +nameSuffix: -tenant-b + +resources: +- ../../../base/config/rbac/namespaced + +patches: + - target: + kind: RoleBinding + path: patch-rolebinding-subjects.yaml \ No newline at end of file diff --git a/install/overlays/multi-namespace/tenant-b-ns-rbac/patch-rolebinding-subjects.yaml b/install/overlays/multi-namespace/tenant-b-ns-rbac/patch-rolebinding-subjects.yaml new file mode 100644 index 0000000000..917bb1cb87 --- /dev/null +++ b/install/overlays/multi-namespace/tenant-b-ns-rbac/patch-rolebinding-subjects.yaml @@ -0,0 +1,23 @@ +# --------------------------------------------------------------------------- +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# --------------------------------------------------------------------------- + +- op: add + path: /subjects/0/namespace + value: "operators" +- op: replace + path: /subjects/0/name + value: camel-k-operator-tenant-ab \ No newline at end of file diff --git a/install/overlays/kubernetes/own-namespace/kustomization.yaml b/install/overlays/own-namespace/kustomization.yaml similarity index 96% rename from install/overlays/kubernetes/own-namespace/kustomization.yaml rename to install/overlays/own-namespace/kustomization.yaml index 1e824fa3a7..51b84f5954 100644 --- a/install/overlays/kubernetes/own-namespace/kustomization.yaml +++ b/install/overlays/own-namespace/kustomization.yaml @@ -18,8 +18,8 @@ apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization resources: -- ../../../base -- ../../../base/config/rbac/namespaced +- ../../base +- ../../base/config/rbac/namespaced namespace: default diff --git a/install/overlays/kubernetes/own-namespace/patch-log-level.yaml b/install/overlays/own-namespace/patch-log-level.yaml similarity index 100% rename from install/overlays/kubernetes/own-namespace/patch-log-level.yaml rename to install/overlays/own-namespace/patch-log-level.yaml diff --git a/install/overlays/kubernetes/own-namespace/patch-node-selector.yaml b/install/overlays/own-namespace/patch-node-selector.yaml similarity index 100% rename from install/overlays/kubernetes/own-namespace/patch-node-selector.yaml rename to install/overlays/own-namespace/patch-node-selector.yaml diff --git a/install/overlays/kubernetes/own-namespace/patch-operator-id.yaml b/install/overlays/own-namespace/patch-operator-id.yaml similarity index 100% rename from install/overlays/kubernetes/own-namespace/patch-operator-id.yaml rename to install/overlays/own-namespace/patch-operator-id.yaml diff --git a/install/overlays/kubernetes/own-namespace/patch-ports.yaml b/install/overlays/own-namespace/patch-ports.yaml similarity index 100% rename from install/overlays/kubernetes/own-namespace/patch-ports.yaml rename to install/overlays/own-namespace/patch-ports.yaml diff --git a/install/overlays/kubernetes/own-namespace/patch-resource-requirements.yaml b/install/overlays/own-namespace/patch-resource-requirements.yaml similarity index 100% rename from install/overlays/kubernetes/own-namespace/patch-resource-requirements.yaml rename to install/overlays/own-namespace/patch-resource-requirements.yaml diff --git a/install/overlays/kubernetes/own-namespace/patch-toleration.yaml b/install/overlays/own-namespace/patch-toleration.yaml similarity index 100% rename from install/overlays/kubernetes/own-namespace/patch-toleration.yaml rename to install/overlays/own-namespace/patch-toleration.yaml diff --git a/install/overlays/kubernetes/single-namespace/kustomization.yaml b/install/overlays/single-namespace/kustomization.yaml similarity index 100% rename from install/overlays/kubernetes/single-namespace/kustomization.yaml rename to install/overlays/single-namespace/kustomization.yaml diff --git a/install/overlays/kubernetes/single-namespace/operator/kustomization.yaml b/install/overlays/single-namespace/operator/kustomization.yaml similarity index 100% rename from install/overlays/kubernetes/single-namespace/operator/kustomization.yaml rename to install/overlays/single-namespace/operator/kustomization.yaml diff --git a/install/overlays/kubernetes/single-namespace/operator/patch-envvars.yaml b/install/overlays/single-namespace/operator/patch-envvars.yaml similarity index 100% rename from install/overlays/kubernetes/single-namespace/operator/patch-envvars.yaml rename to install/overlays/single-namespace/operator/patch-envvars.yaml diff --git a/install/overlays/kubernetes/single-namespace/operator/remove-watch-ns.yaml b/install/overlays/single-namespace/operator/remove-watch-ns.yaml similarity index 100% rename from install/overlays/kubernetes/single-namespace/operator/remove-watch-ns.yaml rename to install/overlays/single-namespace/operator/remove-watch-ns.yaml diff --git a/install/overlays/single-namespace/tenant-a-ns-rbac/kustomization.yaml b/install/overlays/single-namespace/tenant-a-ns-rbac/kustomization.yaml new file mode 100644 index 0000000000..74bea0c2a7 --- /dev/null +++ b/install/overlays/single-namespace/tenant-a-ns-rbac/kustomization.yaml @@ -0,0 +1,29 @@ +# --------------------------------------------------------------------------- +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# --------------------------------------------------------------------------- +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +namespace: tenant-a +nameSuffix: -tenant-a + +resources: +- ../../../base/config/rbac/namespaced + +patches: + - target: + kind: RoleBinding + path: patch-rolebinding-subjects.yaml \ No newline at end of file diff --git a/install/overlays/kubernetes/single-namespace/tenant-a-ns-rbac/patch-rolebinding-subjects.yaml b/install/overlays/single-namespace/tenant-a-ns-rbac/patch-rolebinding-subjects.yaml similarity index 100% rename from install/overlays/kubernetes/single-namespace/tenant-a-ns-rbac/patch-rolebinding-subjects.yaml rename to install/overlays/single-namespace/tenant-a-ns-rbac/patch-rolebinding-subjects.yaml diff --git a/pkg/controller/integration/integration_controller.go b/pkg/controller/integration/integration_controller.go index 10a8c7c351..387502ba13 100644 --- a/pkg/controller/integration/integration_controller.go +++ b/pkg/controller/integration/integration_controller.go @@ -22,7 +22,6 @@ import ( "fmt" "reflect" - "time" appsv1 "k8s.io/api/apps/v1" batchv1 "k8s.io/api/batch/v1" @@ -41,7 +40,6 @@ import ( "sigs.k8s.io/controller-runtime/pkg/predicate" "sigs.k8s.io/controller-runtime/pkg/reconcile" - "knative.dev/serving/pkg/apis/serving" servingv1 "knative.dev/serving/pkg/apis/serving/v1" v1 "github.com/apache/camel-k/v2/pkg/apis/camel/v1" @@ -68,7 +66,7 @@ func Add(ctx context.Context, mgr manager.Manager, c client.Client) error { return fmt.Errorf("unable to set up field indexer for status.phase: %w", err) } - return add(ctx, mgr, c, newReconciler(mgr, c)) + return add(mgr, c, newReconciler(mgr, c)) } func newReconciler(mgr manager.Manager, c client.Client) reconcile.Reconciler { @@ -287,7 +285,7 @@ func integrationPlatformEnqueueRequestsFromMapFunc(ctx context.Context, c client return requests } -func add(ctx context.Context, mgr manager.Manager, c client.Client, r reconcile.Reconciler) error { +func add(mgr manager.Manager, c client.Client, r reconcile.Reconciler) error { b := builder.ControllerManagedBy(mgr). Named("integration-controller"). // Watch for changes to primary resource Integration @@ -318,12 +316,9 @@ func add(ctx context.Context, mgr manager.Manager, c client.Client, r reconcile. watchCronJobResources(b) } // Watch for the Knative Services conditionally - if ok, err := kubernetes.IsAPIResourceInstalled(c, servingv1.SchemeGroupVersion.String(), reflect.TypeFor[servingv1.Service]().Name()); err != nil { + err := watchKnativeResources(c, b) + if err != nil { return err - } else if ok { - if err = watchKnativeResources(ctx, c, b); err != nil { - return err - } } return b.Complete(r) @@ -423,7 +418,7 @@ func watchCronJobResources(b *builder.Builder) { b.Owns(&batchv1.CronJob{}, builder.WithPredicates(StatusChangedPredicate{})) } -func watchKnativeResources(ctx context.Context, c client.Client, b *builder.Builder) error { +func watchKnativeResources(c client.Client, b *builder.Builder) error { // Watch for the owned Knative Services conditionally ok, err := kubernetes.IsAPIResourceInstalled(c, servingv1.SchemeGroupVersion.String(), reflect.TypeFor[servingv1.Service]().Name()) if err != nil { @@ -437,19 +432,9 @@ func watchKnativeResources(ctx context.Context, c client.Client, b *builder.Buil return nil } - // Check for permission to watch the Knative Service resource - checkCtx, cancel := context.WithTimeout(ctx, time.Minute) - defer cancel() - if ok, err = kubernetes.CheckSelfPermission(checkCtx, c, serving.GroupName, "services", platform.GetOperatorWatchNamespace(), "", "watch"); err != nil { - return err - } else if ok { - log.Info("KnativeService resources installed in the cluster. RBAC privileges assigned correctly, you can use Knative serving features.") - 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 " + - "to watch for Camel K managed Knative Services.") - } + log.Info("KnativeService resources installed in the cluster. You can use Knative serving features: " + + "make sure to assign Knative Services RBAC privileges") + b.Owns(&servingv1.Service{}, builder.WithPredicates(StatusChangedPredicate{})) return nil } diff --git a/release.adoc b/release.adoc index fe19cf8064..20fa79948f 100644 --- a/release.adoc +++ b/release.adoc @@ -85,7 +85,7 @@ Camel K Client 2.0.0 If the version retrieved is the one expected you can run an installation procedure: ``` -kustomize build github.com/apache/camel-k/install/overlays/kubernetes/descoped?ref=v | sed 's#apache/camel-k#camelk/camel-k#g' | kubectl apply -f - --server-side +kustomize build github.com/apache/camel-k/install/overlays/all-namespaces?ref=v | sed 's#apache/camel-k#camelk/camel-k#g' | kubectl apply -f - --server-side ``` Make some test and if all is in order, you can upload the sources and CLIs to the dist/dev repository in ASF the staged artifacts, in order to link them in the release vote communication. @@ -105,7 +105,7 @@ You should also manually add some text to explain how to test the staging artifa This release is on vote. It is using a **staging** operator image published as `docker.io/camelk/camel-k:`. The available platforms are AMD64 and ARM64. You can test it following these instructions: ### Kubectl ``` -kustomize build github.com/apache/camel-k/install/overlays/kubernetes/descoped?ref=v | sed 's#apache/camel-k#camelk/camel-k#g' | kubectl apply -f - --server-side +kustomize build github.com/apache/camel-k/install/overlays/all-namespaces?ref=v | sed 's#apache/camel-k#camelk/camel-k#g' | kubectl apply -f - --server-side ``` ### Helm ``` @@ -223,7 +223,7 @@ You can edit the draft release and set it to final, marking as last release. You Install the operator looking at the official https://camel.apache.org/camel-k/<2.5.x>/installation/installation.html[Camel K operator installation procedure]. ``` -Perform a simple test to verify that everything is in place (running a "Hello World" integration after an installation done with, as an example `kubectl apply -k github.com/apache/camel-k/install/overlays/kubernetes/descoped?ref=v --server-side`). Do a simple final test. +Perform a simple test to verify that everything is in place (running a "Hello World" integration after an installation done with, as an example `kubectl apply -k github.com/apache/camel-k/install/overlays/all-namespaces?ref=v --server-side`). Do a simple final test. === Announce the release diff --git a/script/Makefile b/script/Makefile index f1acf5ed84..be3d147dba 100644 --- a/script/Makefile +++ b/script/Makefile @@ -754,7 +754,7 @@ goimport: # START Local installation procedure. Handy for development purpose ##### -KUSTOMIZE_DIR = "install/overlays/kubernetes/descoped" +KUSTOMIZE_DIR = "install/overlays/all-namespaces" DEFAULT_NS = "camel-k" .PHONY: install install-k8s-global install-k8s-ns install-openshift-global install-openshift-ns @@ -800,11 +800,11 @@ endif kubectl apply -k $(KUST_TMP)/$(KUSTOMIZE_DIR) --server-side --force-conflicts install-k8s-global: DEFAULT_NS="camel-k" -install-k8s-global: KUSTOMIZE_DIR="install/overlays/kubernetes/descoped" +install-k8s-global: KUSTOMIZE_DIR="install/overlays/all-namespaces" 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/own-namespace" +install-k8s-ns: KUSTOMIZE_DIR="install/overlays/own-namespace" install-k8s-ns: clone-kustomize-dir set-operator-id set-operator-env install-operator install-registry: NAMESPACE="camel-k"