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
 #

Reply via email to