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

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

commit 5b48717870fec73b6f5547e374b5ef36b61571cf
Author: Pasquale Congiusti <[email protected]>
AuthorDate: Sat Sep 26 10:42:52 2026 +0200

    feat(ci): use dev container registry
---
 .github/actions/registry-setting/action.yml        |   4 +-
 .github/workflows/cert-manager.yml                 |  12 +-
 .github/workflows/common.yml                       |  28 +-
 .github/workflows/gateway.yml                      |  12 +-
 .github/workflows/install.yml                      |  11 -
 .github/workflows/kafka.yml                        |  12 +-
 .github/workflows/knative.yml                      |  12 +-
 .github/workflows/native.yml                       |  12 +-
 .github/workflows/nightly-install-olm.yml          |  11 -
 .github/workflows/nightly-multi.yml                |  18 +-
 .github/workflows/telemetry.yml                    |  12 +-
 .../ROOT/pages/contributing/developers.adoc        |   3 +-
 .../ROOT/pages/installation/installation.adoc      |  43 +--
 docs/modules/ROOT/pages/installation/registry.adoc |  13 +-
 e2e/advanced/environment_test.go                   |  10 -
 e2e/install/helm/setup_test.go                     |  11 -
 e2e/install/kustomize/all_namespaces_test.go       |   2 +-
 e2e/install/kustomize/single_namespace_test.go     |   2 +-
 e2e/support/test_support.go                        |  17 +-
 helm/camel-k/README.md                             |  28 +-
 helm/camel-k/templates/operator-deployment.yaml    |  22 +-
 helm/camel-k/templates/rbacs-common.yaml           |  69 +++++
 pkg/cmd/operator/operator.go                       |  58 ++--
 pkg/install/registry.go                            | 161 +++++++----
 pkg/install/registry_test.go                       | 313 +++++++++++++++++++++
 pkg/resources/config/rbac/dev-registry-role.yaml   |  44 +++
 pkg/util/kubernetes/docker_secret.go               |  20 +-
 27 files changed, 665 insertions(+), 295 deletions(-)

diff --git a/.github/actions/registry-setting/action.yml 
b/.github/actions/registry-setting/action.yml
index b0a6cade9..6c9adc983 100644
--- a/.github/actions/registry-setting/action.yml
+++ b/.github/actions/registry-setting/action.yml
@@ -16,7 +16,7 @@
 # ---------------------------------------------------------------------------
 
 name: registry-setting
-description: 'Setting registry'
+description: 'Setting registry externally'
 
 runs:
   using: "composite"
@@ -30,7 +30,7 @@ runs:
       # Create a self-signed certificate
       openssl req -x509 -newkey rsa:2048 -nodes   -keyout registry.key   -out 
registry.crt   -days 7   -subj "/CN=registry"
       kubectl create secret tls registry-tls --cert=registry.crt 
--key=registry.key -n registry
-      kubectl apply -f .github/actions/registry-setting/registry.yaml -n 
registry
+      kubectl apply -f .github/actions/ext-registry-setting/registry.yaml -n 
registry
       kubectl wait --for=condition=available deployment/registry -n registry 
--timeout=60s
       KAMEL_INSTALL_REGISTRY="$(kubectl -n registry get service registry -o 
jsonpath='{.spec.clusterIP}')"
       echo "KAMEL_INSTALL_REGISTRY=$KAMEL_INSTALL_REGISTRY" >> "$GITHUB_ENV"
diff --git a/.github/workflows/cert-manager.yml 
b/.github/workflows/cert-manager.yml
index e83e1d46e..7d3acf579 100644
--- a/.github/workflows/cert-manager.yml
+++ b/.github/workflows/cert-manager.yml
@@ -72,18 +72,12 @@ jobs:
       run: |
         ./e2e/cert-manager/setup/setup.sh
 
-    - name: Create operator namespace
-      shell: bash
-      run: |
-        kubectl create ns camel-k
-
-    - name: Registry setting
-      uses: ./.github/actions/registry-setting
-
     - name: Install operator
       shell: bash
       run: |
-        make install-k8s-global
+        kubectl create ns camel-k
+        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
 
     - name: Run test
       shell: bash
diff --git a/.github/workflows/common.yml b/.github/workflows/common.yml
index 8b2edd1da..b931cb295 100644
--- a/.github/workflows/common.yml
+++ b/.github/workflows/common.yml
@@ -70,17 +70,20 @@ jobs:
     - name: Infra setting
       uses: ./.github/actions/infra-setting
 
-    - name: Create operator namespace
+    - name: Install operator
       shell: bash
       run: |
         kubectl create ns camel-k
+        # the extra configuration is required to run the container registry 
with secret
+        kubectl create configmap camel-k-operator-configmap-configuration \
+          --from-literal=ENABLE_DEV_REGISTRY_SECRET="true" \
+          
--from-literal=MAVEN_REPOSITORIES_ALLOWED="https://maven.repository.redhat.com/ga@id=redhat";
 \
+          -n camel-k
+        # make the test aware that each Integration requires a pull secret
+        E2E_TEST_REGISTRY_SECRET_COPY=true
+        echo "E2E_TEST_REGISTRY_SECRET_COPY=$E2E_TEST_REGISTRY_SECRET_COPY" >> 
$GITHUB_ENV
+        echo "Setting E2E_TEST_REGISTRY_SECRET_COPY=true in order to automate 
the copy of the pull secret and the setting in each test"
 
-    - name: Registry setting
-      uses: ./.github/actions/registry-setting
-
-    - name: Install operator
-      shell: bash
-      run: |
         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
@@ -104,17 +107,6 @@ jobs:
     - name: Infra setting
       uses: ./.github/actions/infra-setting
 
-    - name: Create registry secret namespace
-      shell: bash
-      run: |
-        kubectl create ns camel-k
-        E2E_TEST_REGISTRY_CONFIG_COPY=true
-        echo "E2E_TEST_REGISTRY_CONFIG_COPY=$E2E_TEST_REGISTRY_CONFIG_COPY" >> 
$GITHUB_ENV
-        echo "Setting E2E_TEST_REGISTRY_CONFIG_COPY=true in order to automate 
the copy of the registry config for operator install"
-
-    - name: Registry setting
-      uses: ./.github/actions/registry-setting
-
     - name: Install CRDs
       shell: bash
       run: |
diff --git a/.github/workflows/gateway.yml b/.github/workflows/gateway.yml
index e3f29074f..8c3b546b8 100644
--- a/.github/workflows/gateway.yml
+++ b/.github/workflows/gateway.yml
@@ -72,18 +72,12 @@ jobs:
       run: |
         ./e2e/gateway/setup/setup.sh
 
-    - name: Create operator namespace
-      shell: bash
-      run: |
-        kubectl create ns camel-k
-
-    - name: Registry setting
-      uses: ./.github/actions/registry-setting
-
     - name: Install operator
       shell: bash
       run: |
-        make install-k8s-global
+        kubectl create ns camel-k
+        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
 
     - name: Run test
       shell: bash
diff --git a/.github/workflows/install.yml b/.github/workflows/install.yml
index 88ca48c9a..4bba6055c 100644
--- a/.github/workflows/install.yml
+++ b/.github/workflows/install.yml
@@ -85,17 +85,6 @@ jobs:
       run: |
         make release-helm
 
-    - name: Create registry secret namespace
-      shell: bash
-      run: |
-        kubectl create ns camel-k
-        E2E_TEST_REGISTRY_CONFIG_COPY=true
-        echo "E2E_TEST_REGISTRY_CONFIG_COPY=$E2E_TEST_REGISTRY_CONFIG_COPY" >> 
$GITHUB_ENV
-        echo "Setting E2E_TEST_REGISTRY_CONFIG_COPY=true in order to automate 
the copy of the registry config for operator install"
-
-    - name: Registry setting
-      uses: ./.github/actions/registry-setting
-
     - name: Run tests
       shell: bash
       run: |
