You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by pc...@apache.org on 2026/10/01 12:56:34 UTC
(camel-k) branch main updated (7820173ac -> 5b4871787)
This is an automated email from the ASF dual-hosted git repository.
squakez pushed a change to branch main
in repository https://gitbox.apache.org/repos/asf/camel-k.git
from 7820173ac chore: expand Strimzi binding test coverage
new 06366afac feat(install): support development container registry
new 5b4871787 feat(ci): use dev container registry
The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails. The revisions
listed as "add" were already present in the repository and have only
been added to this reference.
Summary of changes:
.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 | 46 +++
pkg/install/registry.go | 262 +++++++++++++++++
pkg/install/registry_test.go | 313 +++++++++++++++++++++
.../config/manager/operator-deployment.yaml | 3 +
...binding.yaml => dev-registry-role-binding.yaml} | 4 +-
...or-role-knative.yaml => dev-registry-role.yaml} | 67 +++--
pkg/resources/config/rbac/kustomization.yaml | 2 +
.../resources/resources/registry/deploy.yaml | 52 +---
.../resources/resources/registry/secret.yaml | 8 +-
.../registry/service.yaml} | 18 +-
pkg/util/kubernetes/docker_secret.go | 106 +++++++
33 files changed, 952 insertions(+), 293 deletions(-)
create mode 100644 pkg/install/registry.go
create mode 100644 pkg/install/registry_test.go
copy pkg/resources/config/rbac/{namespaced/operator-role-binding.yaml => dev-registry-role-binding.yaml} (95%)
copy pkg/resources/config/rbac/{namespaced/operator-role-knative.yaml => dev-registry-role.yaml} (67%)
copy .github/actions/registry-setting/registry.yaml => pkg/resources/resources/registry/deploy.yaml (73%)
copy e2e/common/traits/cross-ns/sa.yaml => pkg/resources/resources/registry/secret.yaml (86%)
copy pkg/resources/{config/manager/builder-service-account.yaml => resources/registry/service.yaml} (82%)
create mode 100644 pkg/util/kubernetes/docker_secret.go
(camel-k) 02/02: feat(ci): use dev container registry
Posted by pc...@apache.org.
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 <pa...@gmail.com>
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, ®istrySecret); 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, ®istryService); 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, ®istryDeploy); 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, ®istrySecret); err != nil {
- return fmt.Errorf("could not create development container registry Secret configuration: %w", err)
+ if err := replace(ctx, c, ®istrySecret); err != nil {
+ return nil, fmt.Errorf("could not create development container registry Secret configuration: %w", err)
}
- if err := c.Create(ctx, ®istryService); err != nil {
- return fmt.Errorf("could not create development container registry Service configuration: %w", err)
+ if err := replace(ctx, c, ®istryService); err != nil {
+ return nil, fmt.Errorf("could not create development container registry Service configuration: %w", err)
}
- if err := c.Create(ctx, ®istryDeploy); err != nil {
- return fmt.Errorf("could not create development container registry Deployment configuration: %w", err)
+ if err := replace(ctx, c, ®istryDeploy); 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 ®istryConf{
+ 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,
(camel-k) 01/02: feat(install): support development container registry
Posted by pc...@apache.org.
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 06366afacb4a02ce7ec2fb3e2816740469fc54e2
Author: Pasquale Congiusti <pa...@gmail.com>
AuthorDate: Sat Sep 19 12:50:27 2026 +0200
feat(install): support development container registry
Closes #6758
---
pkg/cmd/operator/operator.go | 20 ++
pkg/install/registry.go | 215 +++++++++++++++++++++
.../config/manager/operator-deployment.yaml | 3 +
...ization.yaml => dev-registry-role-binding.yaml} | 26 +--
.../{kustomization.yaml => dev-registry-role.yaml} | 26 +--
pkg/resources/config/rbac/kustomization.yaml | 2 +
pkg/resources/resources/registry/deploy.yaml | 81 ++++++++
.../registry/secret.yaml} | 21 +-
.../registry/service.yaml} | 29 +--
pkg/util/kubernetes/docker_secret.go | 110 +++++++++++
10 files changed, 481 insertions(+), 52 deletions(-)
diff --git a/pkg/cmd/operator/operator.go b/pkg/cmd/operator/operator.go
index 2d34ea496..60b181bf8 100644
--- a/pkg/cmd/operator/operator.go
+++ b/pkg/cmd/operator/operator.go
@@ -239,6 +239,26 @@ func Run(healthPort, monitoringPort int32, leaderElection bool, leaderElectionID
defer installCancel()
install.OperatorStartupOptionalTools(installCtx, bootstrapClient, log)
+ 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")
+ }
+ }
+
log.Info("Starting the manager")
exitOnError(mgr.Start(ctx), "manager exited non-zero")
}
diff --git a/pkg/install/registry.go b/pkg/install/registry.go
new file mode 100644
index 000000000..460b6a740
--- /dev/null
+++ b/pkg/install/registry.go
@@ -0,0 +1,215 @@
+/*
+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"
+ "fmt"
+ "os"
+ "os/exec"
+ "time"
+
+ "github.com/apache/camel-k/v2/pkg/client"
+ "github.com/apache/camel-k/v2/pkg/platform"
+ "github.com/apache/camel-k/v2/pkg/resources"
+ "github.com/apache/camel-k/v2/pkg/util/kubernetes"
+ "github.com/apache/camel-k/v2/pkg/util/log"
+ appsv1 "k8s.io/api/apps/v1"
+ corev1 "k8s.io/api/core/v1"
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ "k8s.io/apimachinery/pkg/types"
+ "sigs.k8s.io/yaml"
+)
+
+// OperatorStartupRegistry tries to install optional development container registry.
+func OperatorStartupRegistry(ctx context.Context, c client.Client, withSecret bool) 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)
+ }
+ var registrySecret corev1.Secret
+ if err = yaml.Unmarshal(secret, ®istrySecret); err != nil {
+ return 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)
+ }
+ var registryService corev1.Service
+ if err = yaml.Unmarshal(service, ®istryService); err != nil {
+ return 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)
+ }
+ var registryDeploy appsv1.Deployment
+ if err = yaml.Unmarshal(deploy, ®istryDeploy); err != nil {
+ return fmt.Errorf("could not parse development container registry Deployment configuration: %w", err)
+ }
+
+ if withSecret && len(registryDeploy.Spec.Template.Spec.Containers) > 0 {
+ registryDeploy.Spec.Template.Spec.Containers[0].Env =
+ append(registryDeploy.Spec.Template.Spec.Containers[0].Env, corev1.EnvVar{
+ Name: "REGISTRY_AUTH",
+ Value: "htpasswd",
+ })
+ registryDeploy.Spec.Template.Spec.Containers[0].Env =
+ append(registryDeploy.Spec.Template.Spec.Containers[0].Env, corev1.EnvVar{
+ Name: "REGISTRY_AUTH_HTPASSWD_REALM",
+ Value: "Registry Realm",
+ })
+ registryDeploy.Spec.Template.Spec.Containers[0].Env =
+ append(registryDeploy.Spec.Template.Spec.Containers[0].Env, corev1.EnvVar{
+ Name: "REGISTRY_AUTH_HTPASSWD_PATH",
+ Value: "/auth/htpasswd",
+ })
+ }
+
+ // Get owner reference to operator deployment to manage garbage collection
+ ref, err := getOwnerRef(ctx, c)
+ if err != nil {
+ return 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)
+ }
+
+ registrySecret.SetNamespace("camel-k")
+ registryService.SetNamespace("camel-k")
+ registryDeploy.SetNamespace("camel-k")
+ 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, ®istrySecret); err != nil {
+ return fmt.Errorf("could not create development container registry Secret configuration: %w", err)
+ }
+ if err := c.Create(ctx, ®istryService); err != nil {
+ return fmt.Errorf("could not create development container registry Service configuration: %w", err)
+ }
+ if err := c.Create(ctx, ®istryDeploy); err != nil {
+ return 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)
+ if err != nil {
+ return 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")
+ if err != nil {
+ return 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)
+ }
+
+ 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())
+ // 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{}
+
+ err := c.Get(ctx, types.NamespacedName{
+ // TODO: change theme
+ Name: "camel-k-operator",
+ Namespace: "camel-k",
+ }, operatorDeploy)
+ if err != nil {
+ return nil, err
+ }
+
+ ownerRef := metav1.OwnerReference{
+ APIVersion: "apps/v1",
+ Kind: "Deployment",
+ Name: operatorDeploy.Name,
+ UID: operatorDeploy.UID,
+ Controller: new(true),
+ BlockOwnerDeletion: new(true),
+ }
+
+ return &ownerRef, nil
+}
+
+func waitForClusterIP(ctx context.Context, c client.Client, namespace, name string, timeout time.Duration) (string, error) {
+ ctx, cancel := context.WithTimeout(ctx, timeout)
+ defer cancel()
+
+ ticker := time.NewTicker(500 * time.Millisecond)
+ defer ticker.Stop()
+
+ for {
+ var svc corev1.Service
+
+ err := c.Get(ctx, types.NamespacedName{
+ Namespace: namespace,
+ Name: name,
+ }, &svc)
+ if err != nil {
+ return "", err
+ }
+
+ if svc.Spec.ClusterIP != "" &&
+ svc.Spec.ClusterIP != corev1.ClusterIPNone {
+ return svc.Spec.ClusterIP, nil
+ }
+
+ select {
+ case <-ctx.Done():
+ return "", fmt.Errorf(
+ "timed out waiting for Service %s/%s to get a ClusterIP: %w",
+ namespace,
+ name,
+ ctx.Err(),
+ )
+ case <-ticker.C:
+ }
+ }
+}
diff --git a/pkg/resources/config/manager/operator-deployment.yaml b/pkg/resources/config/manager/operator-deployment.yaml
index 771d21d53..548b84999 100644
--- a/pkg/resources/config/manager/operator-deployment.yaml
+++ b/pkg/resources/config/manager/operator-deployment.yaml
@@ -81,6 +81,9 @@ spec:
# 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"
# Attempt to read bootstrap configuration from configmap or secret
envFrom:
- configMapRef:
diff --git a/pkg/resources/config/rbac/kustomization.yaml b/pkg/resources/config/rbac/dev-registry-role-binding.yaml
similarity index 74%
copy from pkg/resources/config/rbac/kustomization.yaml
copy to pkg/resources/config/rbac/dev-registry-role-binding.yaml
index d6a983f2c..61a32dc31 100644
--- a/pkg/resources/config/rbac/kustomization.yaml
+++ b/pkg/resources/config/rbac/dev-registry-role-binding.yaml
@@ -15,16 +15,16 @@
# limitations under the License.
# ---------------------------------------------------------------------------
-#
-# rbac resources applicable for all kubernetes platforms
-#
-apiVersion: kustomize.config.k8s.io/v1beta1
-kind: Kustomization
-
-resources:
-- builder-role.yaml
-- builder-role-openshift.yaml
-- kamelets-viewer-role.yaml
-- builder-role-binding.yaml
-- builder-role-binding-openshift.yaml
-- kamelets-viewer-role-binding.yaml
+kind: RoleBinding
+apiVersion: rbac.authorization.k8s.io/v1
+metadata:
+ name: camel-k-dev-registry
+ labels:
+ app: "camel-k"
+subjects:
+- kind: ServiceAccount
+ name: camel-k-operator
+roleRef:
+ kind: Role
+ name: camel-k-dev-registry
+ apiGroup: rbac.authorization.k8s.io
diff --git a/pkg/resources/config/rbac/kustomization.yaml b/pkg/resources/config/rbac/dev-registry-role.yaml
similarity index 74%
copy from pkg/resources/config/rbac/kustomization.yaml
copy to pkg/resources/config/rbac/dev-registry-role.yaml
index d6a983f2c..deac37f64 100644
--- a/pkg/resources/config/rbac/kustomization.yaml
+++ b/pkg/resources/config/rbac/dev-registry-role.yaml
@@ -15,16 +15,16 @@
# limitations under the License.
# ---------------------------------------------------------------------------
-#
-# rbac resources applicable for all kubernetes platforms
-#
-apiVersion: kustomize.config.k8s.io/v1beta1
-kind: Kustomization
-
-resources:
-- builder-role.yaml
-- builder-role-openshift.yaml
-- kamelets-viewer-role.yaml
-- builder-role-binding.yaml
-- builder-role-binding-openshift.yaml
-- kamelets-viewer-role-binding.yaml
+kind: Role
+apiVersion: rbac.authorization.k8s.io/v1
+metadata:
+ name: camel-k-dev-registry
+ labels:
+ app: "camel-k"
+rules:
+- apiGroups:
+ - ""
+ resources:
+ - secrets
+ verbs:
+ - create
diff --git a/pkg/resources/config/rbac/kustomization.yaml b/pkg/resources/config/rbac/kustomization.yaml
index d6a983f2c..629776651 100644
--- a/pkg/resources/config/rbac/kustomization.yaml
+++ b/pkg/resources/config/rbac/kustomization.yaml
@@ -24,7 +24,9 @@ kind: Kustomization
resources:
- builder-role.yaml
- builder-role-openshift.yaml
+- dev-registry-role.yaml
- kamelets-viewer-role.yaml
- builder-role-binding.yaml
- builder-role-binding-openshift.yaml
+- dev-registry-role-binding.yaml
- kamelets-viewer-role-binding.yaml
diff --git a/pkg/resources/resources/registry/deploy.yaml b/pkg/resources/resources/registry/deploy.yaml
new file mode 100644
index 000000000..51d8e61d1
--- /dev/null
+++ b/pkg/resources/resources/registry/deploy.yaml
@@ -0,0 +1,81 @@
+# ---------------------------------------------------------------------------
+# 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: registry
+spec:
+ selector:
+ matchLabels:
+ app: registry
+ template:
+ metadata:
+ labels:
+ app: registry
+ spec:
+ containers:
+ - name: registry
+ image: registry:3
+ env:
+ - name: REGISTRY_HTTP_ADDR
+ value: 0.0.0.0:443
+ - name: REGISTRY_HTTP_TLS_CERTIFICATE
+ value: /certs/tls.crt
+ - name: REGISTRY_HTTP_TLS_KEY
+ value: /certs/tls.key
+ - name: REGISTRY_STORAGE_FILESYSTEM_ROOTDIRECTORY
+ value: /registry-data
+ # Required to include a secret credential
+ # The operator will be in charge to turn them on if
+ # configured
+ # - name: REGISTRY_AUTH
+ # value: htpasswd
+ # - name: REGISTRY_AUTH_HTPASSWD_REALM
+ # value: Registry Realm
+ # - name: REGISTRY_AUTH_HTPASSWD_PATH
+ # value: /auth/htpasswd
+ ports:
+ - containerPort: 5000
+ name: registry
+ - containerPort: 443
+ name: https
+ volumeMounts:
+ - name: registry-data
+ mountPath: /registry-data
+ - name: registry-auth
+ mountPath: /auth
+ readOnly: true
+ - name: registry-tls
+ mountPath: /certs
+ readOnly: true
+ resources:
+ requests:
+ cpu: 100m
+ memory: 128Mi
+ limits:
+ cpu: 500m
+ memory: 512Mi
+ volumes:
+ - name: registry-data
+ emptyDir: {}
+ - name: registry-auth
+ secret:
+ secretName: registry-auth
+ - name: registry-tls
+ secret:
+ secretName: registry-tls
diff --git a/pkg/resources/config/rbac/kustomization.yaml b/pkg/resources/resources/registry/secret.yaml
similarity index 74%
copy from pkg/resources/config/rbac/kustomization.yaml
copy to pkg/resources/resources/registry/secret.yaml
index d6a983f2c..e2c414b18 100644
--- a/pkg/resources/config/rbac/kustomization.yaml
+++ b/pkg/resources/resources/registry/secret.yaml
@@ -15,16 +15,11 @@
# limitations under the License.
# ---------------------------------------------------------------------------
-#
-# rbac resources applicable for all kubernetes platforms
-#
-apiVersion: kustomize.config.k8s.io/v1beta1
-kind: Kustomization
-
-resources:
-- builder-role.yaml
-- builder-role-openshift.yaml
-- kamelets-viewer-role.yaml
-- builder-role-binding.yaml
-- builder-role-binding-openshift.yaml
-- kamelets-viewer-role-binding.yaml
+apiVersion: v1
+kind: Secret
+metadata:
+ name: registry-auth
+type: Opaque
+stringData:
+ htpasswd: |
+ admin:$2y$05$b8N7UpPbxfFRppWxO5bEXOrKdryg/DUPpQ9xXpKPsGKikeAMzrSHe
diff --git a/pkg/resources/config/rbac/kustomization.yaml b/pkg/resources/resources/registry/service.yaml
similarity index 74%
copy from pkg/resources/config/rbac/kustomization.yaml
copy to pkg/resources/resources/registry/service.yaml
index d6a983f2c..a28da8131 100644
--- a/pkg/resources/config/rbac/kustomization.yaml
+++ b/pkg/resources/resources/registry/service.yaml
@@ -15,16 +15,19 @@
# limitations under the License.
# ---------------------------------------------------------------------------
-#
-# rbac resources applicable for all kubernetes platforms
-#
-apiVersion: kustomize.config.k8s.io/v1beta1
-kind: Kustomization
-
-resources:
-- builder-role.yaml
-- builder-role-openshift.yaml
-- kamelets-viewer-role.yaml
-- builder-role-binding.yaml
-- builder-role-binding-openshift.yaml
-- kamelets-viewer-role-binding.yaml
+apiVersion: v1
+kind: Service
+metadata:
+ name: registry
+spec:
+ selector:
+ app: registry
+ ports:
+ - name: http
+ port: 80
+ protocol: TCP
+ targetPort: 5000
+ - name: https
+ port: 443
+ protocol: TCP
+ targetPort: 443
diff --git a/pkg/util/kubernetes/docker_secret.go b/pkg/util/kubernetes/docker_secret.go
new file mode 100644
index 000000000..56b48ff18
--- /dev/null
+++ b/pkg/util/kubernetes/docker_secret.go
@@ -0,0 +1,110 @@
+/*
+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 kubernetes
+
+import (
+ "context"
+ "crypto/tls"
+ "encoding/base64"
+ "encoding/json"
+ "fmt"
+ "os"
+
+ corev1 "k8s.io/api/core/v1"
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+)
+
+type dockerConfigJSON struct {
+ Auths map[string]dockerConfigEntry `json:"auths"`
+}
+
+type dockerConfigEntry struct {
+ Username string `json:"username"`
+ Password string `json:"password"`
+ Auth string `json:"auth"`
+}
+
+func buildDockerConfigJSON(server, username, password string) ([]byte, error) {
+ auth := base64.StdEncoding.EncodeToString([]byte(username + ":" + password))
+ cfg := dockerConfigJSON{
+ Auths: map[string]dockerConfigEntry{
+ server: {
+ Username: username,
+ Password: password,
+ Auth: auth,
+ },
+ },
+ }
+ return json.Marshal(cfg)
+}
+
+// DockerRegistrySecret returns a kubernetes.io/dockerconfigjson secret.
+func DockerRegistrySecret(ctx context.Context, namespace, name, server, username, password string) (*corev1.Secret, error) {
+ dockerCfgJSON, err := buildDockerConfigJSON(server, username, password)
+ if err != nil {
+ return nil, fmt.Errorf("building docker config json: %w", err)
+ }
+
+ secret := &corev1.Secret{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: name,
+ Namespace: namespace,
+ },
+ Type: corev1.SecretTypeDockerConfigJson,
+ Data: map[string][]byte{
+ corev1.DockerConfigJsonKey: dockerCfgJSON,
+ },
+ }
+
+ return secret, nil
+}
+
+// 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) {
+ certData, err := os.ReadFile(certPath)
+ if err != nil {
+ return nil, fmt.Errorf("reading cert file %q: %w", certPath, err)
+ }
+
+ keyData, err := os.ReadFile(keyPath)
+ if err != nil {
+ return nil, fmt.Errorf("reading key file %q: %w", keyPath, err)
+ }
+
+ // Same validation kubectl performs: parses PEM blocks and confirms the
+ // private key matches the certificate's public key.
+ if _, err := tls.X509KeyPair(certData, keyData); err != nil {
+ return nil, fmt.Errorf("failed to load key pair: %w", err)
+ }
+
+ secret := &corev1.Secret{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: name,
+ Namespace: namespace,
+ },
+ Type: corev1.SecretTypeTLS,
+ Data: map[string][]byte{
+ corev1.TLSCertKey: certData,
+ corev1.TLSPrivateKeyKey: keyData,
+ },
+ }
+
+ return secret, nil
+}