Skip to content

CNTRLPLANE-3584: Enable resource metrics for kube-scheduler endpoints - #8680

Merged
openshift-merge-bot[bot] merged 1 commit into
openshift:mainfrom
dhgautam99:enable-kube-scheduler-resource-metrics
Jun 19, 2026
Merged

CNTRLPLANE-3584: Enable resource metrics for kube-scheduler endpoints#8680
openshift-merge-bot[bot] merged 1 commit into
openshift:mainfrom
dhgautam99:enable-kube-scheduler-resource-metrics

Conversation

@dhgautam99

@dhgautam99 dhgautam99 commented Jun 5, 2026

Copy link
Copy Markdown
Contributor

What this PR does / why we need it:

Adds guest-cluster RBAC to allow the metrics-client certificate identity (system:serviceaccount:hypershift:prometheus) to access the kube-scheduler /metrics/resources endpoint.

The built-in system:monitoring ClusterRole only grants GET on /metrics and /metrics/slis. The /metrics/resources endpoint (KEP-1748, exposing kube_pod_resource_request and kube_pod_resource_limit) requires a separate authorization grant. Without this, prometheus-user-workload on the management cluster receives a 403 Forbidden when scraping /metrics/resources via the ServiceMonitor.

This PR creates a new hypershift-metrics-resources-reader ClusterRole and ClusterRoleBinding in the guest cluster via HCCO, granting GET on /metrics/resources to the metrics-client identity.

Which issue(s) this PR fixes:

Fixes CNTRLPLANE-3584

Depends on #8489

Special notes for your reviewer:

  • The RBAC is reconciled by HCCO (Hosted Cluster Config Operator) in the guest cluster, following the same pattern as the existing system:monitoring ClusterRoleBinding
  • kube-scheduler uses --authorization-kubeconfig pointing to the guest cluster KAS, so RBAC checks for metrics endpoints happen on the guest cluster even though the scheduler runs on the management cluster
  • The /metrics/resources endpoint only returns data on the leader scheduler pod

Checklist:

  • Subject and description added to both, commit and PR.
  • Relevant issues have been referenced.
  • This change includes docs.
  • This change includes unit tests.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added an additional kube-scheduler Prometheus scrape endpoint for /metrics/resources, with TLS and cluster ID labeling applied to both endpoints.
    • Implemented per-metrics-set relabeling for scheduler resource metrics (including drop behavior where appropriate).
  • Chores

    • Updated RBAC to allow access to the new /metrics/resources scrape target.
  • Tests

    • Extended and added unit tests to validate the new RBAC helpers and ServiceMonitor relabeling across both endpoints.

@openshift-merge-bot

Copy link
Copy Markdown
Contributor

Pipeline controller notification
This repo is configured to use the pipeline controller. Second-stage tests will be triggered either automatically or after lgtm label is added, depending on the repository configuration. The pipeline controller will automatically detect which contexts are required and will utilize /test Prow commands to trigger the second stage.

For optional jobs, comment /test ? to see a list of all defined jobs. To trigger manually all jobs from second stage use /pipeline required command.

This repository is configured in: LGTM mode

@openshift-ci-robot openshift-ci-robot added the jira/valid-reference Indicates that this PR references a valid Jira ticket of any type. label Jun 5, 2026
@openshift-ci openshift-ci Bot added the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Jun 5, 2026
@openshift-ci

openshift-ci Bot commented Jun 5, 2026

Copy link
Copy Markdown
Contributor

Skipping CI for Draft Pull Request.
If you want CI signal for your change, please convert it to an actual PR.
You can still manually trigger a test run with /test all

@openshift-ci-robot

openshift-ci-robot commented Jun 5, 2026

Copy link
Copy Markdown

@dhgautam99: This pull request references CNTRLPLANE-3584 which is a valid jira issue.

Details

In response to this:

What this PR does / why we need it:

Adds guest-cluster RBAC to allow the metrics-client certificate identity (system:serviceaccount:hypershift:prometheus) to access the kube-scheduler /metrics/resources endpoint.

The built-in system:monitoring ClusterRole only grants GET on /metrics and /metrics/slis. The /metrics/resources endpoint (KEP-1748, exposing kube_pod_resource_request and kube_pod_resource_limit) requires a separate authorization grant. Without this, prometheus-user-workload on the management cluster receives a 403 Forbidden when scraping /metrics/resources via the ServiceMonitor.

This PR creates a new hypershift-metrics-resources-reader ClusterRole and ClusterRoleBinding in the guest cluster via HCCO, granting GET on /metrics/resources to the metrics-client identity.

Which issue(s) this PR fixes:

Fixes CNTRLPLANE-3584

Depends on #8489

Special notes for your reviewer:

  • The RBAC is reconciled by HCCO (Hosted Cluster Config Operator) in the guest cluster, following the same pattern as the existing system:monitoring ClusterRoleBinding
  • kube-scheduler uses --authorization-kubeconfig pointing to the guest cluster KAS, so RBAC checks for metrics endpoints happen on the guest cluster even though the scheduler runs on the management cluster
  • The /metrics/resources endpoint only returns data on the leader scheduler pod

Checklist:

  • Subject and description added to both, commit and PR.
  • Relevant issues have been referenced.
  • This change includes docs.
  • This change includes unit tests.

🤖 Generated with Claude Code

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the openshift-eng/jira-lifecycle-plugin repository.

@coderabbitai

coderabbitai Bot commented Jun 5, 2026

Copy link
Copy Markdown
Contributor
📝 Walkthrough

