From 3691abd9acaa3d59ac90ccb2f63ed15eba0d0728 Mon Sep 17 00:00:00 2001 From: Matt Burgess <549318+mattburgess@users.noreply.github.com> Date: Wed, 29 Jul 2026 21:39:35 +0100 Subject: [PATCH 1/2] Fix LoggingAlphaOptions wiring Using --feature-gates=LoggingAlphaOptions=true resulted in the descheduler not starting due to the feature gate not being recognised. --- cmd/descheduler/app/server.go | 10 +++-- cmd/descheduler/app/server_test.go | 71 ++++++++++++++++++++++++++++++ pkg/features/features.go | 2 +- 3 files changed, 78 insertions(+), 5 deletions(-) create mode 100644 cmd/descheduler/app/server_test.go diff --git a/cmd/descheduler/app/server.go b/cmd/descheduler/app/server.go index 29a0cdad0..4a4099dbd 100644 --- a/cmd/descheduler/app/server.go +++ b/cmd/descheduler/app/server.go @@ -28,13 +28,13 @@ import ( "sigs.k8s.io/descheduler/cmd/descheduler/app/options" "sigs.k8s.io/descheduler/pkg/descheduler" + "sigs.k8s.io/descheduler/pkg/features" "sigs.k8s.io/descheduler/pkg/tracing" "k8s.io/apimachinery/pkg/util/runtime" "k8s.io/apimachinery/pkg/watch" "k8s.io/apiserver/pkg/server/healthz" "k8s.io/apiserver/pkg/server/mux" - "k8s.io/component-base/featuregate" "k8s.io/component-base/logs" logsapi "k8s.io/component-base/logs/api/v1" _ "k8s.io/component-base/logs/json/register" @@ -49,7 +49,6 @@ func NewDeschedulerCommand(out io.Writer) *cobra.Command { klog.ErrorS(err, "unable to initialize server") } - featureGate := featuregate.NewFeatureGate() logConfig := logsapi.NewLoggingConfiguration() cmd := &cobra.Command{ @@ -58,7 +57,10 @@ func NewDeschedulerCommand(out io.Writer) *cobra.Command { Long: "The descheduler evicts pods which may be bound to less desired nodes", PreRunE: func(cmd *cobra.Command, args []string) error { logs.InitLogs() - if logsapi.ValidateAndApply(logConfig, featureGate); err != nil { + if err := features.DefaultMutableFeatureGate.SetFromMap(s.FeatureGates); err != nil { + return err + } + if err := logsapi.ValidateAndApply(logConfig, features.DefaultMutableFeatureGate); err != nil { return err } descheduler.SetupPlugins() @@ -80,9 +82,9 @@ func NewDeschedulerCommand(out io.Writer) *cobra.Command { } cmd.SetOut(out) flags := cmd.Flags() + runtime.Must(logsapi.AddFeatureGates(features.DefaultMutableFeatureGate)) s.AddFlags(flags) - runtime.Must(logsapi.AddFeatureGates(featureGate)) logsapi.AddFlags(logConfig, flags) return cmd diff --git a/cmd/descheduler/app/server_test.go b/cmd/descheduler/app/server_test.go new file mode 100644 index 000000000..8a0deb248 --- /dev/null +++ b/cmd/descheduler/app/server_test.go @@ -0,0 +1,71 @@ +/* +Copyright 2026 The Kubernetes Authors. + +Licensed 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 app + +import ( + "io" + "strings" + "testing" +) + +func TestNewDeschedulerCommand_LoggingAlphaOptions(t *testing.T) { + tests := []struct { + name string + args []string + wantErr string + }{ + { + name: "LoggingAlphaOptions=false rejects --log-json-split-stream", + args: []string{ + "--logging-format=json", + "--log-json-split-stream", + "--feature-gates=LoggingAlphaOptions=false", + }, + wantErr: "LoggingAlphaOptions is disabled", + }, + { + name: "LoggingAlphaOptions=true accepts --log-json-split-stream", + args: []string{ + "--logging-format=json", + "--log-json-split-stream", + "--feature-gates=LoggingAlphaOptions=true", + }, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + cmd := NewDeschedulerCommand(io.Discard) + if err := cmd.ParseFlags(tc.args); err != nil { + t.Fatalf("ParseFlags(%v): %v", tc.args, err) + } + err := cmd.PreRunE(cmd, cmd.Flags().Args()) + if tc.wantErr == "" { + if err != nil { + t.Fatalf("expected PreRunE to succeed, got error: %v", err) + } + return + } + if err == nil { + t.Fatalf("expected PreRunE to fail with %q, got nil", tc.wantErr) + } + if !strings.Contains(err.Error(), tc.wantErr) { + t.Fatalf("expected PreRunE error to contain %q, got :%v", tc.wantErr, err) + } + }) + } +} diff --git a/pkg/features/features.go b/pkg/features/features.go index fe504f022..2d14bbf3c 100644 --- a/pkg/features/features.go +++ b/pkg/features/features.go @@ -46,4 +46,4 @@ var defaultDeschedulerFeatureGates = map[featuregate.Feature]featuregate.Feature // Tests that need to modify feature gates for the duration of their test should use: // // defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features., )() -var DefaultMutableFeatureGate featuregate.MutableFeatureGate = featuregate.NewFeatureGate() +var DefaultMutableFeatureGate featuregate.MutableVersionedFeatureGate = featuregate.NewFeatureGate() From 39a2b0c68551038e32767992d2d26e08e276f4b7 Mon Sep 17 00:00:00 2001 From: Matt Burgess <549318+mattburgess@users.noreply.github.com> Date: Thu, 30 Jul 2026 18:45:17 +0100 Subject: [PATCH 2/2] gen docs --- docs/cli/descheduler.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/cli/descheduler.md b/docs/cli/descheduler.md index cd5b2b798..f8a9d8179 100644 --- a/docs/cli/descheduler.md +++ b/docs/cli/descheduler.md @@ -26,7 +26,10 @@ descheduler [flags] --feature-gates mapStringBool A set of key=value pairs that describe feature gates for alpha/experimental features. Options are: AllAlpha=true|false (ALPHA - default=false) AllBeta=true|false (BETA - default=false) + ContextualLogging=true|false (BETA - default=true) EvictionsInBackground=true|false (ALPHA - default=false) + LoggingAlphaOptions=true|false (ALPHA - default=false) + LoggingBetaOptions=true|false (BETA - default=true) -h, --help help for descheduler --http2-max-streams-per-connection int The limit that the server gives to clients for the maximum number of streams in an HTTP/2 connection. Zero means to use golang's default. --kubeconfig string File with kube configuration. Deprecated, use client-connection-kubeconfig instead.