diff --git a/.github/workflows/kafka.yml b/.github/workflows/kafka.yml
index 1481627e1..d3473128a 100644
--- a/.github/workflows/kafka.yml
+++ b/.github/workflows/kafka.yml
@@ -72,18 +72,12 @@ jobs:
       run: |
         ./e2e/kafka/setup/setup.sh
 
-    - name: Create operator namespace
-      shell: bash
-      run: |
-        kubectl create ns camel-k
-
-    - name: Registry setting
-      uses: ./.github/actions/registry-setting
-
     - name: Install operator
       shell: bash
       run: |
-        make install-k8s-global
+        kubectl create ns camel-k
+        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
 
     - name: Run test
       shell: bash
diff --git a/.github/workflows/knative.yml b/.github/workflows/knative.yml
index acbe222c4..d42a0ab27 100644
--- a/.github/workflows/knative.yml
+++ b/.github/workflows/knative.yml
@@ -72,18 +72,12 @@ jobs:
       run: |
         ./e2e/knative/files/setup.sh
 
-    - name: Create operator namespace
-      shell: bash
-      run: |
-        kubectl create ns camel-k
-
-    - name: Registry setting
-      uses: ./.github/actions/registry-setting
-
     - name: Install operator
       shell: bash
       run: |
-        make install-k8s-global
+        kubectl create ns camel-k
+        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
         unzip /tmp/camel-kamelets-4.18.1.jar -d /tmp && kubectl apply -f 
/tmp/kamelets -n camel-k
diff --git a/.github/workflows/native.yml b/.github/workflows/native.yml
index ab1b80de9..13627fd01 100644
--- a/.github/workflows/native.yml
+++ b/.github/workflows/native.yml
@@ -73,18 +73,12 @@ jobs:
     - name: Infra setting
       uses: ./.github/actions/infra-setting
 
-    - name: Create operator namespace
-      shell: bash
-      run: |
-        kubectl create ns camel-k
-
-    - name: Registry setting
-      uses: ./.github/actions/registry-setting
-
     - name: Install operator
       shell: bash
       run: |
-        make install-k8s-global
+        kubectl create ns camel-k
+        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
         unzip /tmp/camel-kamelets-4.18.1.jar -d /tmp && kubectl apply -f 
/tmp/kamelets -n camel-k
diff --git a/.github/workflows/nightly-install-olm.yml 
b/.github/workflows/nightly-install-olm.yml
index 9f5682223..eaaee18a0 100644
--- a/.github/workflows/nightly-install-olm.yml
+++ b/.github/workflows/nightly-install-olm.yml
@@ -73,17 +73,6 @@ jobs:
         echo "Setting bundle image name as as $BUNDLE_IMAGE_NAME which is 
required by OLM tests"
         BUNDLE_IMAGE_NAME=docker.io/testcamelk/camel-k-bundle make bundle-push
 
-    - name: Create registry secret namespace
-      shell: bash
-      run: |
-        kubectl create ns camel-k
-        E2E_TEST_REGISTRY_CONFIG_COPY=true
-        echo "E2E_TEST_REGISTRY_CONFIG_COPY=$E2E_TEST_REGISTRY_CONFIG_COPY" >> 
$GITHUB_ENV
-        echo "Setting E2E_TEST_REGISTRY_CONFIG_COPY=true in order to automate 
the copy of the registry config for operator install"
-
-    - name: Registry setting
-      uses: ./.github/actions/registry-setting
-
     - name: Run tests
       shell: bash
       run: |
diff --git a/.github/workflows/nightly-multi.yml 
b/.github/workflows/nightly-multi.yml
index 921e7b3e4..329282b4d 100644
--- a/.github/workflows/nightly-multi.yml
+++ b/.github/workflows/nightly-multi.yml
@@ -48,20 +48,13 @@ jobs:
     - name: Infra setting
       uses: ./.github/actions/infra-setting
 
-    - name: Create operator namespace
-      shell: bash
-      run: |
-        kubectl create ns camel-k
-
-    - name: Registry setting
-      uses: ./.github/actions/registry-setting
-
     - name: Install operator
       shell: bash
       run: |
         THIS_VERSION="$(make get-version)"
         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/all-namespaces/ --server-side 
--force-conflicts
         kubectl wait --for=condition=available deployment/camel-k-operator -n 
camel-k --timeout=60s
         # Install Apache Kamelets catalog
@@ -93,19 +86,12 @@ jobs:
     - name: Infra setting
       uses: ./.github/actions/infra-setting
 
-    - name: Create operator namespace
-      shell: bash
-      run: |
-        kubectl create ns camel-k
-
-    - name: Registry setting
-      uses: ./.github/actions/registry-setting
-
     - name: Install operator
       shell: bash
       run: |
         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/all-namespaces/ --server-side 
--force-conflicts
         kubectl wait --for=condition=available deployment/camel-k-operator -n 
camel-k --timeout=60s
         # Install Apache Kamelets catalog
diff --git a/.github/workflows/telemetry.yml b/.github/workflows/telemetry.yml
index cd0d5fc18..e6af676f5 100644
--- a/.github/workflows/telemetry.yml
+++ b/.github/workflows/telemetry.yml
@@ -67,18 +67,12 @@ jobs:
     - name: Infra setting
       uses: ./.github/actions/infra-setting
 
-    - name: Create operator namespace
-      shell: bash
-      run: |
-        kubectl create ns camel-k
-
-    - name: Registry setting
-      uses: ./.github/actions/registry-setting
-
     - name: Install operator
       shell: bash
       run: |
-        make install-k8s-global
+        kubectl create ns camel-k
+        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
 
     - name: Install OTLP Collector
       uses: ./.github/actions/install-otlp-collector
diff --git a/docs/modules/ROOT/pages/contributing/developers.adoc 
b/docs/modules/ROOT/pages/contributing/developers.adoc
index 17c11603b..88703eee7 100644
--- a/docs/modules/ROOT/pages/contributing/developers.adoc
+++ b/docs/modules/ROOT/pages/contributing/developers.adoc
@@ -165,12 +165,11 @@ If you want to test Helm installation
 
 * Build the Helm chart: `make release-helm`
 * Build the project and the image: `make images`
-* Set the internal registry: `export REGISTRY_ADDRESS=$(kubectl -n kube-system 
get service registry -o jsonpath='{.spec.clusterIP}')`
 * Install with Helm (look at the latest version produced by `make 
release-helm`)
 
 [source]
 ----
-helm install camel-k-dev docs/charts/camel-k-2.4.0-SNAPSHOT.tgz --set 
platform.build.registry.address=${REGISTRY_ADDRESS} --set 
platform.build.registry.insecure=true --set 
operator.image=apache/camel-k:2.4.0-SNAPSHOT
+helm install camel-k-dev docs/charts/camel-k-2.4.0-SNAPSHOT.tgz --set 
operator.image=apache/camel-k:2.4.0-SNAPSHOT
 ----
 
 * To uninstall: `helm uninstall camel-k-dev`
diff --git a/docs/modules/ROOT/pages/installation/installation.adoc 
b/docs/modules/ROOT/pages/installation/installation.adoc
index b0fd3cd56..c57f5f6c4 100644
--- a/docs/modules/ROOT/pages/installation/installation.adoc
+++ b/docs/modules/ROOT/pages/installation/installation.adoc
@@ -3,26 +3,6 @@
 
 Camel K allows us to run Camel integrations directly on a Kubernetes cluster. 
To use it, you need to be connected to a cloud environment or to a local 
cluster created for development purposes (ie, Minikube or Kind).
 
-[[registry]]
-== Container registry configuration
-
-You will need a container registry available in order to push and pull the 
generated Camel applications. The easiest way to configure it is to store the 
configuration on a Configmap which will be used by the operator:
-
-```
-$ kubectl create ns camel-k
-
-$ kubectl create configmap camel-k-operator-configmap-configuration \
-  --from-literal=REGISTRY_ADDRESS="docker.io" \
-  --from-literal=REGISTRY_SECRET="my-docker-secret"
-  -n camel-k
-```
-
-NOTE: the configuration needs to be available in the namespace where the 
operator will run.
-
-Have a further look at the xref:installation/registry.adoc[production ready 
registry configuration documentation].
-
-You can now install and run the Camel K operator. You can do it via any of the 
following methodologies:
-
 [[kustomize]]
 == Installation via Kustomize
 