Walkthrough

This pull request enables Prometheus to scrape a second endpoint (/metrics/resources) from kube-scheduler with mutual TLS authentication. The change introduces a new RBAC ClusterRole and ClusterRoleBinding to grant system:serviceaccount:hypershift:prometheus permission to access the non-resource URL /metrics/resources, updates the ServiceMonitor to define the new HTTPS endpoint with TLS configuration, and extends the ServiceMonitor adaptation logic to apply set-specific metric relabeling to the new endpoint.

🚥 Pre-merge checks | ✅ 10 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Test Structure And Quality ❓ Inconclusive Tests use standard Go testing framework with table-driven patterns and Gomega matchers, not Ginkgo-style (It/Describe blocks). Check specifies "Ginkgo test code" review; unclear if this applies to... Clarify whether custom check applies to standard Go tests with Gomega matchers or only to Ginkgo-style (var _ = Describe) tests. If standard Go tests qualify, they pass: meaningful assertions, single responsibility, no cluster dependenci...
✅ Passed checks (10 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Stable And Deterministic Test Names ✅ Passed Tests use standard Go testing (not Ginkgo). All test names are static, descriptive strings with no dynamic values, generated identifiers, timestamps, or interpolation.
Topology-Aware Scheduling Compatibility ✅ Passed PR adds RBAC rules and metrics configuration only. No pod scheduling constraints, deployments, affinity rules, PodDisruptionBudgets, or topology assumptions are introduced.
Ipv6 And Disconnected Network Test Compatibility ✅ Passed This PR adds no Ginkgo e2e tests. All 3 test files added/modified are standard Go unit tests using func TestXxx(t *testing.T) patterns, not Ginkgo framework patterns (It(), Describe(), etc.).
No-Weak-Crypto ✅ Passed PR adds RBAC and metrics configuration with no weak crypto patterns, custom implementations, or insecure comparisons.
Container-Privileges ✅ Passed PR contains no container privilege escalations. Changes include only RBAC definitions, ServiceMonitor metrics configuration, and tests—no privileged/hostPID/hostNetwork/hostIPC/SYS_ADMIN/allowPrivi...
No-Sensitive-Data-In-Logs ✅ Passed No logging statements found in the new code. The PR adds RBAC functions, metrics relabel configs, and ServiceMonitor endpoint configuration with no logging that could expose passwords, tokens, API...
Title check ✅ Passed The title clearly and specifically describes the main change: enabling resource metrics for kube-scheduler endpoints by adding RBAC support.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@openshift-ci openshift-ci Bot added area/control-plane-operator Indicates the PR includes changes for the control plane operator - in an OCP release area/hypershift-operator Indicates the PR includes changes for the hypershift operator and API - outside an OCP release and removed do-not-merge/needs-area labels Jun 5, 2026
@codecov

codecov Bot commented Jun 5, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 77.77778% with 12 lines in your changes missing coverage. Please review.
✅ Project coverage is 41.68%. Comparing base (44f5195) to head (7fc8f82).
⚠️ Report is 66 commits behind head on main.

Files with missing lines Patch % Lines
...igoperator/controllers/resources/manifests/rbac.go 0.00% 12 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #8680      +/-   ##
==========================================
+ Coverage   41.66%   41.68%   +0.02%     
==========================================
  Files         758      758              
  Lines       93929    93983      +54     
==========================================
+ Hits        39135    39177      +42     
- Misses      52046    52058      +12     
  Partials     2748     2748              
Files with missing lines Coverage Δ
...edcontrolplane/v2/kube_scheduler/servicemonitor.go 100.00% <100.00%> (ø)
...igoperator/controllers/resources/rbac/reconcile.go 2.98% <100.00%> (+2.98%) ⬆️
...rconfigoperator/controllers/resources/resources.go 56.73% <100.00%> (+0.02%) ⬆️
support/metrics/sets.go 5.97% <100.00%> (+3.71%) ⬆️
...igoperator/controllers/resources/manifests/rbac.go 0.00% <0.00%> (ø)
Flag Coverage Δ
cmd-support 34.99% <100.00%> (+0.02%) ⬆️
cpo-hostedcontrolplane 44.02% <100.00%> (+0.01%) ⬆️
cpo-other 43.52% <66.66%> (+0.06%) ⬆️
hypershift-operator 51.65% <ø> (ø)
other 31.56% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
control-plane-operator/hostedclusterconfigoperator/controllers/resources/rbac/reconcile.go (1)

294-298: ⚡ Quick win

Use manifest-derived role name to prevent drift.

Line 297 hardcodes "hypershift-metrics-resources-reader" instead of referencing the manifest constructor. If either side changes, role binding will silently point to the wrong role.

💡 Proposed fix
 	r.RoleRef = rbacv1.RoleRef{
 		APIGroup: rbacv1.SchemeGroupVersion.Group,
 		Kind:     "ClusterRole",
-		Name:     "hypershift-metrics-resources-reader",
+		Name:     hccomanifests.MetricsResourcesClusterRole().Name,
 	}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@control-plane-operator/hostedclusterconfigoperator/controllers/resources/rbac/reconcile.go`
around lines 294 - 298, Replace the hardcoded RoleRef.Name string in the
r.RoleRef assignment with the canonical name produced by the manifest
constructor that builds the corresponding ClusterRole (the same code that
currently constructs "hypershift-metrics-resources-reader"); locate that
manifest constructor or exported name/constant used when creating the
ClusterRole and use it to set r.RoleRef.Name in the r.RoleRef assignment so the
RoleBinding always references the manifest-derived role name and cannot drift.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@control-plane-operator/controllers/hostedcontrolplane/v2/kube_scheduler/servicemonitor.go`:
- Around line 11-16: The adaptServiceMonitor function currently indexes
sm.Spec.Endpoints[0] without verifying endpoints exist, which can panic; update
adaptServiceMonitor to first check len(sm.Spec.Endpoints) > 0 and return a clear
error (or create/append a default endpoint if desired) when it is zero, then
only set MetricRelabelConfigs and call util.ApplyClusterIDLabel on
sm.Spec.Endpoints[0]; reference the adaptServiceMonitor function,
sm.Spec.Endpoints, MetricRelabelConfigs, and util.ApplyClusterIDLabel when
making the change.

In `@support/metrics/sets.go`:
- Around line 229-237: The Telemetry branch in SchedulerRelabelConfigs
incorrectly returns sreMetricsSetConfig.KubeScheduler causing Telemetry to
depend on the SRE config; change the MetricsSetTelemetry case to return the
Telemetry-specific relabel config (e.g., telemetryMetricsSetConfig.KubeScheduler
or a dedicated telemetry config variable) instead of
sreMetricsSetConfig.KubeScheduler, ensuring MetricsSetSRE still returns
sreMetricsSetConfig.KubeScheduler and the default returns nil.

---

Nitpick comments:
In
`@control-plane-operator/hostedclusterconfigoperator/controllers/resources/rbac/reconcile.go`:
- Around line 294-298: Replace the hardcoded RoleRef.Name string in the
r.RoleRef assignment with the canonical name produced by the manifest
constructor that builds the corresponding ClusterRole (the same code that
currently constructs "hypershift-metrics-resources-reader"); locate that
manifest constructor or exported name/constant used when creating the
ClusterRole and use it to set r.RoleRef.Name in the r.RoleRef assignment so the
RoleBinding always references the manifest-derived role name and cannot drift.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Central YAML (inherited)

Review profile: CHILL

Plan: Enterprise

Run ID: 7d14cef1-a721-4b03-b73a-d3d4e5886071

📥 Commits

Reviewing files that changed from the base of the PR and between f13c62d and 0b0a67a.

⛔ Files ignored due to path filters (20)
  • control-plane-operator/controllers/hostedcontrolplane/testdata/kube-scheduler/AROSwift/zz_fixture_TestControlPlaneComponents_kube_scheduler_controlplanecomponent.yaml is excluded by !**/testdata/**
  • control-plane-operator/controllers/hostedcontrolplane/testdata/kube-scheduler/AROSwift/zz_fixture_TestControlPlaneComponents_kube_scheduler_deployment.yaml is excluded by !**/testdata/**
  • control-plane-operator/controllers/hostedcontrolplane/testdata/kube-scheduler/AROSwift/zz_fixture_TestControlPlaneComponents_kube_scheduler_service.yaml is excluded by !**/testdata/**
  • control-plane-operator/controllers/hostedcontrolplane/testdata/kube-scheduler/AROSwift/zz_fixture_TestControlPlaneComponents_kube_scheduler_servicemonitor.yaml is excluded by !**/testdata/**
  • control-plane-operator/controllers/hostedcontrolplane/testdata/kube-scheduler/GCP/zz_fixture_TestControlPlaneComponents_kube_scheduler_controlplanecomponent.yaml is excluded by !**/testdata/**
  • control-plane-operator/controllers/hostedcontrolplane/testdata/kube-scheduler/GCP/zz_fixture_TestControlPlaneComponents_kube_scheduler_deployment.yaml is excluded by !**/testdata/**
  • control-plane-operator/controllers/hostedcontrolplane/testdata/kube-scheduler/GCP/zz_fixture_TestControlPlaneComponents_kube_scheduler_service.yaml is excluded by !**/testdata/**
  • control-plane-operator/controllers/hostedcontrolplane/testdata/kube-scheduler/GCP/zz_fixture_TestControlPlaneComponents_kube_scheduler_servicemonitor.yaml is excluded by !**/testdata/**
  • control-plane-operator/controllers/hostedcontrolplane/testdata/kube-scheduler/IBMCloud/zz_fixture_TestControlPlaneComponents_kube_scheduler_controlplanecomponent.yaml is excluded by !**/testdata/**
  • control-plane-operator/controllers/hostedcontrolplane/testdata/kube-scheduler/IBMCloud/zz_fixture_TestControlPlaneComponents_kube_scheduler_deployment.yaml is excluded by !**/testdata/**
  • control-plane-operator/controllers/hostedcontrolplane/testdata/kube-scheduler/IBMCloud/zz_fixture_TestControlPlaneComponents_kube_scheduler_service.yaml is excluded by !**/testdata/**
  • control-plane-operator/controllers/hostedcontrolplane/testdata/kube-scheduler/IBMCloud/zz_fixture_TestControlPlaneComponents_kube_scheduler_servicemonitor.yaml is excluded by !**/testdata/**
  • control-plane-operator/controllers/hostedcontrolplane/testdata/kube-scheduler/TechPreviewNoUpgrade/zz_fixture_TestControlPlaneComponents_kube_scheduler_controlplanecomponent.yaml is excluded by !**/testdata/**
  • control-plane-operator/controllers/hostedcontrolplane/testdata/kube-scheduler/TechPreviewNoUpgrade/zz_fixture_TestControlPlaneComponents_kube_scheduler_deployment.yaml is excluded by !**/testdata/**
  • control-plane-operator/controllers/hostedcontrolplane/testdata/kube-scheduler/TechPreviewNoUpgrade/zz_fixture_TestControlPlaneComponents_kube_scheduler_service.yaml is excluded by !**/testdata/**
  • control-plane-operator/controllers/hostedcontrolplane/testdata/kube-scheduler/TechPreviewNoUpgrade/zz_fixture_TestControlPlaneComponents_kube_scheduler_servicemonitor.yaml is excluded by !**/testdata/**
  • control-plane-operator/controllers/hostedcontrolplane/testdata/kube-scheduler/zz_fixture_TestControlPlaneComponents_kube_scheduler_controlplanecomponent.yaml is excluded by !**/testdata/**
  • control-plane-operator/controllers/hostedcontrolplane/testdata/kube-scheduler/zz_fixture_TestControlPlaneComponents_kube_scheduler_deployment.yaml is excluded by !**/testdata/**
  • control-plane-operator/controllers/hostedcontrolplane/testdata/kube-scheduler/zz_fixture_TestControlPlaneComponents_kube_scheduler_service.yaml is excluded by !**/testdata/**
  • control-plane-operator/controllers/hostedcontrolplane/testdata/kube-scheduler/zz_fixture_TestControlPlaneComponents_kube_scheduler_servicemonitor.yaml is excluded by !**/testdata/**
📒 Files selected for processing (15)
  • control-plane-operator/controllers/hostedcontrolplane/hostedcontrolplane_controller.go
  • control-plane-operator/controllers/hostedcontrolplane/manifests/pki.go
  • control-plane-operator/controllers/hostedcontrolplane/pki/scheduler.go
  • control-plane-operator/controllers/hostedcontrolplane/pki/scheduler_test.go
  • control-plane-operator/controllers/hostedcontrolplane/v2/assets/kube-scheduler/deployment.yaml
  • control-plane-operator/controllers/hostedcontrolplane/v2/assets/kube-scheduler/service.yaml
  • control-plane-operator/controllers/hostedcontrolplane/v2/assets/kube-scheduler/servicemonitor.yaml
  • control-plane-operator/controllers/hostedcontrolplane/v2/kube_scheduler/component.go
  • control-plane-operator/controllers/hostedcontrolplane/v2/kube_scheduler/component_test.go
  • control-plane-operator/controllers/hostedcontrolplane/v2/kube_scheduler/servicemonitor.go
  • control-plane-operator/controllers/hostedcontrolplane/v2/kube_scheduler/servicemonitor_test.go
  • control-plane-operator/hostedclusterconfigoperator/controllers/resources/manifests/rbac.go
  • control-plane-operator/hostedclusterconfigoperator/controllers/resources/rbac/reconcile.go
  • control-plane-operator/hostedclusterconfigoperator/controllers/resources/resources.go
  • support/metrics/sets.go

Comment on lines +11 to +16
func adaptServiceMonitor(cpContext component.WorkloadContext, sm *prometheusoperatorv1.ServiceMonitor) error {
sm.Spec.NamespaceSelector = prometheusoperatorv1.NamespaceSelector{
MatchNames: []string{sm.Namespace},
}
sm.Spec.Endpoints[0].MetricRelabelConfigs = metrics.SchedulerRelabelConfigs(cpContext.MetricsSet)
util.ApplyClusterIDLabel(&sm.Spec.Endpoints[0], cpContext.HCP.Spec.ClusterID)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Guard endpoint indexing to avoid reconcile panic.

Line 15 and Line 16 dereference sm.Spec.Endpoints[0] without validating length. A zero-endpoint manifest will panic the controller instead of returning a reconcile error.

💡 Proposed fix
 import (
+	"fmt"
+
 	component "github.com/openshift/hypershift/support/controlplane-component"
 	"github.com/openshift/hypershift/support/metrics"
 	"github.com/openshift/hypershift/support/util"
@@
 func adaptServiceMonitor(cpContext component.WorkloadContext, sm *prometheusoperatorv1.ServiceMonitor) error {
 	sm.Spec.NamespaceSelector = prometheusoperatorv1.NamespaceSelector{
 		MatchNames: []string{sm.Namespace},
 	}
+	if len(sm.Spec.Endpoints) == 0 {
+		return fmt.Errorf("kube-scheduler ServiceMonitor must define at least one endpoint")
+	}
 	sm.Spec.Endpoints[0].MetricRelabelConfigs = metrics.SchedulerRelabelConfigs(cpContext.MetricsSet)
 	util.ApplyClusterIDLabel(&sm.Spec.Endpoints[0], cpContext.HCP.Spec.ClusterID)

As per coding guidelines: "Avoid panics in Go except in truly unrecoverable cases."

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
func adaptServiceMonitor(cpContext component.WorkloadContext, sm *prometheusoperatorv1.ServiceMonitor) error {
sm.Spec.NamespaceSelector = prometheusoperatorv1.NamespaceSelector{
MatchNames: []string{sm.Namespace},
}
sm.Spec.Endpoints[0].MetricRelabelConfigs = metrics.SchedulerRelabelConfigs(cpContext.MetricsSet)
util.ApplyClusterIDLabel(&sm.Spec.Endpoints[0], cpContext.HCP.Spec.ClusterID)
import (
"fmt"
component "github.com/openshift/hypershift/support/controlplane-component"
"github.com/openshift/hypershift/support/metrics"
"github.com/openshift/hypershift/support/util"
prometheusoperatorv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1"
)
func adaptServiceMonitor(cpContext component.WorkloadContext, sm *prometheusoperatorv1.ServiceMonitor) error {
sm.Spec.NamespaceSelector = prometheusoperatorv1.NamespaceSelector{
MatchNames: []string{sm.Namespace},
}
if len(sm.Spec.Endpoints) == 0 {
return fmt.Errorf("kube-scheduler ServiceMonitor must define at least one endpoint")
}
sm.Spec.Endpoints[0].MetricRelabelConfigs = metrics.SchedulerRelabelConfigs(cpContext.MetricsSet)
util.ApplyClusterIDLabel(&sm.Spec.Endpoints[0], cpContext.HCP.Spec.ClusterID)
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@control-plane-operator/controllers/hostedcontrolplane/v2/kube_scheduler/servicemonitor.go`
around lines 11 - 16, The adaptServiceMonitor function currently indexes
sm.Spec.Endpoints[0] without verifying endpoints exist, which can panic; update
adaptServiceMonitor to first check len(sm.Spec.Endpoints) > 0 and return a clear
error (or create/append a default endpoint if desired) when it is zero, then
only set MetricRelabelConfigs and call util.ApplyClusterIDLabel on
sm.Spec.Endpoints[0]; reference the adaptServiceMonitor function,
sm.Spec.Endpoints, MetricRelabelConfigs, and util.ApplyClusterIDLabel when
making the change.

Comment thread support/metrics/sets.go
@dhgautam99
dhgautam99 force-pushed the enable-kube-scheduler-resource-metrics branch 2 times, most recently from 2f742aa to 2f9a908 Compare June 11, 2026 15:16

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (1)
control-plane-operator/controllers/hostedcontrolplane/v2/kube_scheduler/servicemonitor.go (1)

16-20: ⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Guard endpoint indexing before mutating relabel configs.

Line 16 and Line 19 index sm.Spec.Endpoints[0]/[1] without checking length; a malformed or future-changed manifest will panic reconciliation.

Proposed fix
 import (
+	"fmt"
+
 	component "github.com/openshift/hypershift/support/controlplane-component"
 	"github.com/openshift/hypershift/support/metrics"
 	"github.com/openshift/hypershift/support/util"
@@
 func adaptServiceMonitor(cpContext component.WorkloadContext, sm *prometheusoperatorv1.ServiceMonitor) error {
 	sm.Spec.NamespaceSelector = prometheusoperatorv1.NamespaceSelector{
 		MatchNames: []string{sm.Namespace},
 	}
+	if len(sm.Spec.Endpoints) < 2 {
+		return fmt.Errorf("kube-scheduler ServiceMonitor must define at least 2 endpoints, got %d", len(sm.Spec.Endpoints))
+	}
 
 	sm.Spec.Endpoints[0].MetricRelabelConfigs = metrics.SchedulerRelabelConfigs(cpContext.MetricsSet)
 	util.ApplyClusterIDLabel(&sm.Spec.Endpoints[0], cpContext.HCP.Spec.ClusterID)
 
 	sm.Spec.Endpoints[1].MetricRelabelConfigs = metrics.SchedulerResourceRelabelConfigs(cpContext.MetricsSet)
 	util.ApplyClusterIDLabel(&sm.Spec.Endpoints[1], cpContext.HCP.Spec.ClusterID)

As per coding guidelines: “Avoid panics in Go except in truly unrecoverable cases.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@control-plane-operator/controllers/hostedcontrolplane/v2/kube_scheduler/servicemonitor.go`
around lines 16 - 20, The code mutates sm.Spec.Endpoints[0] and [1] without
checking that sm.Spec.Endpoints has at least two elements, which can panic;
before assigning MetricRelabelConfigs or calling util.ApplyClusterIDLabel, guard
with length checks (e.g., if len(sm.Spec.Endpoints) > 0 { ... } and if
len(sm.Spec.Endpoints) > 1 { ... }) or ensure the slice is initialized/populated
with the expected endpoints, and then call
metrics.SchedulerRelabelConfigs(cpContext.MetricsSet),
metrics.SchedulerResourceRelabelConfigs(cpContext.MetricsSet) and
util.ApplyClusterIDLabel(... cpContext.HCP.Spec.ClusterID) only after confirming
the corresponding endpoint exists.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Duplicate comments:
In
`@control-plane-operator/controllers/hostedcontrolplane/v2/kube_scheduler/servicemonitor.go`:
- Around line 16-20: The code mutates sm.Spec.Endpoints[0] and [1] without
checking that sm.Spec.Endpoints has at least two elements, which can panic;
before assigning MetricRelabelConfigs or calling util.ApplyClusterIDLabel, guard
with length checks (e.g., if len(sm.Spec.Endpoints) > 0 { ... } and if
len(sm.Spec.Endpoints) > 1 { ... }) or ensure the slice is initialized/populated
with the expected endpoints, and then call
metrics.SchedulerRelabelConfigs(cpContext.MetricsSet),
metrics.SchedulerResourceRelabelConfigs(cpContext.MetricsSet) and
util.ApplyClusterIDLabel(... cpContext.HCP.Spec.ClusterID) only after confirming
the corresponding endpoint exists.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Central YAML (inherited)

Review profile: CHILL

Plan: Enterprise

Run ID: 7ddbd74d-62f2-46e8-8921-746d5e7e7fe5

📥 Commits

Reviewing files that changed from the base of the PR and between 2f742aa and 2f9a908.

📒 Files selected for processing (7)
  • control-plane-operator/controllers/hostedcontrolplane/v2/assets/kube-scheduler/servicemonitor.yaml
  • control-plane-operator/controllers/hostedcontrolplane/v2/kube_scheduler/servicemonitor.go
  • control-plane-operator/controllers/hostedcontrolplane/v2/kube_scheduler/servicemonitor_test.go
  • control-plane-operator/hostedclusterconfigoperator/controllers/resources/manifests/rbac.go
  • control-plane-operator/hostedclusterconfigoperator/controllers/resources/rbac/reconcile.go
  • control-plane-operator/hostedclusterconfigoperator/controllers/resources/resources.go
  • support/metrics/sets.go
🚧 Files skipped from review as they are similar to previous changes (3)
  • control-plane-operator/hostedclusterconfigoperator/controllers/resources/resources.go
  • control-plane-operator/hostedclusterconfigoperator/controllers/resources/rbac/reconcile.go
  • control-plane-operator/hostedclusterconfigoperator/controllers/resources/manifests/rbac.go

The built-in system:monitoring ClusterRole only grants access to /metrics.
This adds a ClusterRole and ClusterRoleBinding in the guest cluster to
allow the metrics client to scrape /metrics/resources on kube-scheduler,
which exposes kube_pod_resource_request and kube_pod_resource_limit metrics.
@dhgautam99
dhgautam99 force-pushed the enable-kube-scheduler-resource-metrics branch from 2f9a908 to 7fc8f82 Compare June 16, 2026 07:46

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
support/metrics/sets_test.go (1)

12-38: ⚡ Quick win

Strengthen SRE/All assertions to validate the actual contract.

The length-only check (len(got)) does not verify the “should return nil” behavior for MetricsSetAll, and hardcoding SRE as 0 is weaker than asserting against the configured SRE slice. Please assert nil for MetricsSetAll and compare SRE to sreMetricsSetConfig.KubeSchedulerResourceMetrics.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@support/metrics/sets_test.go` around lines 12 - 38, The test in the loop over
the test cases is performing weak assertions by only comparing the length of the
returned slice. For the MetricsSetAll case, instead of checking len(got) != 0,
explicitly assert that got is nil to properly validate the "should return nil"
contract. For the MetricsSetSRE case, replace the hardcoded want value of 0 with
a comparison against the actual configured SRE slice
sreMetricsSetConfig.KubeSchedulerResourceMetrics, and update the test assertion
logic to compare the returned slice directly against the configured value rather
than just checking its length. This ensures the SchedulerResourceRelabelConfigs
function returns the correct configured values for each metrics set.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@support/metrics/sets_test.go`:
- Around line 12-38: The test in the loop over the test cases is performing weak
assertions by only comparing the length of the returned slice. For the
MetricsSetAll case, instead of checking len(got) != 0, explicitly assert that
got is nil to properly validate the "should return nil" contract. For the
MetricsSetSRE case, replace the hardcoded want value of 0 with a comparison
against the actual configured SRE slice
sreMetricsSetConfig.KubeSchedulerResourceMetrics, and update the test assertion
logic to compare the returned slice directly against the configured value rather
than just checking its length. This ensures the SchedulerResourceRelabelConfigs
function returns the correct configured values for each metrics set.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Central YAML (inherited)

Review profile: CHILL

Plan: Enterprise

Run ID: 215f91a8-b71c-4e70-843b-c1ab7feb7838

📥 Commits

Reviewing files that changed from the base of the PR and between 2f9a908 and 7fc8f82.

⛔ Files ignored due to path filters (5)
  • control-plane-operator/controllers/hostedcontrolplane/testdata/kube-scheduler/AROSwift/zz_fixture_TestControlPlaneComponents_kube_scheduler_servicemonitor.yaml is excluded by !**/testdata/**
  • control-plane-operator/controllers/hostedcontrolplane/testdata/kube-scheduler/GCP/zz_fixture_TestControlPlaneComponents_kube_scheduler_servicemonitor.yaml is excluded by !**/testdata/**
  • control-plane-operator/controllers/hostedcontrolplane/testdata/kube-scheduler/IBMCloud/zz_fixture_TestControlPlaneComponents_kube_scheduler_servicemonitor.yaml is excluded by !**/testdata/**
  • control-plane-operator/controllers/hostedcontrolplane/testdata/kube-scheduler/TechPreviewNoUpgrade/zz_fixture_TestControlPlaneComponents_kube_scheduler_servicemonitor.yaml is excluded by !**/testdata/**
  • control-plane-operator/controllers/hostedcontrolplane/testdata/kube-scheduler/zz_fixture_TestControlPlaneComponents_kube_scheduler_servicemonitor.yaml is excluded by !**/testdata/**
📒 Files selected for processing (9)
  • control-plane-operator/controllers/hostedcontrolplane/v2/assets/kube-scheduler/servicemonitor.yaml
  • control-plane-operator/controllers/hostedcontrolplane/v2/kube_scheduler/servicemonitor.go
  • control-plane-operator/controllers/hostedcontrolplane/v2/kube_scheduler/servicemonitor_test.go
  • control-plane-operator/hostedclusterconfigoperator/controllers/resources/manifests/rbac.go
  • control-plane-operator/hostedclusterconfigoperator/controllers/resources/rbac/reconcile.go
  • control-plane-operator/hostedclusterconfigoperator/controllers/resources/rbac/reconcile_test.go
  • control-plane-operator/hostedclusterconfigoperator/controllers/resources/resources.go
  • support/metrics/sets.go
  • support/metrics/sets_test.go
🚧 Files skipped from review as they are similar to previous changes (5)
  • support/metrics/sets.go
  • control-plane-operator/hostedclusterconfigoperator/controllers/resources/manifests/rbac.go
  • control-plane-operator/controllers/hostedcontrolplane/v2/assets/kube-scheduler/servicemonitor.yaml
  • control-plane-operator/hostedclusterconfigoperator/controllers/resources/rbac/reconcile.go
  • control-plane-operator/hostedclusterconfigoperator/controllers/resources/resources.go

@dhgautam99
dhgautam99 marked this pull request as ready for review June 16, 2026 08:00
@openshift-ci openshift-ci Bot removed the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Jun 16, 2026
@openshift-ci
openshift-ci Bot requested review from muraee and sjenning June 16, 2026 08:00
@dhgautam99 dhgautam99 changed the title CNTRLPLANE-3584: Add RBAC for kube-scheduler /metrics/resources endpoint CNTRLPLANE-3584: Enable resource metrics for kube-scheduler endpoints Jun 16, 2026
@muraee

muraee commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

/approve

@openshift-ci openshift-ci Bot added the approved Indicates a PR has been approved by an approver from all required OWNERS files. label Jun 16, 2026

@bryan-cox bryan-cox left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/lgtm

@openshift-ci openshift-ci Bot added the lgtm Indicates that a PR is ready to be merged. label Jun 17, 2026
@openshift-merge-bot

Copy link
Copy Markdown
Contributor

Scheduling tests matching the pipeline_run_if_changed or not excluded by pipeline_skip_if_only_changed parameters:
/test e2e-aks
/test e2e-aws
/test e2e-aws-upgrade-hypershift-operator
/test e2e-azure-v2-self-managed
/test e2e-kubevirt-aws-ovn-reduced
/test e2e-v2-aws
/test e2e-v2-gke

@openshift-ci

openshift-ci Bot commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: bryan-cox, dhgautam99, muraee

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@bryan-cox

Copy link
Copy Markdown
Member

/test e2e-aks

@hypershift-jira-solve-ci

Copy link
Copy Markdown
Contributor

AI Test Failure Analysis

Job: pull-ci-openshift-hypershift-main-e2e-aks | Build: 2067249334730100736 | Cost: $3.1602639999999997 | Failed step: hypershift-azure-run-e2e

View full analysis report


Generated by hypershift-analyze-e2e-failure post-step using Claude claude-opus-4-6

@bryan-cox

Copy link
Copy Markdown
Member

/test e2e-aks

@dhgautam99

Copy link
Copy Markdown
Contributor Author

/test e2e-kubevirt-aws-ovn-reduced

@hypershift-jira-solve-ci

Copy link
Copy Markdown
Contributor

Now I have the full picture. Let me produce the final report.

Timeline:

  • 07:09:01 — Job started
  • 07:12:37 — hypershift-tests build started (along with hypershift, hypershift-operator)
  • 07:17:56 — hypershift succeeded (5m19s)
  • 07:19:20 — hypershift-operator succeeded (6m43s)
  • 07:22:51 — hypershift-cli succeeded (3m29s)
  • 07:24:54 — User dhgautam99 manually aborted the jobhypershift-tests was still building (~12m17s elapsed, never completed)
  • The hypershift-tests build (compiling Dockerfile.e2e, which compiles all E2E test binaries) was still running and was the longest build. It had been running for 12m17s before being killed.

The context canceled error in the JUnit XML is the downstream effect of the abort signal propagating through the ci-operator client rate limiter.

Test Failure Analysis Complete

Job Information

  • Prow Job: pull-ci-openshift-hypershift-main-e2e-kubevirt-aws-ovn-reduced
  • Build ID: 2067504843097903104
  • Target: e2e-kubevirt-aws-ovn-reduced
  • Result: ABORTED

Test Failure Analysis

Error

error occurred handling build hypershift-tests-amd64: could not get build hypershift-tests-amd64: client rate limiter Wait returned an error: context canceled

Summary

This job was manually aborted by the PR author dhgautam99 at 07:24:54 UTC, approximately 16 minutes after the job started. At the time of the abort, the hypershift-tests-amd64 image build (which compiles all E2E test binaries from Dockerfile.e2e) had been running for ~12 minutes and had not yet completed. No test steps were ever reached — the job was still in the image build phase. The context canceled error in the JUnit XML is simply the downstream effect of the abort signal propagating through the ci-operator API client. This is not a product bug or CI infrastructure issue.

Root Cause

The PR author dhgautam99 manually aborted the Prow job before it could complete. The prowjob status explicitly confirms this:

"dhgautam99 successfully aborted 31b9dd58-2216-48ea-ad92-e089ae835fa4."

At the time of the abort, the CI pipeline was still in the image build phase — specifically, the hypershift-tests-amd64 build had been running for ~12 minutes (started 07:12:37, killed at 07:24:54). This build compiles all E2E test binaries from Dockerfile.e2e and is typically the longest-running build in the pipeline. The other three image builds (hypershift, hypershift-operator, hypershift-cli) had all completed successfully.

No multi-stage test steps (pre/test/post phases) were ever executed. No cluster was provisioned. No tests ran. The failure is purely due to the user-initiated abort.

Recommendations
  • No action required — This is a user-initiated abort, not a CI or product failure. The PR author likely cancelled the job intentionally (e.g., to push new code, re-trigger with changes, or because the job was no longer needed).
  • Re-trigger the job if CI results are still needed for PR CNTRLPLANE-3584: Enable resource metrics for kube-scheduler endpoints #8680. The job should be expected to run for significantly longer than 16 minutes, as it needs to complete image builds (~15 min), cluster provisioning, and E2E tests.
  • No code or infrastructure changes are necessary based on this job run.
Evidence
Evidence Detail
Prowjob state aborted
Abort description dhgautam99 successfully aborted 31b9dd58-2216-48ea-ad92-e089ae835fa4.
Job start time 2026-06-18T07:09:01Z
Abort time 2026-06-18T07:24:54Z
Total runtime before abort ~16 minutes
Failed step Build image hypershift-tests from the repository (still in progress when killed)
hypershift-tests build duration 12m17s (started 07:12:37, killed 07:24:54, never completed)
hypershift build ✅ Succeeded in 5m19s
hypershift-operator build ✅ Succeeded in 6m43s
hypershift-cli build ✅ Succeeded in 3m29s
Test steps executed None — abort occurred during image build phase
Entrypoint signal Entrypoint received interrupt: terminated
finished.json result ABORTED

@dhgautam99

Copy link
Copy Markdown
Contributor Author

/test e2e-kubevirt-aws-ovn-reduced

@vsolanki12

Copy link
Copy Markdown
Contributor

/verified by @vsolanki12
Before fix:

below endpoint didn't exist and /metrics/resources was not scraping:

  - metricRelabelings:
    - action: drop
      regex: .*
      sourceLabels:
      - __name__
    - action: replace
      replacement: 6d582d1f-6352-4244-bf9f-9e33188a5192
      targetLabel: _id
    path: /metrics/resources
    relabelings:
    - action: replace
      replacement: 6d582d1f-6352-4244-bf9f-9e33188a5192
      targetLabel: _id
    scheme: https
    targetPort: client
    tlsConfig:
      ca:
        configMap:
          key: ca.crt
          name: root-ca
      cert:
        secret:
          key: tls.crt
          name: metrics-client
      keySecret:
        key: tls.key
        name: metrics-client
      serverName: kube-scheduler

After fix:
above endpoint appeared in servicemonitor kube-scheduler, and with SRE and All metric set, it alse removed relabel config accordingly. Example below:

  - metricRelabelings:
    - action: replace
      replacement: 6d582d1f-6352-4244-bf9f-9e33188a5192
      targetLabel: _id
    path: /metrics/resources
    relabelings:
    - action: replace
      replacement: 6d582d1f-6352-4244-bf9f-9e33188a5192
      targetLabel: _id
    scheme: https
    targetPort: client
    tlsConfig:
      ca:
        configMap:
          key: ca.crt
          name: root-ca
      cert:
        secret:
          key: tls.crt
          name: metrics-client
      keySecret:
        key: tls.key
        name: metrics-client
      serverName: kube-scheduler

Servicemonitor is getting successfully recreated, if it is deleted by any user.

@openshift-ci-robot openshift-ci-robot added the verified Signifies that the PR passed pre-merge verification criteria label Jun 19, 2026
@openshift-ci-robot

Copy link
Copy Markdown

@vsolanki12: This PR has been marked as verified by @vsolanki12.

Details

In response to this:

/verified by @vsolanki12
Before fix:

below endpoint didn't exist and /metrics/resources was not scraping:

 - metricRelabelings:
   - action: drop
     regex: .*
     sourceLabels:
     - __name__
   - action: replace
     replacement: 6d582d1f-6352-4244-bf9f-9e33188a5192
     targetLabel: _id
   path: /metrics/resources
   relabelings:
   - action: replace
     replacement: 6d582d1f-6352-4244-bf9f-9e33188a5192
     targetLabel: _id
   scheme: https
   targetPort: client
   tlsConfig:
     ca:
       configMap:
         key: ca.crt
         name: root-ca
     cert:
       secret:
         key: tls.crt
         name: metrics-client
     keySecret:
       key: tls.key
       name: metrics-client
     serverName: kube-scheduler

After fix:
above endpoint appeared in servicemonitor kube-scheduler, and with SRE and All metric set, it alse removed relabel config accordingly. Example below:

 - metricRelabelings:
   - action: replace
     replacement: 6d582d1f-6352-4244-bf9f-9e33188a5192
     targetLabel: _id
   path: /metrics/resources
   relabelings:
   - action: replace
     replacement: 6d582d1f-6352-4244-bf9f-9e33188a5192
     targetLabel: _id
   scheme: https
   targetPort: client
   tlsConfig:
     ca:
       configMap:
         key: ca.crt
         name: root-ca
     cert:
       secret:
         key: tls.crt
         name: metrics-client
     keySecret:
       key: tls.key
       name: metrics-client
     serverName: kube-scheduler

Servicemonitor is getting successfully recreated, if it is deleted by any user.

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the openshift-eng/jira-lifecycle-plugin repository.

@openshift-ci

openshift-ci Bot commented Jun 19, 2026

Copy link
Copy Markdown
Contributor

@dhgautam99: all tests passed!

Full PR test history. Your PR dashboard.

Details

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository. I understand the commands that are listed here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

approved Indicates a PR has been approved by an approver from all required OWNERS files. area/control-plane-operator Indicates the PR includes changes for the control plane operator - in an OCP release area/hypershift-operator Indicates the PR includes changes for the hypershift operator and API - outside an OCP release jira/valid-reference Indicates that this PR references a valid Jira ticket of any type. lgtm Indicates that a PR is ready to be merged. verified Signifies that the PR passed pre-merge verification criteria

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants