From 4702f8170ac287cebddf42043622e801fb041523 Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Mon, 16 Mar 2026 15:34:27 -0700 Subject: [PATCH] pkg/cvo/metrics: Serve cluster_version_available_updates when channel is set MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since 8b9118992d (cvo: Add prometheus metrics to the CVO for current update state, 2018-11-05, #45), cluster_version_available_updates is only served when there are unconditionally-recommended updates and retrieval is succeeding. This makes it hard to understand at the fleet level when: cluster_operator_conditions{name="version", condition="RetrievedUpdates", reason="VersionNotFound"} == 0 is because of a misconfigured channel, or a misbehaving Update Service, or otherwise. We should always export cluster_version_available_updates whenever a channel is set, to make it easier to isolate the “because the cluster-admin has somehow selected a channel not compatible with their current version” (which we can’t do much about other than keep serving our existing CannotRetrieveUpdates alert) from the other possibilities (which we might be able to do something about). --- pkg/cvo/metrics.go | 2 +- pkg/cvo/metrics_test.go | 10 ++++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/pkg/cvo/metrics.go b/pkg/cvo/metrics.go index 30d04302f3..d26abe4277 100644 --- a/pkg/cvo/metrics.go +++ b/pkg/cvo/metrics.go @@ -629,7 +629,7 @@ func (m *operatorMetrics) Collect(ch chan<- prometheus.Metric) { ch <- g } - if len(cv.Spec.Upstream) > 0 || len(cv.Status.AvailableUpdates) > 0 || resourcemerge.IsOperatorStatusConditionTrue(cv.Status.Conditions, configv1.RetrievedUpdates) { + if len(cv.Spec.Channel) > 0 { upstream := "" if len(m.optr.updateService) > 0 { upstream = string(m.optr.updateService) diff --git a/pkg/cvo/metrics_test.go b/pkg/cvo/metrics_test.go index f12852be31..5cefc18c05 100644 --- a/pkg/cvo/metrics_test.go +++ b/pkg/cvo/metrics_test.go @@ -366,6 +366,9 @@ func Test_operatorMetrics_Collect(t *testing.T) { Name: "test", CreationTimestamp: metav1.Time{Time: time.Unix(2, 0)}, }, + Spec: configv1.ClusterVersionSpec{ + Channel: "test-channel", + }, Status: configv1.ClusterVersionStatus{ AvailableUpdates: []configv1.Release{ {Version: "1.0.1"}, @@ -382,7 +385,7 @@ func Test_operatorMetrics_Collect(t *testing.T) { } expectMetric(t, metrics[0], 2, map[string]string{"type": "initial", "version": "", "image": "", "from_version": ""}) expectMetric(t, metrics[1], 2, map[string]string{"type": "cluster", "version": "", "image": "", "from_version": ""}) - expectMetric(t, metrics[2], 2, map[string]string{"upstream": "", "channel": ""}) + expectMetric(t, metrics[2], 2, map[string]string{"upstream": "", "channel": "test-channel"}) expectMetric(t, metrics[3], 0, map[string]string{"type": "current", "version": "", "image": "", "from_version": ""}) expectMetric(t, metrics[4], 1, map[string]string{"type": ""}) }, @@ -398,6 +401,9 @@ func Test_operatorMetrics_Collect(t *testing.T) { Name: "test", CreationTimestamp: metav1.Time{Time: time.Unix(2, 0)}, }, + Spec: configv1.ClusterVersionSpec{ + Channel: "test-channel", + }, Status: configv1.ClusterVersionStatus{ Conditions: []configv1.ClusterOperatorStatusCondition{ {Type: configv1.RetrievedUpdates, Status: configv1.ConditionTrue, Reason: "Because stuff"}, @@ -414,7 +420,7 @@ func Test_operatorMetrics_Collect(t *testing.T) { expectMetric(t, metrics[0], 2, map[string]string{"type": "initial", "version": "", "image": "", "from_version": ""}) expectMetric(t, metrics[1], 2, map[string]string{"type": "cluster", "version": "", "image": "", "from_version": ""}) - expectMetric(t, metrics[2], 0, map[string]string{"upstream": "", "channel": ""}) + expectMetric(t, metrics[2], 0, map[string]string{"upstream": "", "channel": "test-channel"}) expectMetric(t, metrics[3], 1, map[string]string{"name": "version", "condition": "RetrievedUpdates", "reason": "Because stuff"}) expectMetric(t, metrics[4], 0, map[string]string{"type": "current", "version": "", "image": "", "from_version": ""}) expectMetric(t, metrics[5], 1, map[string]string{"type": ""})