@@ -105,6 +85,29 @@ Camel K installation is usually straightforward, but for 
certain cluster types y
 - xref:installation/platform/gke.adoc[Google Kubernetes Engine (GKE)]
 - xref:installation/platform/iks.adoc[IBM Kubernetes Services (IKS)]
 
+[[registry]]
+== Development container registry configuration
+
+Camel K needs a container registry in order to push the generated Camel 
applications and Kubernetes needs the same registry to pull and run them live. 
The project offers a default development container registry that will simplify 
quick starts, demo and testing. When running the project for the first time or 
in a local environment you shouldn't worry as it will be completely transparent 
to you. The development registry is controlled through the following 
environment variables configuration:
+
+[cols="1,3,2", options="header"]
+|===
+| Name | Description | Default Value
+
+| ENABLE_DEV_REGISTRY
+| Run a demo container registry application in the same namespace as the Camel 
K Operator.
+| true
+
+| ENABLE_DEV_REGISTRY_SECURE
+| Whether to secure the demo container registry with a default test user 
(`admin/password`). In this case you need to provide a secret in the 
Integration namespace and use the `pull-secret` trait configuration. Be aware 
that the operator will store a secret named `ck-dev-registry` in the same 
namespace where it is installed. You may copy and use it in your Integration 
namespace for local development purposes.
+| false
+
+|===
+
+When ready to move to a real enterprise installation you will need to turn the 
flag off (or remove it) and provide a proper 
xref:installation/registry.adoc[production ready registry configuration].
+
+NOTE: if you don't remove the `ENABLE_DEV_REGISTRY` parameter, the operator 
will overwrite any registry configuration at startup with its internal demo 
registry.
+
 [[fine-tuning]]
 == Fine Tuning
 
diff --git a/docs/modules/ROOT/pages/installation/registry.adoc 
b/docs/modules/ROOT/pages/installation/registry.adoc
index 6ea10eb47..cef72f0e9 100644
--- a/docs/modules/ROOT/pages/installation/registry.adoc
+++ b/docs/modules/ROOT/pages/installation/registry.adoc
@@ -6,11 +6,9 @@ image::architecture/camel-k-registry.svg[Container registry in 
action, width=800
 
 The Camel K operator is in charge to build a Camel application and to 
"containerize" it, storing the result into a container registry. The same 
registry is used by the cluster to run the Camel application. Basically the 
operator push the image and the cluster pull it from the same source.
 
-For the reason above it's important that you provide a container registry 
which is accessible from both the operator Pod and the cluster internal 
mechanisms. However, a **default registry** is present in certain platforms 
such as _Minikube_, _Openshift_ or _Docker Desktop_.
+For the reason above it's important that you provide a container registry 
which is accessible from both the operator Pod and the cluster internal 
mechanisms. However, a **default registry** is available when running quick 
starts and demos.
 
-For any other platform that do not provide a default container registry, then, 
a container registry must be provided accordingly.
-
-You will need to add or edit any existing registry environment variable 
configuration according your installation method. These are the variables you 
can configure:
+When ready to move to a real enterprise environment, then you're expected to 
configure the registry. You will need to add or edit any existing registry 
environment variable configuration according your installation method. These 
are the variables you can configure:
 
 [cols="1,3,2", options="header"]
 |===
@@ -59,6 +57,13 @@ NOTE: you must include `--docker-server docker.io` value 
also if you're using Do
 
 As each registry may have a slightly different way of securing the access you 
can use the generic guidelines provided in and adjust accordingly (more 
information in the xref:installation/registry/registry-secret.adoc[Secret 
registry configuration] guide). We expect that at the end of the process you 
have a public address (1) an _organization_ (2) (optional, see details below) 
and a _secret_ (3) values that will be used to configure the registry.
 
+[[pull-secret]]
+=== Deployment pull secret
+
+When you're using a secured registry protected via secret, you will also need 
to provide a secret via `pull-secret.secretName` trait parameter. We advice to 
separate the credentials used by the operator to **push** a container from the 
credentials required to **pull** the container. The operators requires 
**write** privileges, whilst the container only requires **read** privileges to 
get the container.
+
+The **read** secret you will be using via trait configuration must be 
available in the same Integration namespace.
+
 [[organization]]
 === Role of the organization parameter
 
diff --git a/e2e/advanced/environment_test.go b/e2e/advanced/environment_test.go
index 2df9018a8..1389c2d4b 100644
--- a/e2e/advanced/environment_test.go
+++ b/e2e/advanced/environment_test.go
@@ -24,7 +24,6 @@ package advanced
 
 import (
        "context"
-       "os"
        "strings"
        "testing"
 
@@ -53,17 +52,8 @@ func TestHTTPProxy(t *testing.T) {
                // Retrieve the Kubernetes Service ClusterIPs to populate the 
NO_PROXY environment variable
                svc := Service(t, ctx, TestDefaultNamespace, "kubernetes")()
                g.Expect(svc).NotTo(BeNil())
-
                noProxy = append(noProxy, svc.Spec.ClusterIPs...)
 
-               // Retrieve the internal container registry to populate the 
NO_PROXY environment variable
-               if registry, ok := os.LookupEnv("KAMEL_INSTALL_REGISTRY"); ok {
-                       domain := RegistryRegexp.FindString(registry)
-                       g.Expect(domain).NotTo(BeNil())
-                       domain = strings.Split(domain, ":")[0]
-                       noProxy = append(noProxy, domain)
-               }
-
                // Install Camel K with the HTTP proxy environment variable
                InstallOperatorWithConf(t, ctx, g, ns, "", false,
                        map[string]string{
diff --git a/e2e/install/helm/setup_test.go b/e2e/install/helm/setup_test.go
index f43dd1f07..d98f10f5d 100644
--- a/e2e/install/helm/setup_test.go
+++ b/e2e/install/helm/setup_test.go
@@ -43,9 +43,6 @@ func TestHelmInstallation(t *testing.T) {
                // as we must make the procedure to install them accordingly
                g.Expect(CRDs(t)()).Should(BeNil(), "No Camel K CRDs should be 
previously installed for this test")
 
-               registry := os.Getenv("KAMEL_INSTALL_REGISTRY")
-               g.Expect(registry).NotTo(BeEmpty(), "KAMEL_INSTALL_REGISTRY env 
var must not be empty")
-
                operatorID := "helm-ck"
                os.Setenv("CAMEL_K_TEST_MAKE_DIR", "../../../")
                ExpectExecSucceed(t, g,
@@ -54,14 +51,6 @@ func TestHelmInstallation(t *testing.T) {
                                "install",
                                "camel-k",
                                
fmt.Sprintf("../../../docs/charts/camel-k-%s.tgz", defaults.Version),
-                               "--set", 
"operator.env[0].name=REGISTRY_ADDRESS",
-                               "--set", "operator.env[0].value="+registry,
-                               // We expect the testing infra to make it 
available a secret
-                               // named "my-registry" in the installation 
namespace
-                               "--set", "operator.env[1].name=REGISTRY_SECRET",
-                               "--set", "operator.env[1].value=my-registry",
-                               "--set", 
"operator.env[2].name=REGISTRY_INSECURE",
-                               "--set-string", "operator.env[2].value=true",
                                "--set", fmt.Sprintf("operator.operatorId=%s", 
operatorID),
                                "-n", operatorNs,
                                "--force",
diff --git a/e2e/install/kustomize/all_namespaces_test.go 
b/e2e/install/kustomize/all_namespaces_test.go
index 9f25ceb06..63930e8da 100644
--- a/e2e/install/kustomize/all_namespaces_test.go
+++ b/e2e/install/kustomize/all_namespaces_test.go
@@ -37,7 +37,7 @@ import (
        . "github.com/onsi/gomega"
 )
 
-func TestKustomizeDescoped(t *testing.T) {
+func TestKustomizeAllNamespaces(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
diff --git a/e2e/install/kustomize/single_namespace_test.go 
b/e2e/install/kustomize/single_namespace_test.go
index 158401a6f..d8187e051 100644
--- a/e2e/install/kustomize/single_namespace_test.go
+++ b/e2e/install/kustomize/single_namespace_test.go
@@ -47,7 +47,7 @@ func TestKustomizeSingleNamespace(t *testing.T) {
                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")
+                       g.Expect(CRDs(t)()).Should(BeNil(), "No Camel K CRDs 
should be previously installed for this test")
                        ExpectExecSucceed(t, g, Kubectl(
                                "apply",
                                "-k",
diff --git a/e2e/support/test_support.go b/e2e/support/test_support.go
index d0e9f59ef..8543e77bb 100644
--- a/e2e/support/test_support.go
+++ b/e2e/support/test_support.go
@@ -80,6 +80,7 @@ import (
 )
 
 const kubeConfigEnvVar = "KUBECONFIG"
+const registrySecret = "ck-dev-registry"
 
 var TestDefaultNamespace = "default"
 
@@ -213,7 +214,7 @@ func KamelRunWithID(t *testing.T, ctx context.Context, 
operatorID string, namesp
 
 func kamelRunWithContext(t *testing.T, ctx context.Context, operatorID string, 
namespace string, args ...string) *cobra.Command {
        if os.Getenv("E2E_TEST_REGISTRY_SECRET_COPY") == "true" {
-               args = append(args, "-t", "pull-secret.secret-name=my-registry")
+               args = append(args, "-t", 
"pull-secret.secret-name="+registrySecret)
        }
        return kamelCommandWithContext(t, ctx, "run", operatorID, namespace, 
args...)
 }
@@ -1958,12 +1959,7 @@ func WithNamedTestNamespace(t *testing.T, doRun 
func(context.Context, *gomega.Wi
 func WithExistingNamedTestNamespace(t *testing.T, doRun func(context.Context, 
*gomega.WithT, string), namespace string) {
        // Required to copy the registry secret previously set on the camel-k 
namespace
        if os.Getenv("E2E_TEST_REGISTRY_SECRET_COPY") == "true" {
-               copySecret(t, testContext, TestClient(t), "my-registry", 
"camel-k", namespace)
-       }
-       // Required to copy the registry config previously set on the camel-k 
namespace
-       // This is used by "advanced" tests which are installing the operator 
on their own
-       if os.Getenv("E2E_TEST_REGISTRY_CONFIG_COPY") == "true" {
-               copyConfigMap(t, testContext, TestClient(t), 
"camel-k-operator-configmap-configuration", "camel-k", namespace)
+               copySecret(t, testContext, TestClient(t), registrySecret, 
"camel-k", namespace)
        }
 
        invokeUserTestCode(t, testContext, namespace, doRun)
@@ -2117,12 +2113,7 @@ func NewNamedTestNamespace(t *testing.T, ctx 
context.Context, name string) ctrl.
 
        // Required to copy the registry secret previously set on the camel-k 
namespace
        if os.Getenv("E2E_TEST_REGISTRY_SECRET_COPY") == "true" {
-               copySecret(t, ctx, c, "my-registry", "camel-k", name)
-       }
-       // Required to copy the registry config previously set on the camel-k 
namespace
-       // This is used by "advanced" tests which are installing the operator 
on their own
-       if os.Getenv("E2E_TEST_REGISTRY_CONFIG_COPY") == "true" {
-               copyConfigMap(t, ctx, c, 
"camel-k-operator-configmap-configuration", "camel-k", name)
+               copySecret(t, ctx, c, registrySecret, "camel-k", name)
        }
 
        return namespaceOrProject
diff --git a/helm/camel-k/README.md b/helm/camel-k/README.md
index 2ca0f4cac..1b360a328 100644
--- a/helm/camel-k/README.md
+++ b/helm/camel-k/README.md
@@ -2,24 +2,6 @@
 
 Apache Camel K is the lightweight integration platform for Kubernetes: the 
easiest way to build and manage your Camel applications on Kubernetes. This 
chart deploys the Camel K operator and all resources needed to natively run 
Apache Camel Integrations on any Kubernetes cluster.
 
-## Prerequisites
-
-- A container image registry installed and configured for pull
-- For production environments, a registry secret containing the access to 
container registry
-
-### Minikube
-
-Minikube offers a container registry addon, which it makes very well suited 
for local Camel K development and testing purposes:
-
-```bash
-$ minikube addons enable registry
-...
-$ kubectl -n kube-system get service registry -o jsonpath='{.spec.clusterIP}'
-a.b.c.d
-```
-
-You can use the container registry ClusterIP result `a.b.c.d` as 
`REGISTRY_ADDRESS` configuration.
-
 ## Installation procedure
 
 To install the chart, first add the Camel K repository:
@@ -30,17 +12,11 @@ $ helm repo add camel-k 
https://apache.github.io/camel-k/charts
 
 ## Install the operator
 
-When installing the operator you must at least include the container registry 
to use (either the address or the service to use):
-
 ```bash
-$ helm install camel-k camel-k/camel-k --set global=true \
-  --set operator.env[0].name=REGISTRY_ADDRESS \
-  --set operator.env[0].value=<my-registry-address> \
-  --set operator.env[1].name=REGISTRY_SECRET \
-  --set operator.env[1].value=<my-registry-secret>
+$ helm install camel-k camel-k/camel-k --set global=true
 ```
 
-**Note**: if you're running a local Minikube installation, you can use the 
registry ClusterIP as `REGISTRY_ADDRESS`, skip the `REGISTRY_SECRET` and add 
`REGISTRY_INSECURE=true` environment variables.
+The procedure is installing the operator and a side default development 
container registry you can use for local development. See official 
documentation to learn how to configure a production grade container registry 
instead.
 
 ## Test your installation
 
diff --git a/helm/camel-k/templates/operator-deployment.yaml 
b/helm/camel-k/templates/operator-deployment.yaml
index 56ea2942f..9a4e8094f 100644
--- a/helm/camel-k/templates/operator-deployment.yaml
+++ b/helm/camel-k/templates/operator-deployment.yaml
@@ -20,6 +20,7 @@ kind: Deployment
 metadata:
   labels:
     app: camel-k
+    name: camel-k-operator
     camel.apache.org/component: operator
     {{- include "camel-k.labels" . | nindent 4 }}
   {{- with .Values.operator.annotations }}
@@ -64,16 +65,6 @@ spec:
               value: {{ .Values.operator.logLevel }}
             - name: OPERATOR_NAME
               value: camel-k
-
-            # Registry vars
-            - name: REGISTRY_ADDRESS
-              value: ""
-            - name: REGISTRY_SECRET
-              value: ""
-            # Set true only for testing purposes
-            - name: REGISTRY_INSECURE
-              value: "false"
-
             - name: POD_NAME
               valueFrom:
                 fieldRef:
@@ -82,6 +73,17 @@ spec:
               valueFrom:
                 fieldRef:
                   fieldPath: metadata.namespace
+            # Used to automatically enable Camel Monitor operator project 
monitoring
+            # Keep the default value or change it to the same label used by 
the project
+            # Note: remove the variable to disable the feature.
+            - name: CAMEL_MONITOR_OPERATOR_LABEL
+              value: "camel.apache.org/monitor"
+            # You can provide a different SA for builder Pods
+            - name: BUILDER_SA
+              value: "camel-k-builder"
+            # Demo only: turn it off or remove the variable in a production 
environment.
+            - name: ENABLE_DEV_REGISTRY
+              value: "true"
             - name: OPERATOR_ID
               value: {{ .Values.operator.operatorId }}
             {{- with .Values.operator.extraEnv }}
diff --git a/helm/camel-k/templates/rbacs-common.yaml 
b/helm/camel-k/templates/rbacs-common.yaml
index 41b426201..8b794597b 100644
--- a/helm/camel-k/templates/rbacs-common.yaml
+++ b/helm/camel-k/templates/rbacs-common.yaml
@@ -101,6 +101,61 @@ rules:
 ---
 apiVersion: rbac.authorization.k8s.io/v1
 kind: Role
+metadata:
+  labels:
+    app: camel-k
+  name: {{ include "camel-k.fullname" . }}-dev-registry
+rules:
+- apiGroups:
+  - ""
+  resources:
+  - secrets
+  verbs:
+  - create
+- apiGroups:
+  - ""
+  resourceNames:
+  - registry-auth
+  - registry-tls
+  - ck-dev-registry
+  resources:
+  - secrets
+  verbs:
+  - get
+  - update
+- apiGroups:
+  - apps
+  resources:
+  - deployments
+  verbs:
+  - create
+- apiGroups:
+  - apps
+  resourceNames:
+  - registry
+  resources:
+  - deployments
+  verbs:
+  - get
+  - update
+- apiGroups:
+  - ""
+  resources:
+  - services
+  verbs:
+  - create
+- apiGroups:
+  - ""
+  resourceNames:
+  - registry
+  resources:
+  - services
+  verbs:
+  - get
+  - update
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: Role
 metadata:
   labels:
     app: camel-k
@@ -145,6 +200,20 @@ subjects:
 ---
 apiVersion: rbac.authorization.k8s.io/v1
 kind: RoleBinding
+metadata:
+  labels:
+    app: camel-k
+  name: {{ include "camel-k.fullname" . }}-dev-registry
+roleRef:
+  apiGroup: rbac.authorization.k8s.io
+  kind: Role
+  name: {{ include "camel-k.fullname" . }}-dev-registry
+subjects:
+- kind: ServiceAccount
+  name: {{ include "camel-k.fullname" . }}-operator
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: RoleBinding
 metadata:
   labels:
     app: camel-k
diff --git a/pkg/cmd/operator/operator.go b/pkg/cmd/operator/operator.go
index 60b181bf8..fe116c981 100644
--- a/pkg/cmd/operator/operator.go
+++ b/pkg/cmd/operator/operator.go
@@ -66,6 +66,11 @@ import (
        logutil "github.com/apache/camel-k/v2/pkg/util/log"
 )
 
+const (
+       devRegistryselfSignedKey = "/tmp/registry.key"
+       devRegistryselfSignedCrt = "/tmp/registry.crt"
+)
+
 var log = logutil.Log.WithName("cmd")
 
 func printVersion() {
@@ -241,22 +246,7 @@ func Run(healthPort, monitoringPort int32, leaderElection 
bool, leaderElectionID
 
        devRegistryEnvVal, devReg := os.LookupEnv("ENABLE_DEV_REGISTRY")
        if devReg && devRegistryEnvVal == "true" {
-               // Only enable registry protected by secret if configured
-               devRegistrySecretEnvVal, devRegSecret := 
os.LookupEnv("ENABLE_DEV_REGISTRY_SECRET")
-               withSecret := devRegSecret && devRegistrySecretEnvVal == "true"
-               log.Info("Installing development container registry")
-               if withSecret {
-                       log.Info("NOTE: ENABLE_DEV_REGISTRY_SECRET is set, mind 
to provide a secret with the default values in the Integration namespace " +
-                               "and to pull images with it.")
-               }
-               log.Info("WARNING: the internal development container registry 
is ephemeral and not secured. " +
-                       "It MUST be considered for DEVELOPMENT and DEMO 
purposes only. Make sure to read documentation and switch to " +
-                       "a production grade container registry when moving the 
operator to a production environment.")
-               registryCtx, registryCancel := context.WithTimeout(ctx, 
1*time.Minute)
-               defer registryCancel()
-               if err := install.OperatorStartupRegistry(registryCtx, 
bootstrapClient, withSecret); err != nil {
-                       log.Error(err, "could not install the development 
container registry")
-               }
+               setupDevContainerRegistry(ctx, bootstrapClient, 
operatorNamespace)
        }
 
        log.Info("Starting the manager")
@@ -327,6 +317,42 @@ func getOperatorImage(ctx context.Context, kubeClient 
client.Client) (string, er
        )
 }
 
+// setupDevContainerRegistry is in charge to setup the development container 
registry configuration.
+func setupDevContainerRegistry(ctx context.Context, bootstrapClient 
client.Client, operatorNamespace string) {
+       // Only enable registry protected by secret if configured
+       devRegistrySecretEnvVal, devRegSecret := 
os.LookupEnv("ENABLE_DEV_REGISTRY_SECRET")
+       withSecret := devRegSecret && devRegistrySecretEnvVal == "true"
+       log.Info("Installing development container registry")
+       if withSecret {
+               log.Info("NOTE: ENABLE_DEV_REGISTRY_SECRET is set, mind to 
provide a secret with the expected default values (see docs) " +
+                       " in the Integration namespace and to pull images with 
it.")
+       }
+       log.Info("WARNING: the internal development container registry is 
ephemeral and not secured. " +
+               "It MUST be considered for DEVELOPMENT and DEMO purposes only. 
Make sure to read documentation and switch to " +
+               "a production grade container registry when moving the operator 
to a production environment.")
+       registryCtx, registryCancel := context.WithTimeout(ctx, 1*time.Minute)
+       defer registryCancel()
+       if err := install.CreateContainerRegistrySelfSignedCerts(ctx, 
devRegistryselfSignedKey, devRegistryselfSignedCrt); err != nil {
+               log.Error(err, "could not generate development container 
registry self signed certificate")
+
+               return
+       }
+       crtSecret, err := kubernetes.TLSSecretFromFiles(ctx, operatorNamespace, 
"registry-tls", devRegistryselfSignedKey, devRegistryselfSignedCrt)
+       if err != nil {
+               log.Error(err, "could not generate development container 
registry self signed certificate secret")
+
+               return
+       }
+       overrideConf, err := install.OperatorStartupRegistry(registryCtx, 
bootstrapClient, withSecret, crtSecret)
+       if err != nil {
+               log.Error(err, "could not install the development container 
registry")
+
+               return
+       }
+
+       install.OverrideRegistryConfiguration(overrideConf)
+}
+
 func exitOnError(err error, msg string) {
        if err != nil {
                log.Error(err, msg)
diff --git a/pkg/install/registry.go b/pkg/install/registry.go
index 460b6a740..1247db3de 100644
--- a/pkg/install/registry.go
+++ b/pkg/install/registry.go
@@ -34,35 +34,61 @@ import (
        metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
        "k8s.io/apimachinery/pkg/types"
        "sigs.k8s.io/yaml"
+
+       apierrors "k8s.io/apimachinery/pkg/api/errors"
+       ctrlcli "sigs.k8s.io/controller-runtime/pkg/client"
 )
 
+const serviceRegistryClusterIPTimeout = 30 * time.Second
+const serviceRegistryClusterIPPollingTime = 500 * time.Millisecond
+
+type registryConf struct {
+       clusterIP                string
+       insecure                 string
+       dockerRegistrySecretName string
+}
+
+// CreateContainerRegistrySelfSignedCerts is in charge to generate a self 
signed certificate for development
+// and internal use only.
+func CreateContainerRegistrySelfSignedCerts(ctx context.Context, key, crt 
string) error {
+       cmd := exec.CommandContext(ctx, "openssl",
+               "req", "-x509", "-newkey", "rsa:2048", "-nodes",
+               "-keyout", key,
+               "-out", crt,
+               "-days", "365",
+               "-subj", "/CN=registry",
+       )
+
+       return cmd.Run()
+}
+
 // OperatorStartupRegistry tries to install optional development container 
registry.
-func OperatorStartupRegistry(ctx context.Context, c client.Client, withSecret 
bool) error {
+func OperatorStartupRegistry(ctx context.Context, c client.Client, withSecret 
bool, crtSecret *corev1.Secret) (*registryConf, error) {
        secret, err := resources.Resource("/resources/registry/secret.yaml")
        if err != nil {
-               return fmt.Errorf("could not load development container 
registry Secret configuration: %w", err)
+               return nil, fmt.Errorf("could not load development container 
registry Secret configuration: %w", err)
        }
        var registrySecret corev1.Secret
        if err = yaml.Unmarshal(secret, &registrySecret); err != nil {
-               return fmt.Errorf("could not parse development container 
registry Secret configuration: %w", err)
+               return nil, fmt.Errorf("could not parse development container 
registry Secret configuration: %w", err)
        }
 
        service, err := resources.Resource("/resources/registry/service.yaml")
        if err != nil {
-               return fmt.Errorf("could not load development container 
registry Service configuration: %w", err)
+               return nil, fmt.Errorf("could not load development container 
registry Service configuration: %w", err)
        }
        var registryService corev1.Service
        if err = yaml.Unmarshal(service, &registryService); err != nil {
-               return fmt.Errorf("could not parse development container 
registry Service configuration: %w", err)
+               return nil, fmt.Errorf("could not parse development container 
registry Service configuration: %w", err)
        }
 
        deploy, err := resources.Resource("/resources/registry/deploy.yaml")
        if err != nil {
-               return fmt.Errorf("could not load development container 
registry Deployment configuration: %w", err)
+               return nil, fmt.Errorf("could not load development container 
registry Deployment configuration: %w", err)
        }
        var registryDeploy appsv1.Deployment
        if err = yaml.Unmarshal(deploy, &registryDeploy); err != nil {
-               return fmt.Errorf("could not parse development container 
registry Deployment configuration: %w", err)
+               return nil, fmt.Errorf("could not parse development container 
registry Deployment configuration: %w", err)
        }
 
        if withSecret && len(registryDeploy.Spec.Template.Spec.Containers) > 0 {
@@ -83,89 +109,89 @@ func OperatorStartupRegistry(ctx context.Context, c 
client.Client, withSecret bo
                        })
        }
 
+       deployNamespace := platform.GetOperatorNamespace()
        // Get owner reference to operator deployment to manage garbage 
collection
-       ref, err := getOwnerRef(ctx, c)
+       ref, err := getOwnerRef(ctx, c, "camel-k-operator", deployNamespace)
        if err != nil {
-               return fmt.Errorf("could not get operator deployment ownership: 
%w", err)
+               return nil, fmt.Errorf("could not get operator deployment 
ownership: %w", err)
        }
 
-       // create self signed certificate
-       cmd := exec.Command("openssl",
-               "req", "-x509", "-newkey", "rsa:2048", "-nodes",
-               "-keyout", "/tmp/registry.key",
-               "-out", "/tmp/registry.crt",
-               "-days", "365",
-               "-subj", "/CN=registry",
-       )
-
-       if err = cmd.Run(); err != nil {
-               return fmt.Errorf("could not generate development container 
registry self signed certificate: %w", err)
-       }
-       crtSecret, err := kubernetes.TLSSecretFromFiles(ctx, "camel-k", 
"registry-tls", "/tmp/registry.crt", "/tmp/registry.key")
-       if err != nil {
-               return fmt.Errorf("could not generate development container 
registry self signed certificate secret: %w", err)
-       }
        crtSecret.SetOwnerReferences([]metav1.OwnerReference{*ref})
-       if err := c.Create(ctx, crtSecret); err != nil {
-               return fmt.Errorf("could not create development container 
registry push secret: %w", err)
+       if err := replace(ctx, c, crtSecret); err != nil {
+               return nil, fmt.Errorf("could not create development container 
registry push secret: %w", err)
        }
 
-       registrySecret.SetNamespace("camel-k")
-       registryService.SetNamespace("camel-k")
-       registryDeploy.SetNamespace("camel-k")
+       registrySecret.SetNamespace(deployNamespace)
+       registryService.SetNamespace(deployNamespace)
+       registryDeploy.SetNamespace(deployNamespace)
        registrySecret.SetOwnerReferences([]metav1.OwnerReference{*ref})
        registryService.SetOwnerReferences([]metav1.OwnerReference{*ref})
        registryDeploy.SetOwnerReferences([]metav1.OwnerReference{*ref})
 
        // Try to create the resources now
-       if err := c.Create(ctx, &registrySecret); err != nil {
-               return fmt.Errorf("could not create development container 
registry Secret configuration: %w", err)
+       if err := replace(ctx, c, &registrySecret); err != nil {
+               return nil, fmt.Errorf("could not create development container 
registry Secret configuration: %w", err)
        }
-       if err := c.Create(ctx, &registryService); err != nil {
-               return fmt.Errorf("could not create development container 
registry Service configuration: %w", err)
+       if err := replace(ctx, c, &registryService); err != nil {
+               return nil, fmt.Errorf("could not create development container 
registry Service configuration: %w", err)
        }
-       if err := c.Create(ctx, &registryDeploy); err != nil {
-               return fmt.Errorf("could not create development container 
registry Deployment configuration: %w", err)
+       if err := replace(ctx, c, &registryDeploy); err != nil {
+               return nil, fmt.Errorf("could not create development container 
registry Deployment configuration: %w", err)
        }
 
        // Get the cluster IP and use it to configure internally the operator
-       clusterIP, err := waitForClusterIP(ctx, c, 
registryService.GetNamespace(), registryService.GetName(), 30*time.Second)
+       clusterIP, err := waitForClusterIP(ctx, c, 
registryService.GetNamespace(), registryService.GetName(), 
serviceRegistryClusterIPTimeout)
        if err != nil {
-               return fmt.Errorf("could not get development container registry 
Service IP: %w", err)
+               return nil, fmt.Errorf("could not get development container 
registry Service IP: %w", err)
        }
 
-       dockerRegistrySecret, err := kubernetes.DockerRegistrySecret(ctx, 
"camel-k", "ck-dev-registry", clusterIP, "admin", "password")
+       dockerRegistrySecret, err := kubernetes.DockerRegistrySecret(ctx, 
deployNamespace, "ck-dev-registry", clusterIP, "admin", "password")
        if err != nil {
-               return fmt.Errorf("could not generate development container 
registry push secret: %w", err)
+               return nil, fmt.Errorf("could not generate development 
container registry push secret: %w", err)
        }
        dockerRegistrySecret.SetOwnerReferences([]metav1.OwnerReference{*ref})
-       if err := c.Create(ctx, dockerRegistrySecret); err != nil {
-               return fmt.Errorf("could not create development container 
registry push secret: %w", err)
+       if err := replace(ctx, c, dockerRegistrySecret); err != nil {
+               return nil, fmt.Errorf("could not create development container 
registry push secret: %w", err)
        }
 
+       return &registryConf{
+               clusterIP:                clusterIP,
+               insecure:                 "false",
+               dockerRegistrySecretName: dockerRegistrySecret.GetName(),
+       }, nil
+}
+
+// OverrideRegistryConfiguration is in charge to change the registry 
configuration at runtime.
+func OverrideRegistryConfiguration(conf *registryConf) {
+       if conf == nil {
+               return
+       }
        log.Infof("Setting up development container registry configuration 
environment variables (registry IP %s). Notice that it overrides the operator 
configuration"+
-               " but it won't override any IntegrationProfile configuration.", 
clusterIP)
-       os.Setenv("REGISTRY_ADDRESS", clusterIP)
-       os.Setenv("REGISTRY_INSECURE", "false")
-       os.Setenv("REGISTRY_SECRET", dockerRegistrySecret.GetName())
+               " but it won't override any IntegrationProfile configuration.", 
conf.clusterIP)
+       os.Setenv("REGISTRY_ADDRESS", conf.clusterIP)
+       os.Setenv("REGISTRY_INSECURE", conf.insecure)
+       os.Setenv("REGISTRY_SECRET", conf.dockerRegistrySecretName)
        // We must reinitialize to get those values just changed in the default 
platform configuration
        platform.InitPlatform()
-
-       return nil
 }
 
-func getOwnerRef(ctx context.Context, c client.Client) 
(*metav1.OwnerReference, error) {
-       operatorDeploy := &appsv1.Deployment{}
+func getOwnerRef(ctx context.Context, c client.Client, operatorName, 
deployNamespace string) (*metav1.OwnerReference, error) {
+       var deployments appsv1.DeploymentList
 
-       err := c.Get(ctx, types.NamespacedName{
-               // TODO: change theme
-               Name:      "camel-k-operator",
-               Namespace: "camel-k",
-       }, operatorDeploy)
-       if err != nil {
+       if err := c.List(ctx, &deployments,
+               ctrlcli.InNamespace(deployNamespace),
+               ctrlcli.MatchingLabels{"name": operatorName},
+       ); err != nil {
                return nil, err
        }
 
+       if len(deployments.Items) != 1 {
+               return nil, fmt.Errorf("expected exactly one deployment with 
label name=%s in namespace %s, got %d",
+                       operatorName, deployNamespace, len(deployments.Items))
+       }
+
+       operatorDeploy := &deployments.Items[0]
+
        ownerRef := metav1.OwnerReference{
                APIVersion:         "apps/v1",
                Kind:               "Deployment",
@@ -182,7 +208,7 @@ func waitForClusterIP(ctx context.Context, c client.Client, 
namespace, name stri
        ctx, cancel := context.WithTimeout(ctx, timeout)
        defer cancel()
 
-       ticker := time.NewTicker(500 * time.Millisecond)
+       ticker := time.NewTicker(serviceRegistryClusterIPPollingTime)
        defer ticker.Stop()
 
        for {
@@ -213,3 +239,24 @@ func waitForClusterIP(ctx context.Context, c 
client.Client, namespace, name stri
                }
        }
 }
+
+func replace(ctx context.Context, c client.Client, obj ctrlcli.Object) error {
+       copied := obj.DeepCopyObject()
+
+       current, ok := copied.(ctrlcli.Object)
+       if !ok {
+               return fmt.Errorf("DeepCopyObject returned %T, expected 
controller runtime Object", copied)
+       }
+
+       err := c.Get(ctx, ctrlcli.ObjectKeyFromObject(obj), current)
+       if apierrors.IsNotFound(err) {
+               return c.Create(ctx, obj)
+       }
+       if err != nil {
+               return err
+       }
+
+       obj.SetResourceVersion(current.GetResourceVersion())
+
+       return c.Update(ctx, obj)
+}
diff --git a/pkg/install/registry_test.go b/pkg/install/registry_test.go
new file mode 100644
index 000000000..4fc39ed31
--- /dev/null
+++ b/pkg/install/registry_test.go
@@ -0,0 +1,313 @@
+/*
+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 (
+       "context"
+       "testing"
+       "time"
+
+       "github.com/apache/camel-k/v2/pkg/client"
+       "github.com/apache/camel-k/v2/pkg/internal"
+       "github.com/stretchr/testify/assert"
+       "github.com/stretchr/testify/require"
+       appsv1 "k8s.io/api/apps/v1"
+       corev1 "k8s.io/api/core/v1"
+       apierrors "k8s.io/apimachinery/pkg/api/errors"
+       metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+       "k8s.io/apimachinery/pkg/types"
+)
+
+func operatorDeployment() *appsv1.Deployment {
+       return &appsv1.Deployment{
+               ObjectMeta: metav1.ObjectMeta{
+                       Name:      "camel-k-operator",
+                       Namespace: "camel-k",
+                       UID:       types.UID("operator-uid"),
+                       Labels: map[string]string{
+                               "name": "camel-k-operator",
+                       },
+               },
+       }
+}
+
+func TestGetOwnerRef(t *testing.T) {
+       t.Run("returns owner reference", func(t *testing.T) {
+               fakeClient, err := internal.NewFakeClient(operatorDeployment())
+               require.NoError(t, err)
+
+               ref, err := getOwnerRef(
+                       context.Background(),
+                       fakeClient,
+                       "camel-k-operator",
+                       "camel-k",
+               )
+
+               require.NoError(t, err)
+               require.NotNil(t, ref)
+
+               assert.Equal(t, "apps/v1", ref.APIVersion)
+               assert.Equal(t, "Deployment", ref.Kind)
+               assert.Equal(t, "camel-k-operator", ref.Name)
+               assert.Equal(t, types.UID("operator-uid"), ref.UID)
+
+               require.NotNil(t, ref.Controller)
+               assert.True(t, *ref.Controller)
+
+               require.NotNil(t, ref.BlockOwnerDeletion)
+               assert.True(t, *ref.BlockOwnerDeletion)
+       })
+
+       t.Run("returns error when operator deployment does not exist", func(t 
*testing.T) {
+               fakeClient, err := internal.NewFakeClient()
+               require.NoError(t, err)
+
+               _, err = getOwnerRef(
+                       context.Background(),
+                       fakeClient,
+                       "camel-k-operator",
+                       "camel-k",
+               )
+
+               require.Error(t, err)
+               assert.Contains(t, err.Error(), "expected exactly one 
deployment")
+       })
+}
+
+func TestWaitForClusterIP(t *testing.T) {
+       t.Run("returns cluster IP", func(t *testing.T) {
+               service := &corev1.Service{
+                       ObjectMeta: metav1.ObjectMeta{
+                               Name:      "registry",
+                               Namespace: "camel-k",
+                       },
+                       Spec: corev1.ServiceSpec{
+                               ClusterIP: "10.96.0.20",
+                       },
+               }
+
+               fakeClient, err := internal.NewFakeClient(service)
+               require.NoError(t, err)
+
+               ip, err := waitForClusterIP(
+                       context.Background(),
+                       fakeClient,
+                       "camel-k",
+                       "registry",
+                       time.Second,
+               )
+
+               require.NoError(t, err)
+               assert.Equal(t, "10.96.0.20", ip)
+       })
+
+       t.Run("returns get error", func(t *testing.T) {
+               fakeClient, err := internal.NewFakeClient()
+               require.NoError(t, err)
+
+               _, err = waitForClusterIP(
+                       context.Background(),
+                       fakeClient,
+                       "camel-k",
+                       "registry",
+                       time.Second,
+               )
+
+               require.Error(t, err)
+               assert.True(t, apierrors.IsNotFound(err))
+       })
+
+       t.Run("times out when ClusterIP is not assigned", func(t *testing.T) {
+               service := &corev1.Service{
+                       ObjectMeta: metav1.ObjectMeta{
+                               Name:      "registry",
+                               Namespace: "camel-k",
+                       },
+               }
+
+               fakeClient, err := internal.NewFakeClient(service)
+               require.NoError(t, err)
+
+               _, err = waitForClusterIP(
+                       context.Background(),
+                       fakeClient,
+                       "camel-k",
+                       "registry",
+                       10*time.Millisecond,
+               )
+
+               require.Error(t, err)
+               assert.Contains(t, err.Error(), "timed out waiting for Service")
+       })
+
+       t.Run("times out for headless service", func(t *testing.T) {
+               service := &corev1.Service{
+                       ObjectMeta: metav1.ObjectMeta{
+                               Name:      "registry",
+                               Namespace: "camel-k",
+                       },
+                       Spec: corev1.ServiceSpec{
+                               ClusterIP: corev1.ClusterIPNone,
+                       },
+               }
+
+               fakeClient, err := internal.NewFakeClient(service)
+               require.NoError(t, err)
+
+               _, err = waitForClusterIP(
+                       context.Background(),
+                       fakeClient,
+                       "camel-k",
+                       "registry",
+                       10*time.Millisecond,
+               )
+
+               require.Error(t, err)
+               assert.Contains(t, err.Error(), "timed out waiting for Service")
+       })
+}
+
+func TestReplace(t *testing.T) {
+       ctx := context.Background()
+
+       t.Run("creates object when it does not exist", func(t *testing.T) {
+               secret := &corev1.Secret{
+                       ObjectMeta: metav1.ObjectMeta{
+                               Name:      "test-secret",
+                               Namespace: "camel-k",
+                       },
+                       Data: map[string][]byte{
+                               "foo": []byte("bar"),
+                       },
+               }
+
+               fakeClient, err := internal.NewFakeClient()
+               require.NoError(t, err)
+
+               err = replace(ctx, fakeClient, secret)
+               require.NoError(t, err)
+
+               var actual corev1.Secret
+               require.NoError(t, fakeClient.Get(ctx, types.NamespacedName{
+                       Name:      "test-secret",
+                       Namespace: "camel-k",
+               }, &actual))
+
+               assert.Equal(t, []byte("bar"), actual.Data["foo"])
+       })
+
+       t.Run("updates existing object", func(t *testing.T) {
+               existing := &corev1.Secret{
+                       ObjectMeta: metav1.ObjectMeta{
+                               Name:      "test-secret",
+                               Namespace: "camel-k",
+                       },
+                       Data: map[string][]byte{
+                               "foo": []byte("old"),
+                       },
+               }
+
+               fakeClient, err := internal.NewFakeClient(existing)
+               require.NoError(t, err)
+
+               updated := &corev1.Secret{
+                       ObjectMeta: metav1.ObjectMeta{
+                               Name:      "test-secret",
+                               Namespace: "camel-k",
+                       },
+                       Data: map[string][]byte{
+                               "foo": []byte("new"),
+                       },
+               }
+
+               err = replace(ctx, fakeClient, updated)
+               require.NoError(t, err)
+
+               var actual corev1.Secret
+               require.NoError(t, fakeClient.Get(ctx, types.NamespacedName{
+                       Name:      "test-secret",
+                       Namespace: "camel-k",
+               }, &actual))
+
+               assert.Equal(t, []byte("new"), actual.Data["foo"])
+       })
+}
+
+func TestOperatorStartupRegistry(t *testing.T) {
+       t.Setenv("NAMESPACE", "camel-k")
+
+       crtSecret := &corev1.Secret{
+               ObjectMeta: metav1.ObjectMeta{
+                       Name:      "fake-secret",
+                       Namespace: "camel-k",
+               },
+               Data: map[string][]byte{
+                       "foo": []byte("bar"),
+               },
+       }
+
+       fakeClient, err := internal.NewFakeClient(operatorDeployment(), 
crtSecret)
+       require.NoError(t, err)
+
+       // This is running in parallel, simulating the setting of an IP, when 
the Service is create by the
+       // OperatorStartupRegistry func.
+       setServiceClusterIP(context.Background(), fakeClient, "camel-k", 
"registry", "10.96.0.20")
+
+       conf, err := OperatorStartupRegistry(
+               context.Background(),
+               fakeClient,
+               false,
+               crtSecret,
+       )
+
+       require.NoError(t, err)
+       require.NotNil(t, conf)
+
+       assert.Equal(t, "10.96.0.20", conf.clusterIP)
+       assert.Equal(t, "false", conf.insecure)
+       assert.Equal(t, "ck-dev-registry", conf.dockerRegistrySecretName)
+}
+
+// Useful to simulate Kubernetes IP assignment.
+func setServiceClusterIP(ctx context.Context, c client.Client, namespace 
string, name string, clusterIP string) {
+       go func() {
+               ticker := time.NewTicker(10 * time.Millisecond)
+               defer ticker.Stop()
+
+               for {
+                       var service corev1.Service
+
+                       err := c.Get(ctx, types.NamespacedName{
+                               Namespace: namespace,
+                               Name:      name,
+                       }, &service)
+
+                       if err == nil {
+                               service.Spec.ClusterIP = clusterIP
+                               _ = c.Update(ctx, &service)
+                               return
+                       }
+
+                       select {
+                       case <-ctx.Done():
+                               return
+                       case <-ticker.C:
+                       }
+               }
+       }()
+}
diff --git a/pkg/resources/config/rbac/dev-registry-role.yaml 
b/pkg/resources/config/rbac/dev-registry-role.yaml
index deac37f64..2d937c506 100644
--- a/pkg/resources/config/rbac/dev-registry-role.yaml
+++ b/pkg/resources/config/rbac/dev-registry-role.yaml
@@ -15,6 +15,9 @@
 # limitations under the License.
 # ---------------------------------------------------------------------------
 
+# Required to setup an internal development registry. You can skip this 
configuration in a production environment.
+# Note that the resources names must match the ones expected in the code, 
which, are static.
+
 kind: Role
 apiVersion: rbac.authorization.k8s.io/v1
 metadata:
@@ -28,3 +31,44 @@ rules:
   - secrets
   verbs:
   - create
+- apiGroups:
+    - ""
+  resources:
+    - secrets
+  verbs:
+    - get
+    - update
+  resourceNames:
+    - registry-auth
+    - registry-tls
+    - ck-dev-registry
+- apiGroups:
+  - apps
+  resources:
+  - deployments
+  verbs:
+  - create
+- apiGroups:
+  - apps
+  resources:
+  - deployments
+  verbs:
+    - get
+    - update
+  resourceNames:
+    - registry
+- apiGroups:
+  - ""
+  resources:
+  - services
+  verbs:
+  - create
+- apiGroups:
+  - ""
+  resources:
+  - services
+  verbs:
+    - get
+    - update
+  resourceNames:
+    - registry
diff --git a/pkg/util/kubernetes/docker_secret.go 
b/pkg/util/kubernetes/docker_secret.go
index 56b48ff18..4d4d81e49 100644
--- a/pkg/util/kubernetes/docker_secret.go
+++ b/pkg/util/kubernetes/docker_secret.go
@@ -26,7 +26,6 @@ import (
        "os"
 
        corev1 "k8s.io/api/core/v1"
-       metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 )
 
 type dockerConfigJSON struct {
@@ -50,6 +49,7 @@ func buildDockerConfigJSON(server, username, password string) 
([]byte, error) {
                        },
                },
        }
+
        return json.Marshal(cfg)
 }
 
@@ -61,11 +61,9 @@ func DockerRegistrySecret(ctx context.Context, namespace, 
name, server, username
        }
 
        secret := &corev1.Secret{
-               ObjectMeta: metav1.ObjectMeta{
-                       Name:      name,
-                       Namespace: namespace,
-               },
-               Type: corev1.SecretTypeDockerConfigJson,
+               Name:      name,
+               Namespace: namespace,
+               Type:      corev1.SecretTypeDockerConfigJson,
                Data: map[string][]byte{
                        corev1.DockerConfigJsonKey: dockerCfgJSON,
                },
@@ -77,7 +75,7 @@ func DockerRegistrySecret(ctx context.Context, namespace, 
name, server, username
 // TLSSecretFromFiles mirrors `kubectl create secret tls --cert=<path> 
--key=<path>`,
 // reading the cert/key from disk and validating them the same way kubectl does
 // (tls.X509KeyPair) before submitting to the API.
-func TLSSecretFromFiles(ctx context.Context, namespace, name string, certPath, 
keyPath string) (*corev1.Secret, error) {
+func TLSSecretFromFiles(ctx context.Context, namespace, name string, keyPath, 
certPath string) (*corev1.Secret, error) {
        certData, err := os.ReadFile(certPath)
        if err != nil {
                return nil, fmt.Errorf("reading cert file %q: %w", certPath, 
err)
@@ -95,11 +93,9 @@ func TLSSecretFromFiles(ctx context.Context, namespace, name 
string, certPath, k
        }
 
        secret := &corev1.Secret{
-               ObjectMeta: metav1.ObjectMeta{
-                       Name:      name,
-                       Namespace: namespace,
-               },
-               Type: corev1.SecretTypeTLS,
+               Name:      name,
+               Namespace: namespace,
+               Type:      corev1.SecretTypeTLS,
                Data: map[string][]byte{
                        corev1.TLSCertKey:       certData,
                        corev1.TLSPrivateKeyKey: keyData,

Reply via email to