From 07705a5de7fe084a582a083efaf725d3283d08af Mon Sep 17 00:00:00 2001 From: Richa Banker Date: Wed, 4 Feb 2026 17:09:58 -0800 Subject: [PATCH] Enable native histograms in scheduler gated by feature flag --- cmd/kube-scheduler/app/options/options.go | 2 + .../metrics/nativehistograms/metrics_test.go | 92 +++++++++++++++++++ 2 files changed, 94 insertions(+) diff --git a/cmd/kube-scheduler/app/options/options.go b/cmd/kube-scheduler/app/options/options.go index 80f6eeab695..7e8e7d0f667 100644 --- a/cmd/kube-scheduler/app/options/options.go +++ b/cmd/kube-scheduler/app/options/options.go @@ -47,6 +47,7 @@ import ( "k8s.io/component-base/logs" logsapi "k8s.io/component-base/logs/api/v1" "k8s.io/component-base/metrics" + metricsfeatures "k8s.io/component-base/metrics/features" zpagesfeatures "k8s.io/component-base/zpages/features" "k8s.io/klog/v2" schedulerappconfig "k8s.io/kubernetes/cmd/kube-scheduler/app/config" @@ -261,6 +262,7 @@ func (o *Options) ApplyTo(logger klog.Logger, c *schedulerappconfig.Config) erro return err } } + metricsfeatures.ApplyFeatureGates(utilfeature.DefaultFeatureGate) o.Metrics.Apply() // Apply value independently instead of using ApplyDeprecated() because it can't be configured via ComponentConfig. diff --git a/test/integration/metrics/nativehistograms/metrics_test.go b/test/integration/metrics/nativehistograms/metrics_test.go index d48c3eb68c4..52d82e14180 100644 --- a/test/integration/metrics/nativehistograms/metrics_test.go +++ b/test/integration/metrics/nativehistograms/metrics_test.go @@ -18,8 +18,13 @@ package nativehistograms import ( "context" + "crypto/tls" + "crypto/x509" "fmt" "net/http" + "os" + "path" + "strings" "testing" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -30,7 +35,9 @@ import ( metricsfeatures "k8s.io/component-base/metrics/features" "k8s.io/component-base/metrics/testutil" kubeapiservertesting "k8s.io/kubernetes/cmd/kube-apiserver/app/testing" + kubeschedulertesting "k8s.io/kubernetes/cmd/kube-scheduler/app/testing" "k8s.io/kubernetes/test/integration/framework" + "k8s.io/kubernetes/test/utils/ktesting" ) func scrapeMetrics(s *kubeapiservertesting.TestServer) (testutil.Metrics, error) { @@ -101,3 +108,88 @@ func TestAPIServerNativeHistogramMetrics(t *testing.T) { "apiserver_request_duration_seconds_count", }) } + +// TestSchedulerNativeHistogramMetrics verifies that native histogram metrics are properly +// exposed in scheduler when the NativeHistograms feature gate is enabled. +func TestSchedulerNativeHistogramMetrics(t *testing.T) { + featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, metricsfeatures.NativeHistograms, true) + s := kubeapiservertesting.StartTestServerOrDie(t, nil, framework.DefaultTestServerFlags(), framework.SharedEtcd()) + defer s.TearDownFn() + + apiserverConfig, err := os.CreateTemp("", "kubeconfig") + if err != nil { + t.Fatalf("Failed to create config file: %v", err) + } + defer func() { + if err := os.Remove(apiserverConfig.Name()); err != nil { + t.Errorf("Failed to remove config file: %v", err) + } + }() + + configStr := fmt.Sprintf(` +apiVersion: v1 +kind: Config +clusters: +- cluster: + server: %s + certificate-authority: %s + name: integration +contexts: +- context: + cluster: integration + user: kube-scheduler + name: default-context +current-context: default-context +users: +- name: kube-scheduler + user: + token: fake-token +`, s.ClientConfig.Host, s.ServerOpts.SecureServing.ServerCert.CertKey.CertFile) + + if _, err = apiserverConfig.WriteString(configStr); err != nil { + t.Fatalf("Failed to write config file: %v", err) + } + + _, ctx := ktesting.NewTestContext(t) + schedulerServer, err := kubeschedulertesting.StartTestServer( + t, ctx, + []string{"--kubeconfig", apiserverConfig.Name(), "--leader-elect=false", "--authentication-skip-lookup=true", "--authorization-always-allow-paths=/metrics"}, + ) + if err != nil { + t.Fatalf("Failed to start kube-scheduler server: %v", err) + } + if schedulerServer.TearDownFn != nil { + defer schedulerServer.TearDownFn() + } + + secureInfo := schedulerServer.Config.SecureServing + secureOptions := schedulerServer.Options.SecureServing + url := fmt.Sprintf("https://%s", secureInfo.Listener.Addr().String()) + url = strings.ReplaceAll(url, "[::]", "127.0.0.1") + + pool := x509.NewCertPool() + serverCertPath := path.Join(secureOptions.ServerCert.CertDirectory, secureOptions.ServerCert.PairName+".crt") + serverCert, err := os.ReadFile(serverCertPath) + if err != nil { + t.Fatalf("Failed to read component server cert: %v", err) + } + pool.AppendCertsFromPEM(serverCert) + httpClient := &http.Client{ + Transport: &http.Transport{ + TLSClientConfig: &tls.Config{RootCAs: pool}, + }, + } + + histogramMetric := "scheduler_scheduling_algorithm_duration_seconds" + metrics, err := testutil.ScrapeMetricsProto(url+"/metrics", httpClient) + if err != nil { + t.Fatalf("failed to scrape metrics: %v", err) + } + + mf, ok := metrics[histogramMetric] + if !ok { + t.Fatalf("metric %q not found", histogramMetric) + } + + testutil.AssertHasNativeHistogram(t, mf, nil) +}