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
The following commit(s) were added to refs/heads/main by this push:
new 779163ef6 feat: I expose CamelMonitor availability on Integrations
(#6902)
779163ef6 is described below
commit 779163ef6c4e76106f50c1ca159fcb286c9ed14a
Author: VĂctor Jean Pierre Mandujano Gutierrez <[email protected]>
AuthorDate: Wed Nov 11 10:50:11 2026 -0500
feat: I expose CamelMonitor availability on Integrations (#6902)
* Fix #6898: I report associated CamelMonitor resources
I add an informational condition and kubectl column to show whether
an Integration has an associated CamelMonitor. I verify direct ownership
or the bounded Deployment/CronJob chain by namespace, name and UID.
I reuse unstructured lookup and optional watches to keep the discovery
condition current without changing runtime readiness. I add focused
regression tests, minimal read permissions and regenerated CRD/Helm assets.
Fixes #6898
* Fix #6898: I update discovery through the monitor action
I let CamelMonitor events use normal reconciliation and update the
availability condition from the existing monitor action. I remove the
separate status patch and fixed retry from the reconciler.
I clear the condition when the association disappears and keep lookup
errors from interrupting normal runtime monitoring. I move the kubectl
column last and update the regression tests and documentation.
Fixes #6898
* Fix #6898: I simplify monitor discovery with watches and labels
* Fix #6898: I check monitor availability during reconciliation
* Fix #6898: I cover CamelMonitor discovery with an e2e workflow
* Fix #6898: I reconcile monitors after source changes
* Fix #6898: I simplify CamelMonitor discovery
---
.github/workflows/camel-monitor.yml | 89 +++++++++++
docs/modules/ROOT/pages/contributing/e2e.adoc | 1 +
.../ROOT/pages/observability/dashboard.adoc | 5 +
e2e/camelmonitor/camel_monitor_test.go | 41 +++++
e2e/camelmonitor/files/timer.yaml | 25 +++
go.mod | 22 +--
go.sum | 60 +++----
helm/camel-k/crds/camel-k-crds.yaml | 4 +
helm/camel-k/templates/rbacs-descoped.yaml | 8 +
helm/camel-k/templates/rbacs-namespaced.yaml | 8 +
pkg/apis/addtoscheme_camel_monitor.go | 24 +++
pkg/apis/camel/v1/integration_types.go | 3 +
pkg/controller/integration/camel_monitor.go | 46 ++++++
pkg/controller/integration/camel_monitor_test.go | 177 +++++++++++++++++++++
.../integration/integration_controller.go | 37 ++++-
.../integration/integration_controller_test.go | 2 +-
pkg/controller/integration/monitor.go | 12 +-
pkg/controller/integration/predicate_test.go | 19 +++
.../crd/bases/camel.apache.org_integrations.yaml | 4 +
.../rbac/descoped/operator-cluster-role.yaml | 8 +
.../config/rbac/namespaced/operator-role.yaml | 8 +
script/Makefile | 9 ++
22 files changed, 559 insertions(+), 53 deletions(-)
diff --git a/.github/workflows/camel-monitor.yml
b/.github/workflows/camel-monitor.yml
new file mode 100644
index 000000000..321a101ca
--- /dev/null
+++ b/.github/workflows/camel-monitor.yml
@@ -0,0 +1,89 @@
+# ---------------------------------------------------------------------------
+# 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.
+# ---------------------------------------------------------------------------
+
+name: camel-monitor
+
+env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ CAMEL_K_TEST_TIMEOUT_SHORT: 2m
+ CAMEL_K_TEST_TIMEOUT_MEDIUM: 5m
+ CAMEL_K_TEST_TIMEOUT_LONG: 10m
+
+on:
+ pull_request:
+ branches:
+ - main
+ - "release-*"
+ paths-ignore:
+ - 'docs/**'
+ - 'java/**'
+ - 'proposals/**'
+ - '**.adoc'
+ - '**.md'
+ - 'KEYS'
+ - 'LICENSE'
+ - 'NOTICE'
+ push:
+ branches:
+ - main
+ - "release-*"
+ paths-ignore:
+ - 'docs/**'
+ - 'java/**'
+ - 'proposals/**'
+ - '**.adoc'
+ - '**.md'
+ - 'KEYS'
+ - 'LICENSE'
+ - 'NOTICE'
+ workflow_dispatch:
+
+concurrency:
+ group: ${{ github.workflow }}-${{ github.event.pull_request.number ||
github.sha }}
+ cancel-in-progress: true
+
+jobs:
+ camel-monitor:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v7
+ with:
+ persist-credentials: false
+ submodules: recursive
+
+ - name: Infra setting
+ uses: ./.github/actions/infra-setting
+
+ - name: Install Camel Monitor operator
+ shell: bash
+ run: |
+ kubectl create ns camel
+ kubectl apply -k
github.com/camel-tooling/camel-monitor-operator/install/overlays/all-namespaces?ref=v1.0.0
--server-side
+ kubectl wait --for=condition=Established
crd/camelmonitors.camel.apache.org --timeout=60s
+ kubectl wait --for=condition=available
deployment/camel-monitor-operator -n camel --timeout=60s
+
+ - name: Install Camel K 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 --timeout=60s
+
+ - name: Run test
+ shell: bash
+ run: |
+ DO_TEST_PREBUILD=false GOTESTFMT="-json 2>&1 | gotestfmt" make
test-camel-monitor
diff --git a/docs/modules/ROOT/pages/contributing/e2e.adoc
b/docs/modules/ROOT/pages/contributing/e2e.adoc
index 82ce1697e..bcecad5d3 100644
--- a/docs/modules/ROOT/pages/contributing/e2e.adoc
+++ b/docs/modules/ROOT/pages/contributing/e2e.adoc
@@ -34,6 +34,7 @@ This is the list of the groups we are using (please, notice
that they can slight
* advanced (`make test-advanced`)
* install (`make test-install`)
* knative (`make test-knative`)
+ * camelmonitor (`make test-camel-monitor`)
* native (`make test-quarkus-native`)
* telemetry (`make test-telemetry`)
diff --git a/docs/modules/ROOT/pages/observability/dashboard.adoc
b/docs/modules/ROOT/pages/observability/dashboard.adoc
index 374e92527..4da29436d 100644
--- a/docs/modules/ROOT/pages/observability/dashboard.adoc
+++ b/docs/modules/ROOT/pages/observability/dashboard.adoc
@@ -97,3 +97,8 @@ items:
NOTE: Camel K won't require the availability of Camel Monitor operator to
work, neither Camel Monitor operator would require Camel K. They are
independent projects which can cooperate to provide a Build and Monitoring tool
respectively.
Learn more by reading the official
https://camel-tooling.github.io/camel-dashboard/[Camel Dashboard documentation].
+
+== Discover monitoring from an Integration
+
+The `CamelMonitorAvailable` condition and `Camel Monitor` column indicate when
a
+`CamelMonitor` with the Integration's name is found in its namespace.
diff --git a/e2e/camelmonitor/camel_monitor_test.go
b/e2e/camelmonitor/camel_monitor_test.go
new file mode 100644
index 000000000..7036fa3f0
--- /dev/null
+++ b/e2e/camelmonitor/camel_monitor_test.go
@@ -0,0 +1,41 @@
+//go:build integration
+// +build integration
+
+/*
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements. See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package camelmonitor
+
+import (
+ "context"
+ "testing"
+
+ . "github.com/onsi/gomega"
+ corev1 "k8s.io/api/core/v1"
+
+ . "github.com/apache/camel-k/v2/e2e/support"
+ v1 "github.com/apache/camel-k/v2/pkg/apis/camel/v1"
+)
+
+func TestCamelMonitorDiscovery(t *testing.T) {
+ WithNewTestNamespace(t, func(ctx context.Context, g *WithT, ns string) {
+ name := "monitored-integration"
+ g.Expect(KamelRun(t, ctx, ns, "files/timer.yaml", "--name",
name).Execute()).To(Succeed())
+ g.Eventually(IntegrationConditionStatus(t, ctx, ns, name,
v1.IntegrationConditionCamelMonitorAvailable), TestTimeoutLong).
+ Should(Equal(corev1.ConditionTrue))
+ })
+}
diff --git a/e2e/camelmonitor/files/timer.yaml
b/e2e/camelmonitor/files/timer.yaml
new file mode 100644
index 000000000..16069f3f5
--- /dev/null
+++ b/e2e/camelmonitor/files/timer.yaml
@@ -0,0 +1,25 @@
+# ---------------------------------------------------------------------------
+# 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.
+# ---------------------------------------------------------------------------
+
+- from:
+ uri: "timer:monitored"
+ parameters:
+ period: "5000"
+ steps:
+ - setBody:
+ constant: "Monitoring discovery test"
+ - to: "log:info"
diff --git a/go.mod b/go.mod
index 38831e4b4..09dd7af0f 100644
--- a/go.mod
+++ b/go.mod
@@ -5,6 +5,7 @@ go 1.27.3
require (
dario.cat/mergo v1.0.2
github.com/Masterminds/semver v1.5.0
+ github.com/camel-tooling/camel-monitor-operator v1.0.0
github.com/evanphx/json-patch v5.9.11+incompatible
github.com/gertd/go-pluralize v0.2.1
github.com/go-git/go-git/v5 v5.19.3
@@ -17,7 +18,7 @@ require (
github.com/mattn/go-shellwords v1.0.16
github.com/onsi/gomega v1.44.0
// go get github.com/openshift/[email protected]
- github.com/openshift/api v0.0.0-20250820105013-6282350d0c39
+ github.com/openshift/api v0.0.0-20260708103422-b1cc68a860b3
github.com/operator-framework/api v0.46.0
github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring
v0.95.1
github.com/prometheus/client_golang v1.25.0
@@ -74,17 +75,16 @@ require (
github.com/go-openapi/jsonreference v1.0.0 // indirect
github.com/go-openapi/swag v0.27.1 // indirect
github.com/go-openapi/swag/cmdutils v0.27.1 // indirect
- github.com/go-openapi/swag/conv v0.27.1 // indirect
- github.com/go-openapi/swag/fileutils v0.27.1 // indirect
- github.com/go-openapi/swag/jsonutils v0.27.1 // indirect
- github.com/go-openapi/swag/loading v0.27.1 // indirect
- github.com/go-openapi/swag/mangling v0.27.1 // indirect
+ github.com/go-openapi/swag/conv v0.27.3 // indirect
+ github.com/go-openapi/swag/fileutils v0.27.3 // indirect
+ github.com/go-openapi/swag/jsonutils v0.27.3 // indirect
+ github.com/go-openapi/swag/loading v0.27.3 // indirect
+ github.com/go-openapi/swag/mangling v0.27.3 // indirect
github.com/go-openapi/swag/netutils v0.27.1 // indirect
- github.com/go-openapi/swag/pools v0.27.1 // indirect
- github.com/go-openapi/swag/stringutils v0.27.1 // indirect
- github.com/go-openapi/swag/typeutils v0.27.1 // indirect
- github.com/go-openapi/swag/yamlutils v0.27.1 // indirect
- github.com/gogo/protobuf v1.3.2 // indirect
+ github.com/go-openapi/swag/pools v0.27.3 // indirect
+ github.com/go-openapi/swag/stringutils v0.27.3 // indirect
+ github.com/go-openapi/swag/typeutils v0.27.3 // indirect
+ github.com/go-openapi/swag/yamlutils v0.27.3 // indirect
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 //
indirect
github.com/google/gnostic-models v0.7.1 // indirect
github.com/google/go-cmp v0.7.0 // indirect
diff --git a/go.sum b/go.sum
index 93b60664b..427a35f34 100644
--- a/go.sum
+++ b/go.sum
@@ -19,6 +19,8 @@ github.com/beorn7/perks v1.0.1
h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod
h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/blang/semver/v4 v4.0.0
h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM=
github.com/blang/semver/v4 v4.0.0/go.mod
h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ=
+github.com/camel-tooling/camel-monitor-operator v1.0.0
h1:svk+zojrOvqu9/NbD0jhweMw7V8jL0m04MzcDYqlgho=
+github.com/camel-tooling/camel-monitor-operator v1.0.0/go.mod
h1:srXAc8o8ka+XFYMKjLqDSAz7/8do95/waFbAQ/Tsyjg=
github.com/cespare/xxhash/v2 v2.3.0
h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod
h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cloudflare/circl v1.6.3
h1:9GPOhQGF9MCYUeXyMYlqTR6a5gTrgR/fBLXvUgtVcg8=
@@ -73,28 +75,28 @@ github.com/go-openapi/swag v0.27.1
h1:VotvOLWW8q/EAxB0YdsBBGC8XYyeL1YwBj2ungAGPN
github.com/go-openapi/swag v0.27.1/go.mod
h1:GTkJPwHfhJp6MWr4/rCh64HVI3Ofu+tcsbfjfHmTxpE=
github.com/go-openapi/swag/cmdutils v0.27.1
h1:I7sYqaWVl5mq0NEmNQkAmFDyNin9ufvMX/p2zwtQaOE=
github.com/go-openapi/swag/cmdutils v0.27.1/go.mod
h1:Sm1MVFMkF6guJJ+pQqHnQA3N0j9qALV3NxzDSv6bETM=
-github.com/go-openapi/swag/conv v0.27.1
h1:8wi9ZG+olmY1wXphl93EWniPtbSPkXM/feH7FgjsvrU=
-github.com/go-openapi/swag/conv v0.27.1/go.mod
h1:QbqMivkpKhC3g1B1GGGOJ6ANewI3S62dbzYu3Duowqs=
-github.com/go-openapi/swag/fileutils v0.27.1
h1:QQqBSoi5mW4XpU85nS0mLcA+zAE6vLzrb0QkmLKf9oM=
-github.com/go-openapi/swag/fileutils v0.27.1/go.mod
h1:VvJFZLTZS0AI854gEQz5tk7dBESdLjiNUMSZ/th2ry8=
-github.com/go-openapi/swag/jsonutils v0.27.1
h1:SVgK3i4USzCU5mibOOS/l4ea2h9UQXy7J7RNLTjuXjU=
-github.com/go-openapi/swag/jsonutils v0.27.1/go.mod
h1:tdlEpZqdcQ17uj6J4YdK9vd8It5qWMwjWXOs0tjpRlk=
-github.com/go-openapi/swag/jsonutils/fixtures_test v0.27.1
h1:mJu3COL9WEaZVp/Kf2PRMi7tPszPEJfSr/OO75ynCs8=
-github.com/go-openapi/swag/jsonutils/fixtures_test v0.27.1/go.mod
h1:mofwUWx70wvskwESqRJ//k/9kURmCgyJl5m5Ppoh5kY=
-github.com/go-openapi/swag/loading v0.27.1
h1:/DxUgDXKbBX4bcn7r9uEXfJyzN5XpiJmZplzQTjrRCY=
-github.com/go-openapi/swag/loading v0.27.1/go.mod
h1:jvGh3iA2+zyUUycB5fgJWzeHnhrpvGnJJM0RVE9ZShE=
-github.com/go-openapi/swag/mangling v0.27.1
h1:yC9D0HyUE8gbP+BfmGx9+AA89ikwZTMjESK3OnnoaqA=
-github.com/go-openapi/swag/mangling v0.27.1/go.mod
h1:jtBE2+V+3pILxOR7Vgce+Cwp6A2PgZbvVqfNntbVs0w=
+github.com/go-openapi/swag/conv v0.27.3
h1:iqJFmGEjmX3AY0lSszABFqRVqOSt99XS0LzNIMJYuhU=
+github.com/go-openapi/swag/conv v0.27.3/go.mod
h1:nPRmN6jgNme99hpf+nM0auDZGALWIqlwhisKPK/bQhQ=
+github.com/go-openapi/swag/fileutils v0.27.3
h1:3UVoZ2RLaIs1lt+2jcKzL8RM3Yk0rmsDE9FLA/HGxFE=
+github.com/go-openapi/swag/fileutils v0.27.3/go.mod
h1:VvJFZLTZS0AI854gEQz5tk7dBESdLjiNUMSZ/th2ry8=
+github.com/go-openapi/swag/jsonutils v0.27.3
h1:1DEz+O82frtSMBcos/7XIn1GnpNTbsD4Bru4Dc/uhRc=
+github.com/go-openapi/swag/jsonutils v0.27.3/go.mod
h1:qiDCoQvzkMxrV3G8FLEdIU5L+EFYc0zcDOHWT3Yofvo=
+github.com/go-openapi/swag/jsonutils/fixtures_test v0.27.3
h1:h/eT9kmGCDdFLJF29lOhzLtF0FmP1AX2MhLJWVebsb8=
+github.com/go-openapi/swag/jsonutils/fixtures_test v0.27.3/go.mod
h1:mofwUWx70wvskwESqRJ//k/9kURmCgyJl5m5Ppoh5kY=
+github.com/go-openapi/swag/loading v0.27.3
h1:L9nQkEgzU7QgFQL+pLEMfGUKxeM4pWwGwbET9Z3weW0=
+github.com/go-openapi/swag/loading v0.27.3/go.mod
h1:rJ0NeaKsF4CVPnMGjPQl7JlSHzvD0bc2DKXLss1hiuE=
+github.com/go-openapi/swag/mangling v0.27.3
h1:gRzzD1PAUoLTtGMgI3KpBmCSOlTuLTFWnviLxLcTnyg=
+github.com/go-openapi/swag/mangling v0.27.3/go.mod
h1:jtBE2+V+3pILxOR7Vgce+Cwp6A2PgZbvVqfNntbVs0w=
github.com/go-openapi/swag/netutils v0.27.1
h1:mICMFoS82F5TZ4Zy3cqmcQk+BFeCp3Uyq3Np7GI0/qU=
github.com/go-openapi/swag/netutils v0.27.1/go.mod
h1:J+WYyFMLtvtCGqa6jLv+YNUmIKI3ZRQRrvfNDMoQoEQ=
-github.com/go-openapi/swag/pools v0.27.1
h1:9LeadcMyb2GJCbXX5hVQDbZ2Lq9TL4dCs/nx1j5DO0E=
-github.com/go-openapi/swag/pools v0.27.1/go.mod
h1:kVQefhSK5RWuRe7BXsL8htgBPAMpN7HDGpGEknqugeE=
-github.com/go-openapi/swag/stringutils v0.27.1
h1:ZXePZ0r2p1qSjo8tD3Un4vFj8+FqlCkczxDrJIhYUp8=
-github.com/go-openapi/swag/stringutils v0.27.1/go.mod
h1:lzRN95CxXmA03XcDWHLOb6nOMcxCqR5rGY0lOgsfRoM=
-github.com/go-openapi/swag/typeutils v0.27.1
h1:KSTdFlfnse4r6dP9IrEnwMldjE+zs71UeEB3//PtVXc=
-github.com/go-openapi/swag/typeutils v0.27.1/go.mod
h1:Srm0xFNRZ1Y+vCxJclo5qzx8aj+1pAKda/YfFPrG0dQ=
-github.com/go-openapi/swag/yamlutils v0.27.1
h1:ftxv6xvXb1E3zohUc+okZ9nSqNb9StQX/FXnKZ98sQA=
-github.com/go-openapi/swag/yamlutils v0.27.1/go.mod
h1:bnxFIB1qewGRiZHypXGZ3fNgf13/0HfRgnS/iZBDrOo=
+github.com/go-openapi/swag/pools v0.27.3
h1:gXjImP3F6/56wRRcFgEPld084Y6u2gs21ikPBt8NKBk=
+github.com/go-openapi/swag/pools v0.27.3/go.mod
h1:kVQefhSK5RWuRe7BXsL8htgBPAMpN7HDGpGEknqugeE=
+github.com/go-openapi/swag/stringutils v0.27.3
h1:Ru28hnbAvN5wycALQYy8IobHvASq+FUFMlp1QzLM0JI=
+github.com/go-openapi/swag/stringutils v0.27.3/go.mod
h1:lzRN95CxXmA03XcDWHLOb6nOMcxCqR5rGY0lOgsfRoM=
+github.com/go-openapi/swag/typeutils v0.27.3
h1:l6SSrx5eR5/WVwrGNzN6bQ9WqL04mrxNBl9YgQ3rcJ4=
+github.com/go-openapi/swag/typeutils v0.27.3/go.mod
h1:Srm0xFNRZ1Y+vCxJclo5qzx8aj+1pAKda/YfFPrG0dQ=
+github.com/go-openapi/swag/yamlutils v0.27.3
h1:cRFCAoYtslYn9L9T0xWryHy1t7c1MACC+DMj3CLvwvs=
+github.com/go-openapi/swag/yamlutils v0.27.3/go.mod
h1:6JYBGj8sw/NawMllyZY+cTA8Mzk2etS3ZBASdcyPsiU=
github.com/go-openapi/testify/enable/yaml/v2 v2.6.0
h1:gGHwAJ0R/5jU8BEGDbfRNR3hL68dAVi84WuOApp29B0=
github.com/go-openapi/testify/enable/yaml/v2 v2.6.0/go.mod
h1:tY+St1SGq4NFl0QIqdTY4aEdbChAHxhyB77XQi9iJCo=
github.com/go-openapi/testify/v2 v2.6.0
h1:5PKH2HE7YJ/LuRPQGvSxBRlFXNQhSetBLlGAgUEu3ug=
@@ -103,8 +105,6 @@ github.com/go-task/slim-sprig/v3 v3.0.0
h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1v
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod
h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
github.com/go-viper/mapstructure/v2 v2.5.0
h1:vM5IJoUAy3d7zRSVtIwQgBj7BiWtMPfmPEgAXnvj1Ro=
github.com/go-viper/mapstructure/v2 v2.5.0/go.mod
h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
-github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
-github.com/gogo/protobuf v1.3.2/go.mod
h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8
h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ=
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod
h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw=
github.com/google/gnostic-models v0.7.1
h1:SisTfuFKJSKM5CPZkffwi6coztzzeYUhc3v4yxLWH8c=
@@ -137,8 +137,6 @@ github.com/json-iterator/go v1.1.12
h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnr
github.com/json-iterator/go v1.1.12/go.mod
h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/kevinburke/ssh_config v1.2.0
h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4=
github.com/kevinburke/ssh_config v1.2.0/go.mod
h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
-github.com/kisielk/errcheck v1.5.0/go.mod
h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
-github.com/kisielk/gotool v1.0.0/go.mod
h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.20.1
h1:T7kKElXUMXrUJ2E9QhQhxFtcK5rPyLdsGZvdbLMPdiQ=
github.com/klauspost/compress v1.20.1/go.mod
h1:LUdAzn7YLVvxLpc7y3V1m40wESHTgc1422pwwBSKYuI=
github.com/kr/pretty v0.1.0/go.mod
h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
@@ -173,8 +171,8 @@ github.com/onsi/ginkgo/v2 v2.27.4
h1:fcEcQW/A++6aZAZQNUmNjvA9PSOzefMJBerHJ4t8v8Y
github.com/onsi/ginkgo/v2 v2.27.4/go.mod
h1:ArE1D/XhNXBXCBkKOLkbsb2c81dQHCRcF5zwn/ykDRo=
github.com/onsi/gomega v1.44.0 h1:eAiGl3Pw5jz5GQdDff0BcxYpAX1JxW8xD7mFUuwNfZQ=
github.com/onsi/gomega v1.44.0/go.mod
h1:e/C2HwaZ1DhvjzXXuFhcR7hY7Sh9pl7MmoWKEjzwcdA=
-github.com/openshift/api v0.0.0-20250820105013-6282350d0c39
h1:X42iTyo3AAHS36BkiBkU8FvxfK8NEDmnBi3QrnaCIlA=
-github.com/openshift/api v0.0.0-20250820105013-6282350d0c39/go.mod
h1:SPLf21TYPipzCO67BURkCfK6dcIIxx0oNRVWaOyRcXM=
+github.com/openshift/api v0.0.0-20260708103422-b1cc68a860b3
h1:sqSw/73v3MnSSQftRJ9x32Z5vXVoOQNk8lgP5bfRw/Y=
+github.com/openshift/api v0.0.0-20260708103422-b1cc68a860b3/go.mod
h1:7WJ3IPaK6nmWT8bDcaNooHqd0H5WepjVqV/10VlkMEM=
github.com/operator-framework/api v0.46.0
h1:EitSlfRJZKYDewTK+ntH9L/p+NIKv/L1PgRoaIWH+to=
github.com/operator-framework/api v0.46.0/go.mod
h1:oFXzh7rHCnMwC6N0B92NEsfQyLGJ7FrceAYemjwoKPI=
github.com/pandatix/go-abnf v0.5.3
h1:LA9AaxAqwzdxfDe7+HIGUB6tzx2N6wYVEAChmfsoPgo=
@@ -250,7 +248,6 @@ github.com/x448/float16 v0.8.4/go.mod
h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcY
github.com/xanzy/ssh-agent v0.3.3
h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM=
github.com/xanzy/ssh-agent v0.3.3/go.mod
h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw=
github.com/yuin/goldmark v1.1.27/go.mod
h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
-github.com/yuin/goldmark v1.2.1/go.mod
h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs=
go.uber.org/automaxprocs v1.6.0/go.mod
h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
@@ -266,18 +263,15 @@ go.yaml.in/yaml/v3 v3.0.5
h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw=
go.yaml.in/yaml/v3 v3.0.5/go.mod
h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod
h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod
h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
-golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod
h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod
h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.58.0 h1:COkYLr4k7nDI4r6QtMML2AfzeIswWHAJthLS6K/M/54=
golang.org/x/crypto v0.58.0/go.mod
h1:Xh+kl5A+M0gMiWDytH2Hhr5Z7WbNCpv/eB/EFgfSk+w=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
-golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.41.0 h1:qJmnOUb4YB+FsEuM3HcWucdZASCPGhsX6uljO6pog0c=
golang.org/x/mod v0.41.0/go.mod h1:Ek9pY8RKWXwsWvd3rQiHYtMqkjSUV+s1Rj7j4H5Ur6o=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod
h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod
h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod
h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod
h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod
h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.61.0 h1:RR+6j/BTBCrggOLugxa+66R8FvoN6MHlzDlZIbN9YFw=
golang.org/x/net v0.61.0/go.mod h1:WgDCOTH8iwtB66T4/GfI94AHo/hhdb7Hah9NDseQfiI=
@@ -285,13 +279,11 @@ golang.org/x/oauth2 v0.38.0
h1:uW7XQbcu1eoiQS7uMJvLOjPMl04bTbmY1VeRPxvbspI=
golang.org/x/oauth2 v0.38.0/go.mod
h1:IxwZNxUULJmpBFf9K/9NTMSIfZZuvuTy1gGxhigP/58=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod
h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod
h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod
h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.25.0 h1:dJnifr0nMV8/SBnBvdo4eSGGnrc3O4FFx6IqvtLcmRs=
golang.org/x/sync v0.25.0/go.mod
h1:sUUOizhqBxiL6pEWpqNLUiaJn1ShEbZ6BBqskPbjZm0=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod
h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod
h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod
h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod
h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod
h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod
h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod
h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -304,7 +296,6 @@ golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod
h1:bj7SfCRtBDWHUb9sn
golang.org/x/term v0.47.0 h1:cpyOwQkr2Nzxe2p6KnrTG7Ei2wtzjsuSjhpXXepZ/BM=
golang.org/x/term v0.47.0/go.mod
h1:efAfAuG6RKABvg1ArGebRkvX6LXul14SM67o2e4mUCY=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
-golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.43.0 h1:1QivrlhwAsnMOcqgOdl9my7s9OyZl8O5/UGUTJVXoWc=
golang.org/x/text v0.43.0/go.mod
h1:hbSIYA/amXcRXTFSZqh8tWHEoqRvvbKPJ2uz/KU+a+s=
@@ -313,14 +304,11 @@ golang.org/x/time v0.16.0/go.mod
h1:rVKOqvZeKvrDKTQiAHJ7wmwP0RzleSphoEA9RcdLA0s=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod
h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod
h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200505023115-26f46d2f7ef8/go.mod
h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
-golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod
h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
-golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod
h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.51.0 h1:k4Xc/1Om9jwkBJBo4NVLMSARBoWtK10mx+W5BnXCeAI=
golang.org/x/tools v0.51.0/go.mod
h1:9eEncMayCV6zRMGhR5eZEC2iBx98qWcF1HZ9Z7wJOoA=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod
h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod
h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod
h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod
h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gomodules.xyz/jsonpatch/v2 v2.5.0
h1:JELs8RLM12qJGXU4u/TO3V25KW8GreMKl9pdkk14RM0=
gomodules.xyz/jsonpatch/v2 v2.5.0/go.mod
h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY=
google.golang.org/protobuf v1.36.12
h1:pJOKDDOyeXErUroCihFAd5LQuwXBSpVnKGrj5o/fwxc=
diff --git a/helm/camel-k/crds/camel-k-crds.yaml
b/helm/camel-k/crds/camel-k-crds.yaml
index fddd2a143..3cf1ad33d 100644
--- a/helm/camel-k/crds/camel-k-crds.yaml
+++ b/helm/camel-k/crds/camel-k-crds.yaml
@@ -13718,6 +13718,10 @@ spec:
jsonPath: .status.replicas
name: Replicas
type: integer
+ - description: Whether an associated CamelMonitor resource is available
+ jsonPath: .status.conditions[?(@.type=="CamelMonitorAvailable")].status
+ name: Camel Monitor
+ type: string
name: v1
schema:
openAPIV3Schema:
diff --git a/helm/camel-k/templates/rbacs-descoped.yaml
b/helm/camel-k/templates/rbacs-descoped.yaml
index 2a57524c2..eb11fa0d6 100644
--- a/helm/camel-k/templates/rbacs-descoped.yaml
+++ b/helm/camel-k/templates/rbacs-descoped.yaml
@@ -22,6 +22,14 @@ metadata:
app: camel-k
name: {{ include "camel-k.fullname" . }}-operator
rules:
+- apiGroups:
+ - camel.apache.org
+ resources:
+ - camelmonitors
+ verbs:
+ - get
+ - list
+ - watch
- apiGroups:
- camel.apache.org
resources:
diff --git a/helm/camel-k/templates/rbacs-namespaced.yaml
b/helm/camel-k/templates/rbacs-namespaced.yaml
index 2b3bbbb9c..5c53106a8 100644
--- a/helm/camel-k/templates/rbacs-namespaced.yaml
+++ b/helm/camel-k/templates/rbacs-namespaced.yaml
@@ -22,6 +22,14 @@ metadata:
app: camel-k
name: {{ include "camel-k.fullname" . }}-operator
rules:
+- apiGroups:
+ - camel.apache.org
+ resources:
+ - camelmonitors
+ verbs:
+ - get
+ - list
+ - watch
- apiGroups:
- camel.apache.org
resources:
diff --git a/pkg/apis/addtoscheme_camel_monitor.go
b/pkg/apis/addtoscheme_camel_monitor.go
new file mode 100644
index 000000000..bb82349e5
--- /dev/null
+++ b/pkg/apis/addtoscheme_camel_monitor.go
@@ -0,0 +1,24 @@
+/*
+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 apis
+
+import monitorv1alpha1
"github.com/camel-tooling/camel-monitor-operator/pkg/apis/camel/v1alpha1"
+
+func init() {
+ AddToSchemes = append(AddToSchemes, monitorv1alpha1.AddToScheme)
+}
diff --git a/pkg/apis/camel/v1/integration_types.go
b/pkg/apis/camel/v1/integration_types.go
index a57720fdc..247b48d51 100644
--- a/pkg/apis/camel/v1/integration_types.go
+++ b/pkg/apis/camel/v1/integration_types.go
@@ -40,6 +40,7 @@ import (
// +kubebuilder:printcolumn:name="Catalog
Version",type=string,JSONPath=`.status.catalog.version`,description="The
catalog version"
//
+kubebuilder:printcolumn:name="Kit",type=string,JSONPath=`.status.integrationKit.name`,description="The
integration kit"
//
+kubebuilder:printcolumn:name="Replicas",type=integer,JSONPath=`.status.replicas`,description="The
number of pods"
+// +kubebuilder:printcolumn:name="Camel
Monitor",type=string,JSONPath=`.status.conditions[?(@.type=="CamelMonitorAvailable")].status`,description="Whether
an associated CamelMonitor resource is available"
// Integration is the Schema for the integrations API.
type Integration struct {
@@ -190,6 +191,8 @@ const (
// Deprecated: no longer in use.
IntegrationPhaseUnknown IntegrationPhase = "Unknown"
+ // IntegrationConditionCamelMonitorAvailable reports discovery of an
associated CamelMonitor.
+ IntegrationConditionCamelMonitorAvailable IntegrationConditionType =
"CamelMonitorAvailable"
// IntegrationConditionReady --.
IntegrationConditionReady IntegrationConditionType = "Ready"
// IntegrationConditionKitAvailable --.
diff --git a/pkg/controller/integration/camel_monitor.go
b/pkg/controller/integration/camel_monitor.go
new file mode 100644
index 000000000..c2ef09629
--- /dev/null
+++ b/pkg/controller/integration/camel_monitor.go
@@ -0,0 +1,46 @@
+/*
+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 integration
+
+import (
+ "context"
+
+ monitorv1alpha1
"github.com/camel-tooling/camel-monitor-operator/pkg/apis/camel/v1alpha1"
+ corev1 "k8s.io/api/core/v1"
+ k8serrors "k8s.io/apimachinery/pkg/api/errors"
+ ctrl "sigs.k8s.io/controller-runtime/pkg/client"
+
+ v1 "github.com/apache/camel-k/v2/pkg/apis/camel/v1"
+)
+
+func addCamelMonitorCondition(ctx context.Context, c ctrl.Reader, integration
*v1.Integration) error {
+ monitor := &monitorv1alpha1.CamelMonitor{}
+ if err := c.Get(ctx, ctrl.ObjectKeyFromObject(integration), monitor);
err != nil {
+ if k8serrors.IsNotFound(err) {
+
integration.Status.RemoveCondition(v1.IntegrationConditionCamelMonitorAvailable)
+
+ return nil
+ }
+
+ return err
+ }
+
integration.Status.SetCondition(v1.IntegrationConditionCamelMonitorAvailable,
corev1.ConditionTrue,
+ "CamelMonitorAvailable", "A CamelMonitor resource is available")
+
+ return nil
+}
diff --git a/pkg/controller/integration/camel_monitor_test.go
b/pkg/controller/integration/camel_monitor_test.go
new file mode 100644
index 000000000..fd404918f
--- /dev/null
+++ b/pkg/controller/integration/camel_monitor_test.go
@@ -0,0 +1,177 @@
+/*
+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 integration
+
+import (
+ "context"
+ "errors"
+ "testing"
+
+ monitorv1alpha1
"github.com/camel-tooling/camel-monitor-operator/pkg/apis/camel/v1alpha1"
+ "github.com/stretchr/testify/require"
+ corev1 "k8s.io/api/core/v1"
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ "k8s.io/apimachinery/pkg/runtime"
+ "k8s.io/client-go/discovery/fake"
+ k8stesting "k8s.io/client-go/testing"
+ "sigs.k8s.io/controller-runtime/pkg/builder"
+ ctrl "sigs.k8s.io/controller-runtime/pkg/client"
+ "sigs.k8s.io/controller-runtime/pkg/client/interceptor"
+ "sigs.k8s.io/controller-runtime/pkg/manager"
+
+ v1 "github.com/apache/camel-k/v2/pkg/apis/camel/v1"
+ "github.com/apache/camel-k/v2/pkg/client"
+ "github.com/apache/camel-k/v2/pkg/internal"
+ "github.com/apache/camel-k/v2/pkg/util/log"
+)
+
+func TestCamelMonitorCondition(t *testing.T) {
+ ctx := context.Background()
+ integration := v1.NewIntegration("test", "sample")
+ integration.Status.Phase = v1.IntegrationPhaseRunning
+ integration.SetReadyCondition(corev1.ConditionTrue, "Ready", "running")
+ ready :=
integration.Status.GetCondition(v1.IntegrationConditionReady).DeepCopy()
+ foreign := monitorv1alpha1.NewCamelMonitor("other", integration.Name)
+ different := monitorv1alpha1.NewCamelMonitor(integration.Namespace,
"different-name")
+ different.Labels = map[string]string{v1.IntegrationLabel:
integration.Name}
+ c, err := internal.NewFakeClient()
+ require.NoError(t, err)
+ require.NoError(t, c.Create(ctx, &foreign))
+ require.NoError(t, c.Create(ctx, &different))
+ require.NoError(t, addCamelMonitorCondition(ctx, c, &integration))
+ require.Nil(t,
integration.Status.GetCondition(v1.IntegrationConditionCamelMonitorAvailable))
+
+ monitor := monitorv1alpha1.NewCamelMonitor(integration.Namespace,
integration.Name)
+ monitor.Status.Conditions = []metav1.Condition{{Type: "Healthy",
Status: metav1.ConditionFalse}}
+ require.NoError(t, c.Create(ctx, &monitor))
+ require.NoError(t, addCamelMonitorCondition(ctx, c, &integration))
+ condition :=
integration.Status.GetCondition(v1.IntegrationConditionCamelMonitorAvailable).DeepCopy()
+ require.Equal(t, corev1.ConditionTrue, condition.Status)
+ require.NoError(t, addCamelMonitorCondition(ctx, c, &integration))
+ require.Equal(t, condition,
integration.Status.GetCondition(v1.IntegrationConditionCamelMonitorAvailable))
+ require.NoError(t, c.Delete(ctx, &monitor))
+ require.NoError(t, addCamelMonitorCondition(ctx, c, &integration))
+ require.Nil(t,
integration.Status.GetCondition(v1.IntegrationConditionCamelMonitorAvailable))
+ require.Equal(t, ready,
integration.Status.GetCondition(v1.IntegrationConditionReady))
+ require.Equal(t, v1.IntegrationPhaseRunning, integration.Status.Phase)
+}
+
+func TestCamelMonitorLookupFailure(t *testing.T) {
+ integration := v1.NewIntegration("test", "sample")
+
integration.Status.SetCondition(v1.IntegrationConditionCamelMonitorAvailable,
corev1.ConditionTrue, "Available", "found")
+ previous := integration.Status.DeepCopy()
+ c, err := internal.NewFakeClient()
+ require.NoError(t, err)
+ lookupErr := errors.New("lookup unavailable")
+ c.(*internal.FakeClient).Intercept(&interceptor.Funcs{Get:
func(context.Context, ctrl.WithWatch, ctrl.ObjectKey, ctrl.Object,
...ctrl.GetOption) error {
+ return lookupErr
+ }})
+ require.ErrorIs(t, addCamelMonitorCondition(context.Background(), c,
&integration), lookupErr)
+ require.Equal(t, previous, &integration.Status)
+}
+
+func enableCamelMonitorDiscovery(t *testing.T, c client.Client)
*fake.FakeDiscovery {
+ t.Helper()
+ fakeClient, ok := c.(*internal.FakeClient)
+ require.True(t, ok)
+ discovery, ok := fakeClient.Interface.Discovery().(*fake.FakeDiscovery)
+ require.True(t, ok)
+ discovery.Resources = []*metav1.APIResourceList{{GroupVersion:
monitorv1alpha1.SchemeGroupVersion.String(),
+ APIResources: []metav1.APIResource{{Kind:
monitorv1alpha1.AppKind, Name: "camelmonitors", Namespaced: true}}}}
+
+ return discovery
+}
+
+func TestMonitorActionCachesCamelMonitorAvailability(t *testing.T) {
+ for _, installed := range []bool{false, true} {
+ t.Run(map[bool]string{false: "absent", true:
"installed"}[installed], func(t *testing.T) {
+ c, integration, err := nominalEnvironment()
+ require.NoError(t, err)
+ if installed {
+ enableCamelMonitorDiscovery(t, c)
+ }
+ monitor :=
monitorv1alpha1.NewCamelMonitor(integration.Namespace, integration.Name)
+ require.NoError(t, c.Create(context.Background(),
&monitor))
+ action := NewMonitorAction(installed)
+ action.InjectClient(c)
+ action.InjectLogger(log.Log)
+ if installed {
+
c.(*internal.FakeClient).Interface.Discovery().(*fake.FakeDiscovery).Resources
= nil
+ } else {
+ enableCamelMonitorDiscovery(t, c)
+ }
+ discoveryRequests := 0
+
c.(*internal.FakeClient).Interface.Discovery().(*fake.FakeDiscovery).PrependReactor("get",
"resource", func(k8stesting.Action) (bool, runtime.Object, error) {
+ discoveryRequests++
+
+ return true, nil, errors.New("unexpected
discovery during reconciliation")
+ })
+ for range 2 {
+ result, err :=
action.Handle(context.Background(), integration)
+ require.NoError(t, err)
+ require.NotNil(t, result)
+ condition :=
result.Status.GetCondition(v1.IntegrationConditionCamelMonitorAvailable)
+ if installed {
+ require.NotNil(t, condition)
+ require.Equal(t, corev1.ConditionTrue,
condition.Status)
+ } else {
+ require.Nil(t, condition)
+ }
+ }
+ require.Zero(t, discoveryRequests)
+ })
+ }
+}
+
+func TestCamelMonitorOptionalWatch(t *testing.T) {
+ c, err := internal.NewFakeClient()
+ require.NoError(t, err)
+ require.NoError(t, watchCamelMonitorResources(c, nil))
+ discovery := enableCamelMonitorDiscovery(t, c)
+ require.NoError(t, watchCamelMonitorResources(c, &builder.Builder{}))
+ lookupErr := errors.New("discovery unavailable")
+ discovery.PrependReactor("get", "resource", func(k8stesting.Action)
(bool, runtime.Object, error) {
+ return true, nil, lookupErr
+ })
+ require.ErrorIs(t, watchCamelMonitorResources(c, &builder.Builder{}),
lookupErr)
+}
+
+func TestAddCamelMonitorDiscoveryFailure(t *testing.T) {
+ c, err := internal.NewFakeClient()
+ require.NoError(t, err)
+ discovery := enableCamelMonitorDiscovery(t, c)
+ lookupErr := errors.New("discovery unavailable")
+ discovery.PrependReactor("get", "resource", func(k8stesting.Action)
(bool, runtime.Object, error) {
+ return true, nil, lookupErr
+ })
+ require.ErrorIs(t, Add(context.Background(), camelMonitorTestManager{},
c), lookupErr)
+}
+
+type camelMonitorTestManager struct {
+ manager.Manager
+}
+
+func (camelMonitorTestManager) GetFieldIndexer() ctrl.FieldIndexer {
+ return camelMonitorTestFieldIndexer{}
+}
+
+type camelMonitorTestFieldIndexer struct{}
+
+func (camelMonitorTestFieldIndexer) IndexField(context.Context, ctrl.Object,
string, ctrl.IndexerFunc) error {
+ return nil
+}
diff --git a/pkg/controller/integration/integration_controller.go
b/pkg/controller/integration/integration_controller.go
index 85570d97f..c45dd3213 100644
--- a/pkg/controller/integration/integration_controller.go
+++ b/pkg/controller/integration/integration_controller.go
@@ -39,6 +39,8 @@ import (
"sigs.k8s.io/controller-runtime/pkg/predicate"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
+ monitorv1alpha1
"github.com/camel-tooling/camel-monitor-operator/pkg/apis/camel/v1alpha1"
+
v1 "github.com/apache/camel-k/v2/pkg/apis/camel/v1"
servingv1
"github.com/apache/camel-k/v2/pkg/apis/duck/knative/serving/v1"
"github.com/apache/camel-k/v2/pkg/client"
@@ -64,10 +66,15 @@ func Add(ctx context.Context, mgr manager.Manager, c
client.Client) error {
return fmt.Errorf("unable to set up field indexer for
status.phase: %w", err)
}
- return add(mgr, c, newReconciler(mgr, c))
+ camelMonitorAvailable, err := kubernetes.IsAPIResourceInstalled(c,
monitorv1alpha1.SchemeGroupVersion.String(), monitorv1alpha1.AppKind)
+ if err != nil {
+ return err
+ }
+
+ return add(mgr, c, newReconciler(mgr, c, camelMonitorAvailable))
}
-func newReconciler(mgr manager.Manager, c client.Client) reconcile.Reconciler {
+func newReconciler(mgr manager.Manager, c client.Client, camelMonitorAvailable
bool) reconcile.Reconciler {
base := []Action{
NewPlatformSetupAction(),
NewInitializeAction(),
@@ -81,7 +88,7 @@ func newReconciler(mgr manager.Manager, c client.Client)
reconcile.Reconciler {
client: c,
scheme: mgr.GetScheme(),
recorder:
mgr.GetEventRecorder("camel-k-integration-controller"),
- baseActions: append(append([]Action{}, base...),
NewMonitorAction()),
+ baseActions: append(append([]Action{}, base...),
NewMonitorAction(camelMonitorAvailable)),
},
schema.GroupVersionKind{
Group: v1.SchemeGroupVersion.Group,
@@ -312,6 +319,12 @@ func add(mgr manager.Manager, c client.Client, r
reconcile.Reconciler) error {
return err
}
+ // Watch for the Camel Monitors conditionally
+ err = watchCamelMonitorResources(c, b)
+ if err != nil {
+ return err
+ }
+
return b.Complete(r)
}
@@ -428,6 +441,24 @@ func watchKnativeResources(c client.Client, b
*builder.Builder) error {
return nil
}
+func watchCamelMonitorResources(c client.Client, b *builder.Builder) error {
+ installed, err := kubernetes.IsAPIResourceInstalled(c,
monitorv1alpha1.SchemeGroupVersion.String(), monitorv1alpha1.AppKind)
+ if err != nil {
+ return err
+ }
+ if !installed {
+ log.Info("CamelMonitor resources are not installed in the
cluster. To enable monitoring discovery after installing the CRD, " +
+ "assign the required RBAC privileges and restart the
Camel K operator.")
+
+ return nil
+ }
+ log.Info("CamelMonitor resources installed in the cluster: make sure to
assign CamelMonitor RBAC privileges")
+ b.Watches(&monitorv1alpha1.CamelMonitor{},
&handler.EnqueueRequestForObject{},
+ builder.WithPredicates(StatusChangedPredicate{}))
+
+ return nil
+}
+
var _ reconcile.Reconciler = &reconcileIntegration{}
// reconcileIntegration reconciles an Integration object.
diff --git a/pkg/controller/integration/integration_controller_test.go
b/pkg/controller/integration/integration_controller_test.go
index 634686302..c55d52942 100644
--- a/pkg/controller/integration/integration_controller_test.go
+++ b/pkg/controller/integration/integration_controller_test.go
@@ -38,7 +38,7 @@ func TestActions(t *testing.T) {
NewBuildCompleteAction(),
}
reconciler := reconcileIntegration{
- baseActions: append(append([]Action{}, base...),
NewMonitorAction()),
+ baseActions: append(append([]Action{}, base...),
NewMonitorAction(false)),
}
actions := reconciler.baseActions
diff --git a/pkg/controller/integration/monitor.go
b/pkg/controller/integration/monitor.go
index 186d46d77..7ec4255f5 100644
--- a/pkg/controller/integration/monitor.go
+++ b/pkg/controller/integration/monitor.go
@@ -46,12 +46,14 @@ import (
)
// NewMonitorAction is an action used to monitor manager Integrations.
-func NewMonitorAction() Action {
- return &monitorAction{}
+func NewMonitorAction(camelMonitorAvailable bool) Action {
+ return &monitorAction{camelMonitorAvailable: camelMonitorAvailable}
}
type monitorAction struct {
baseAction
+
+ camelMonitorAvailable bool
}
func (action *monitorAction) Name() string {
@@ -139,6 +141,12 @@ func (action *monitorAction) Handle(ctx context.Context,
integration *v1.Integra
}
action.checkTraitAnnotationsDeprecatedNotice(integration)
+ if action.camelMonitorAvailable {
+ if err := addCamelMonitorCondition(ctx, action.client,
integration); err != nil {
+ return integration, err
+ }
+ }
+
return action.monitorPods(ctx, environment, integration)
}
diff --git a/pkg/controller/integration/predicate_test.go
b/pkg/controller/integration/predicate_test.go
index 70f59958f..b06469677 100644
--- a/pkg/controller/integration/predicate_test.go
+++ b/pkg/controller/integration/predicate_test.go
@@ -20,6 +20,7 @@ package integration
import (
"testing"
+ monitorv1alpha1
"github.com/camel-tooling/camel-monitor-operator/pkg/apis/camel/v1alpha1"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@@ -74,3 +75,21 @@ func TestStatusChangedPredicateKnativeService(t *testing.T) {
assert.False(t, p.Update(event.UpdateEvent{ObjectOld: nil, ObjectNew:
same}))
assert.False(t, p.Update(event.UpdateEvent{ObjectOld: old, ObjectNew:
nil}))
}
+
+func TestStatusChangedPredicateCamelMonitor(t *testing.T) {
+ p := StatusChangedPredicate{}
+ monitor := monitorv1alpha1.NewCamelMonitor("test", "sample")
+ monitor.Status.Conditions = []metav1.Condition{{Type: "Healthy",
Status: metav1.ConditionFalse}}
+
+ assert.True(t, p.Create(event.CreateEvent{Object: &monitor}))
+ assert.True(t, p.Delete(event.DeleteEvent{Object: &monitor}))
+
+ metadataChanged := monitor.DeepCopy()
+ metadataChanged.Labels = map[string]string{"app": "sample"}
+ metadataChanged.Generation++
+ assert.False(t, p.Update(event.UpdateEvent{ObjectOld: &monitor,
ObjectNew: metadataChanged}))
+
+ statusChanged := monitor.DeepCopy()
+ statusChanged.Status.Conditions[0].Status = metav1.ConditionTrue
+ assert.True(t, p.Update(event.UpdateEvent{ObjectOld: &monitor,
ObjectNew: statusChanged}))
+}
diff --git a/pkg/resources/config/crd/bases/camel.apache.org_integrations.yaml
b/pkg/resources/config/crd/bases/camel.apache.org_integrations.yaml
index d0afbb9fd..41a0ff7fc 100644
--- a/pkg/resources/config/crd/bases/camel.apache.org_integrations.yaml
+++ b/pkg/resources/config/crd/bases/camel.apache.org_integrations.yaml
@@ -65,6 +65,10 @@ spec:
jsonPath: .status.replicas
name: Replicas
type: integer
+ - description: Whether an associated CamelMonitor resource is available
+ jsonPath: .status.conditions[?(@.type=="CamelMonitorAvailable")].status
+ name: Camel Monitor
+ type: string
name: v1
schema:
openAPIV3Schema:
diff --git a/pkg/resources/config/rbac/descoped/operator-cluster-role.yaml
b/pkg/resources/config/rbac/descoped/operator-cluster-role.yaml
index a21bef406..5e51a35c8 100644
--- a/pkg/resources/config/rbac/descoped/operator-cluster-role.yaml
+++ b/pkg/resources/config/rbac/descoped/operator-cluster-role.yaml
@@ -22,6 +22,14 @@ metadata:
labels:
app: "camel-k"
rules:
+- apiGroups:
+ - camel.apache.org
+ resources:
+ - camelmonitors
+ verbs:
+ - get
+ - list
+ - watch
- apiGroups:
- camel.apache.org
resources:
diff --git a/pkg/resources/config/rbac/namespaced/operator-role.yaml
b/pkg/resources/config/rbac/namespaced/operator-role.yaml
index 0999ba5b0..c9054a877 100644
--- a/pkg/resources/config/rbac/namespaced/operator-role.yaml
+++ b/pkg/resources/config/rbac/namespaced/operator-role.yaml
@@ -22,6 +22,14 @@ metadata:
labels:
app: "camel-k"
rules:
+- apiGroups:
+ - camel.apache.org
+ resources:
+ - camelmonitors
+ verbs:
+ - get
+ - list
+ - watch
- apiGroups:
- camel.apache.org
resources:
diff --git a/script/Makefile b/script/Makefile
index 2deb97485..a95499292 100644
--- a/script/Makefile
+++ b/script/Makefile
@@ -338,6 +338,15 @@ test-knative: gotestfmt-install
go test -timeout 60m -v ./e2e/knative -tags=integration $(GOTESTFMT) ||
((FAILED++)); \
exit $${FAILED}
+#
+# CamelMonitor discovery tests require Camel Monitor and Camel K operators.
+#
+.PHONY: test-camel-monitor
+test-camel-monitor: gotestfmt-install
+ set -o pipefail; FAILED=0; \
+ go test -timeout 20m -v ./e2e/camelmonitor -tags=integration
$(GOTESTFMT) || ((FAILED++)); \
+ exit $${FAILED}
+
#
# Kafka tests that require the presence of Strimzi operator configuration
#