diff --git a/cmd/kube-scheduler/app/server_test.go b/cmd/kube-scheduler/app/server_test.go index 6689309582d..dd670d75793 100644 --- a/cmd/kube-scheduler/app/server_test.go +++ b/cmd/kube-scheduler/app/server_test.go @@ -177,8 +177,8 @@ profiles: "PreScorePlugin": { {Name: "InterPodAffinity"}, {Name: "PodTopologySpread"}, - {Name: "DefaultPodTopologySpread"}, {Name: "TaintToleration"}, + {Name: "DefaultPodTopologySpread"}, }, "ScorePlugin": { {Name: "NodeResourcesBalancedAllocation", Weight: 1}, @@ -188,8 +188,8 @@ profiles: {Name: "NodeAffinity", Weight: 1}, {Name: "NodePreferAvoidPods", Weight: 10000}, {Name: "PodTopologySpread", Weight: 2}, - {Name: "DefaultPodTopologySpread", Weight: 1}, {Name: "TaintToleration", Weight: 1}, + {Name: "DefaultPodTopologySpread", Weight: 1}, }, "BindPlugin": {{Name: "DefaultBinder"}}, "ReservePlugin": {{Name: "VolumeBinding"}}, @@ -308,8 +308,8 @@ profiles: "PreScorePlugin": { {Name: "InterPodAffinity"}, {Name: "PodTopologySpread"}, - {Name: "DefaultPodTopologySpread"}, {Name: "TaintToleration"}, + {Name: "DefaultPodTopologySpread"}, }, "ScorePlugin": { {Name: "NodeResourcesBalancedAllocation", Weight: 1}, @@ -319,8 +319,8 @@ profiles: {Name: "NodeAffinity", Weight: 1}, {Name: "NodePreferAvoidPods", Weight: 10000}, {Name: "PodTopologySpread", Weight: 2}, - {Name: "DefaultPodTopologySpread", Weight: 1}, {Name: "TaintToleration", Weight: 1}, + {Name: "DefaultPodTopologySpread", Weight: 1}, }, "BindPlugin": {{Name: "DefaultBinder"}}, "ReservePlugin": {{Name: "VolumeBinding"}}, diff --git a/pkg/features/kube_features.go b/pkg/features/kube_features.go index 1b1436c3e59..a5e708268b8 100644 --- a/pkg/features/kube_features.go +++ b/pkg/features/kube_features.go @@ -293,6 +293,13 @@ const ( // Enables CSI Inline volumes support for pods CSIInlineVolume featuregate.Feature = "CSIInlineVolume" + // owner: @alculquicondor + // alpha: v1.19 + // + // Enables the use of PodTopologySpread scheduling plugin to do default + // spreading and disables legacy DefaultPodTopologySpread plugin. + DefaultPodTopologySpread featuregate.Feature = "DefaultPodTopologySpread" + // owner: @tallclair // alpha: v1.12 // beta: v1.14 @@ -660,6 +667,7 @@ var defaultKubernetesFeatureGates = map[featuregate.Feature]featuregate.FeatureS HugePageStorageMediumSize: {Default: false, PreRelease: featuregate.Alpha}, ExternalPolicyForExternalIP: {Default: true, PreRelease: featuregate.GA}, // remove in 1.20 AnyVolumeDataSource: {Default: false, PreRelease: featuregate.Alpha}, + DefaultPodTopologySpread: {Default: false, PreRelease: featuregate.Alpha}, // inherited features from generic apiserver, relisted here to get a conflict if it is changed // unintentionally on either side: diff --git a/pkg/scheduler/algorithmprovider/BUILD b/pkg/scheduler/algorithmprovider/BUILD index 01123f3accf..df9970a7e33 100644 --- a/pkg/scheduler/algorithmprovider/BUILD +++ b/pkg/scheduler/algorithmprovider/BUILD @@ -11,6 +11,7 @@ go_library( srcs = ["registry.go"], importpath = "k8s.io/kubernetes/pkg/scheduler/algorithmprovider", deps = [ + "//pkg/features:go_default_library", "//pkg/scheduler/apis/config:go_default_library", "//pkg/scheduler/framework/plugins/defaultbinder:go_default_library", "//pkg/scheduler/framework/plugins/defaultpodtopologyspread:go_default_library", @@ -29,6 +30,8 @@ go_library( "//pkg/scheduler/framework/plugins/volumebinding:go_default_library", "//pkg/scheduler/framework/plugins/volumerestrictions:go_default_library", "//pkg/scheduler/framework/plugins/volumezone:go_default_library", + "//staging/src/k8s.io/apiserver/pkg/util/feature:go_default_library", + "//vendor/k8s.io/klog/v2:go_default_library", ], ) @@ -37,6 +40,7 @@ go_test( srcs = ["registry_test.go"], embed = [":go_default_library"], deps = [ + "//pkg/features:go_default_library", "//pkg/scheduler/apis/config:go_default_library", "//pkg/scheduler/framework/plugins/defaultbinder:go_default_library", "//pkg/scheduler/framework/plugins/defaultpodtopologyspread:go_default_library", @@ -55,6 +59,9 @@ go_test( "//pkg/scheduler/framework/plugins/volumebinding:go_default_library", "//pkg/scheduler/framework/plugins/volumerestrictions:go_default_library", "//pkg/scheduler/framework/plugins/volumezone:go_default_library", + "//staging/src/k8s.io/apiserver/pkg/util/feature:go_default_library", + "//staging/src/k8s.io/component-base/featuregate:go_default_library", + "//staging/src/k8s.io/component-base/featuregate/testing:go_default_library", "//vendor/github.com/google/go-cmp/cmp:go_default_library", ], ) diff --git a/pkg/scheduler/algorithmprovider/registry.go b/pkg/scheduler/algorithmprovider/registry.go index 65bfb1f4e2f..76cc60b47f9 100644 --- a/pkg/scheduler/algorithmprovider/registry.go +++ b/pkg/scheduler/algorithmprovider/registry.go @@ -20,6 +20,9 @@ import ( "sort" "strings" + utilfeature "k8s.io/apiserver/pkg/util/feature" + "k8s.io/klog/v2" + "k8s.io/kubernetes/pkg/features" schedulerapi "k8s.io/kubernetes/pkg/scheduler/apis/config" "k8s.io/kubernetes/pkg/scheduler/framework/plugins/defaultbinder" "k8s.io/kubernetes/pkg/scheduler/framework/plugins/defaultpodtopologyspread" @@ -49,7 +52,10 @@ type Registry map[string]*schedulerapi.Plugins // NewRegistry returns an algorithm provider registry instance. func NewRegistry() Registry { defaultConfig := getDefaultConfig() + applyFeatureGates(defaultConfig) + caConfig := getClusterAutoscalerConfig() + applyFeatureGates(caConfig) return Registry{ schedulerapi.SchedulerDefaultProviderName: defaultConfig, @@ -106,7 +112,6 @@ func getDefaultConfig() *schedulerapi.Plugins { Enabled: []schedulerapi.Plugin{ {Name: interpodaffinity.Name}, {Name: podtopologyspread.Name}, - {Name: defaultpodtopologyspread.Name}, {Name: tainttoleration.Name}, }, }, @@ -122,7 +127,6 @@ func getDefaultConfig() *schedulerapi.Plugins { // - This is a score coming from user preference. // - It makes its signal comparable to NodeResourcesLeastAllocated. {Name: podtopologyspread.Name, Weight: 2}, - {Name: defaultpodtopologyspread.Name, Weight: 1}, {Name: tainttoleration.Name, Weight: 1}, }, }, @@ -164,3 +168,15 @@ func getClusterAutoscalerConfig() *schedulerapi.Plugins { } return caConfig } + +func applyFeatureGates(config *schedulerapi.Plugins) { + if !utilfeature.DefaultFeatureGate.Enabled(features.DefaultPodTopologySpread) { + // When feature is enabled, the default spreading is done by + // PodTopologySpread plugin, which is enabled by default. + klog.Infof("Registering DefaultPodTopologySpread plugin") + s := schedulerapi.Plugin{Name: defaultpodtopologyspread.Name} + config.PreScore.Enabled = append(config.PreScore.Enabled, s) + s.Weight = 1 + config.Score.Enabled = append(config.Score.Enabled, s) + } +} diff --git a/pkg/scheduler/algorithmprovider/registry_test.go b/pkg/scheduler/algorithmprovider/registry_test.go index 8de10060e41..4e0ff2ca8ed 100644 --- a/pkg/scheduler/algorithmprovider/registry_test.go +++ b/pkg/scheduler/algorithmprovider/registry_test.go @@ -20,7 +20,11 @@ import ( "testing" "github.com/google/go-cmp/cmp" + "k8s.io/component-base/featuregate" + utilfeature "k8s.io/apiserver/pkg/util/feature" + featuregatetesting "k8s.io/component-base/featuregate/testing" + "k8s.io/kubernetes/pkg/features" schedulerapi "k8s.io/kubernetes/pkg/scheduler/apis/config" "k8s.io/kubernetes/pkg/scheduler/framework/plugins/defaultbinder" "k8s.io/kubernetes/pkg/scheduler/framework/plugins/defaultpodtopologyspread" @@ -79,8 +83,8 @@ func TestClusterAutoscalerProvider(t *testing.T) { Enabled: []schedulerapi.Plugin{ {Name: interpodaffinity.Name}, {Name: podtopologyspread.Name}, - {Name: defaultpodtopologyspread.Name}, {Name: tainttoleration.Name}, + {Name: defaultpodtopologyspread.Name}, }, }, Score: &schedulerapi.PluginSet{ @@ -92,8 +96,8 @@ func TestClusterAutoscalerProvider(t *testing.T) { {Name: nodeaffinity.Name, Weight: 1}, {Name: nodepreferavoidpods.Name, Weight: 10000}, {Name: podtopologyspread.Name, Weight: 2}, - {Name: defaultpodtopologyspread.Name, Weight: 1}, {Name: tainttoleration.Name, Weight: 1}, + {Name: defaultpodtopologyspread.Name, Weight: 1}, }, }, Reserve: &schedulerapi.PluginSet{ @@ -129,3 +133,191 @@ func TestClusterAutoscalerProvider(t *testing.T) { t.Errorf("unexpected config diff (-want, +got): %s", diff) } } + +func TestApplyFeatureGates(t *testing.T) { + tests := []struct { + name string + feature featuregate.Feature + wantConfig *schedulerapi.Plugins + }{ + { + name: "Feature gates disabled", + wantConfig: &schedulerapi.Plugins{ + QueueSort: &schedulerapi.PluginSet{ + Enabled: []schedulerapi.Plugin{ + {Name: queuesort.Name}, + }, + }, + PreFilter: &schedulerapi.PluginSet{ + Enabled: []schedulerapi.Plugin{ + {Name: noderesources.FitName}, + {Name: nodeports.Name}, + {Name: podtopologyspread.Name}, + {Name: interpodaffinity.Name}, + }, + }, + Filter: &schedulerapi.PluginSet{ + Enabled: []schedulerapi.Plugin{ + {Name: nodeunschedulable.Name}, + {Name: noderesources.FitName}, + {Name: nodename.Name}, + {Name: nodeports.Name}, + {Name: nodeaffinity.Name}, + {Name: volumerestrictions.Name}, + {Name: tainttoleration.Name}, + {Name: nodevolumelimits.EBSName}, + {Name: nodevolumelimits.GCEPDName}, + {Name: nodevolumelimits.CSIName}, + {Name: nodevolumelimits.AzureDiskName}, + {Name: volumebinding.Name}, + {Name: volumezone.Name}, + {Name: podtopologyspread.Name}, + {Name: interpodaffinity.Name}, + }, + }, + PreScore: &schedulerapi.PluginSet{ + Enabled: []schedulerapi.Plugin{ + {Name: interpodaffinity.Name}, + {Name: podtopologyspread.Name}, + {Name: tainttoleration.Name}, + {Name: defaultpodtopologyspread.Name}, + }, + }, + Score: &schedulerapi.PluginSet{ + Enabled: []schedulerapi.Plugin{ + {Name: noderesources.BalancedAllocationName, Weight: 1}, + {Name: imagelocality.Name, Weight: 1}, + {Name: interpodaffinity.Name, Weight: 1}, + {Name: noderesources.LeastAllocatedName, Weight: 1}, + {Name: nodeaffinity.Name, Weight: 1}, + {Name: nodepreferavoidpods.Name, Weight: 10000}, + {Name: podtopologyspread.Name, Weight: 2}, + {Name: tainttoleration.Name, Weight: 1}, + {Name: defaultpodtopologyspread.Name, Weight: 1}, + }, + }, + Reserve: &schedulerapi.PluginSet{ + Enabled: []schedulerapi.Plugin{ + {Name: volumebinding.Name}, + }, + }, + Unreserve: &schedulerapi.PluginSet{ + Enabled: []schedulerapi.Plugin{ + {Name: volumebinding.Name}, + }, + }, + PreBind: &schedulerapi.PluginSet{ + Enabled: []schedulerapi.Plugin{ + {Name: volumebinding.Name}, + }, + }, + Bind: &schedulerapi.PluginSet{ + Enabled: []schedulerapi.Plugin{ + {Name: defaultbinder.Name}, + }, + }, + PostBind: &schedulerapi.PluginSet{ + Enabled: []schedulerapi.Plugin{ + {Name: volumebinding.Name}, + }, + }, + }, + }, + { + name: "NewDefaultPodTopologySpread enabled", + feature: features.DefaultPodTopologySpread, + wantConfig: &schedulerapi.Plugins{ + QueueSort: &schedulerapi.PluginSet{ + Enabled: []schedulerapi.Plugin{ + {Name: queuesort.Name}, + }, + }, + PreFilter: &schedulerapi.PluginSet{ + Enabled: []schedulerapi.Plugin{ + {Name: noderesources.FitName}, + {Name: nodeports.Name}, + {Name: podtopologyspread.Name}, + {Name: interpodaffinity.Name}, + }, + }, + Filter: &schedulerapi.PluginSet{ + Enabled: []schedulerapi.Plugin{ + {Name: nodeunschedulable.Name}, + {Name: noderesources.FitName}, + {Name: nodename.Name}, + {Name: nodeports.Name}, + {Name: nodeaffinity.Name}, + {Name: volumerestrictions.Name}, + {Name: tainttoleration.Name}, + {Name: nodevolumelimits.EBSName}, + {Name: nodevolumelimits.GCEPDName}, + {Name: nodevolumelimits.CSIName}, + {Name: nodevolumelimits.AzureDiskName}, + {Name: volumebinding.Name}, + {Name: volumezone.Name}, + {Name: podtopologyspread.Name}, + {Name: interpodaffinity.Name}, + }, + }, + PreScore: &schedulerapi.PluginSet{ + Enabled: []schedulerapi.Plugin{ + {Name: interpodaffinity.Name}, + {Name: podtopologyspread.Name}, + {Name: tainttoleration.Name}, + }, + }, + Score: &schedulerapi.PluginSet{ + Enabled: []schedulerapi.Plugin{ + {Name: noderesources.BalancedAllocationName, Weight: 1}, + {Name: imagelocality.Name, Weight: 1}, + {Name: interpodaffinity.Name, Weight: 1}, + {Name: noderesources.LeastAllocatedName, Weight: 1}, + {Name: nodeaffinity.Name, Weight: 1}, + {Name: nodepreferavoidpods.Name, Weight: 10000}, + {Name: podtopologyspread.Name, Weight: 2}, + {Name: tainttoleration.Name, Weight: 1}, + }, + }, + Reserve: &schedulerapi.PluginSet{ + Enabled: []schedulerapi.Plugin{ + {Name: volumebinding.Name}, + }, + }, + Unreserve: &schedulerapi.PluginSet{ + Enabled: []schedulerapi.Plugin{ + {Name: volumebinding.Name}, + }, + }, + PreBind: &schedulerapi.PluginSet{ + Enabled: []schedulerapi.Plugin{ + {Name: volumebinding.Name}, + }, + }, + Bind: &schedulerapi.PluginSet{ + Enabled: []schedulerapi.Plugin{ + {Name: defaultbinder.Name}, + }, + }, + PostBind: &schedulerapi.PluginSet{ + Enabled: []schedulerapi.Plugin{ + {Name: volumebinding.Name}, + }, + }, + }, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + if test.feature != "" { + defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, test.feature, true)() + } + + r := NewRegistry() + gotConfig := r[schedulerapi.SchedulerDefaultProviderName] + if diff := cmp.Diff(test.wantConfig, gotConfig); diff != "" { + t.Errorf("unexpected config diff (-want, +got): %s", diff) + } + }) + } +} diff --git a/pkg/scheduler/apis/config/testing/compatibility_test.go b/pkg/scheduler/apis/config/testing/compatibility_test.go index 26638d9fa17..1caab74aec2 100644 --- a/pkg/scheduler/apis/config/testing/compatibility_test.go +++ b/pkg/scheduler/apis/config/testing/compatibility_test.go @@ -1410,8 +1410,8 @@ func TestAlgorithmProviderCompatibility(t *testing.T) { "PreScorePlugin": { {Name: "InterPodAffinity"}, {Name: "PodTopologySpread"}, - {Name: "DefaultPodTopologySpread"}, {Name: "TaintToleration"}, + {Name: "DefaultPodTopologySpread"}, }, "ScorePlugin": { {Name: "NodeResourcesBalancedAllocation", Weight: 1}, @@ -1421,8 +1421,8 @@ func TestAlgorithmProviderCompatibility(t *testing.T) { {Name: "NodeAffinity", Weight: 1}, {Name: "NodePreferAvoidPods", Weight: 10000}, {Name: "PodTopologySpread", Weight: 2}, - {Name: "DefaultPodTopologySpread", Weight: 1}, {Name: "TaintToleration", Weight: 1}, + {Name: "DefaultPodTopologySpread", Weight: 1}, }, "BindPlugin": {{Name: "DefaultBinder"}}, "ReservePlugin": {{Name: "VolumeBinding"}}, @@ -1478,8 +1478,8 @@ func TestAlgorithmProviderCompatibility(t *testing.T) { "PreScorePlugin": { {Name: "InterPodAffinity"}, {Name: "PodTopologySpread"}, - {Name: "DefaultPodTopologySpread"}, {Name: "TaintToleration"}, + {Name: "DefaultPodTopologySpread"}, }, "ScorePlugin": { {Name: "NodeResourcesBalancedAllocation", Weight: 1}, @@ -1489,8 +1489,8 @@ func TestAlgorithmProviderCompatibility(t *testing.T) { {Name: "NodeAffinity", Weight: 1}, {Name: "NodePreferAvoidPods", Weight: 10000}, {Name: "PodTopologySpread", Weight: 2}, - {Name: "DefaultPodTopologySpread", Weight: 1}, {Name: "TaintToleration", Weight: 1}, + {Name: "DefaultPodTopologySpread", Weight: 1}, }, "ReservePlugin": {{Name: "VolumeBinding"}}, "UnreservePlugin": {{Name: "VolumeBinding"}}, @@ -1566,8 +1566,8 @@ func TestPluginsConfigurationCompatibility(t *testing.T) { "PreScorePlugin": { {Name: "InterPodAffinity"}, {Name: "PodTopologySpread"}, - {Name: "DefaultPodTopologySpread"}, {Name: "TaintToleration"}, + {Name: "DefaultPodTopologySpread"}, }, "ScorePlugin": { {Name: "NodeResourcesBalancedAllocation", Weight: 1}, @@ -1577,8 +1577,8 @@ func TestPluginsConfigurationCompatibility(t *testing.T) { {Name: "NodeAffinity", Weight: 1}, {Name: "NodePreferAvoidPods", Weight: 10000}, {Name: "PodTopologySpread", Weight: 2}, - {Name: "DefaultPodTopologySpread", Weight: 1}, {Name: "TaintToleration", Weight: 1}, + {Name: "DefaultPodTopologySpread", Weight: 1}, }, "ReservePlugin": {{Name: "VolumeBinding"}}, "UnreservePlugin": {{Name: "VolumeBinding"}}, diff --git a/pkg/scheduler/apis/config/v1beta1/BUILD b/pkg/scheduler/apis/config/v1beta1/BUILD index 952927dcf66..47edd92fa12 100644 --- a/pkg/scheduler/apis/config/v1beta1/BUILD +++ b/pkg/scheduler/apis/config/v1beta1/BUILD @@ -15,12 +15,14 @@ go_library( visibility = ["//visibility:public"], deps = [ "//pkg/apis/core:go_default_library", + "//pkg/features:go_default_library", "//pkg/scheduler/apis/config:go_default_library", "//staging/src/k8s.io/api/core/v1:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/conversion:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/util/runtime:go_default_library", + "//staging/src/k8s.io/apiserver/pkg/util/feature:go_default_library", "//staging/src/k8s.io/component-base/config/v1alpha1:go_default_library", "//staging/src/k8s.io/kube-scheduler/config/v1:go_default_library", "//staging/src/k8s.io/kube-scheduler/config/v1beta1:go_default_library", @@ -36,11 +38,16 @@ go_test( ], embed = [":go_default_library"], deps = [ + "//pkg/features:go_default_library", "//pkg/scheduler/apis/config:go_default_library", + "//staging/src/k8s.io/api/core/v1:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/util/runtime:go_default_library", + "//staging/src/k8s.io/apiserver/pkg/util/feature:go_default_library", "//staging/src/k8s.io/component-base/config/v1alpha1:go_default_library", + "//staging/src/k8s.io/component-base/featuregate:go_default_library", + "//staging/src/k8s.io/component-base/featuregate/testing:go_default_library", "//staging/src/k8s.io/kube-scheduler/config/v1beta1:go_default_library", "//vendor/github.com/google/go-cmp/cmp:go_default_library", "//vendor/k8s.io/utils/pointer:go_default_library", diff --git a/pkg/scheduler/apis/config/v1beta1/defaults.go b/pkg/scheduler/apis/config/v1beta1/defaults.go index e23175e7eba..5861aa49dd4 100644 --- a/pkg/scheduler/apis/config/v1beta1/defaults.go +++ b/pkg/scheduler/apis/config/v1beta1/defaults.go @@ -22,8 +22,10 @@ import ( v1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apiserver/pkg/util/feature" componentbaseconfigv1alpha1 "k8s.io/component-base/config/v1alpha1" "k8s.io/kube-scheduler/config/v1beta1" + "k8s.io/kubernetes/pkg/features" "k8s.io/kubernetes/pkg/scheduler/apis/config" "k8s.io/utils/pointer" @@ -193,3 +195,25 @@ func SetDefaults_VolumeBindingArgs(obj *v1beta1.VolumeBindingArgs) { obj.BindTimeoutSeconds = pointer.Int64Ptr(600) } } + +func SetDefaults_PodTopologySpreadArgs(obj *v1beta1.PodTopologySpreadArgs) { + if !feature.DefaultFeatureGate.Enabled(features.DefaultPodTopologySpread) { + // When feature is disabled, the default spreading is done by legacy + // DefaultPodTopologySpread plugin. + return + } + if obj.DefaultConstraints == nil { + obj.DefaultConstraints = []v1.TopologySpreadConstraint{ + { + TopologyKey: v1.LabelHostname, + WhenUnsatisfiable: v1.ScheduleAnyway, + MaxSkew: 3, + }, + { + TopologyKey: v1.LabelZoneFailureDomainStable, + WhenUnsatisfiable: v1.ScheduleAnyway, + MaxSkew: 5, + }, + } + } +} diff --git a/pkg/scheduler/apis/config/v1beta1/defaults_test.go b/pkg/scheduler/apis/config/v1beta1/defaults_test.go index ffa16d2216e..f9a37960d61 100644 --- a/pkg/scheduler/apis/config/v1beta1/defaults_test.go +++ b/pkg/scheduler/apis/config/v1beta1/defaults_test.go @@ -21,11 +21,16 @@ import ( "time" "github.com/google/go-cmp/cmp" + v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" utilruntime "k8s.io/apimachinery/pkg/util/runtime" + utilfeature "k8s.io/apiserver/pkg/util/feature" componentbaseconfig "k8s.io/component-base/config/v1alpha1" + "k8s.io/component-base/featuregate" + featuregatetesting "k8s.io/component-base/featuregate/testing" "k8s.io/kube-scheduler/config/v1beta1" + "k8s.io/kubernetes/pkg/features" "k8s.io/utils/pointer" ) @@ -265,9 +270,10 @@ func TestSchedulerDefaults(t *testing.T) { func TestPluginArgsDefaults(t *testing.T) { tests := []struct { - name string - in runtime.Object - want runtime.Object + name string + feature featuregate.Feature + in runtime.Object + want runtime.Object }{ { name: "InterPodAffinityArgs empty", @@ -363,11 +369,59 @@ func TestPluginArgsDefaults(t *testing.T) { }, }, }, + { + name: "PodTopologySpreadArgs resources empty", + in: &v1beta1.PodTopologySpreadArgs{}, + want: &v1beta1.PodTopologySpreadArgs{}, + }, + { + name: "PodTopologySpreadArgs resources with value", + in: &v1beta1.PodTopologySpreadArgs{ + DefaultConstraints: []v1.TopologySpreadConstraint{ + { + TopologyKey: "planet", + WhenUnsatisfiable: v1.DoNotSchedule, + MaxSkew: 2, + }, + }, + }, + want: &v1beta1.PodTopologySpreadArgs{ + DefaultConstraints: []v1.TopologySpreadConstraint{ + { + TopologyKey: "planet", + WhenUnsatisfiable: v1.DoNotSchedule, + MaxSkew: 2, + }, + }, + }, + }, + { + name: "PodTopologySpreadArgs resources empty, NewPodTopologySpread feature enabled", + feature: features.DefaultPodTopologySpread, + in: &v1beta1.PodTopologySpreadArgs{}, + want: &v1beta1.PodTopologySpreadArgs{ + DefaultConstraints: []v1.TopologySpreadConstraint{ + { + TopologyKey: v1.LabelHostname, + WhenUnsatisfiable: v1.ScheduleAnyway, + MaxSkew: 3, + }, + { + TopologyKey: v1.LabelZoneFailureDomainStable, + WhenUnsatisfiable: v1.ScheduleAnyway, + MaxSkew: 5, + }, + }, + }, + }, } for _, tc := range tests { scheme := runtime.NewScheme() utilruntime.Must(AddToScheme(scheme)) t.Run(tc.name, func(t *testing.T) { + if tc.feature != "" { + defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, tc.feature, true)() + } scheme.Default(tc.in) if diff := cmp.Diff(tc.in, tc.want); diff != "" { t.Errorf("Got unexpected defaults (-want, +got):\n%s", diff) diff --git a/pkg/scheduler/apis/config/v1beta1/zz_generated.defaults.go b/pkg/scheduler/apis/config/v1beta1/zz_generated.defaults.go index 2b87e63efdc..a6a78548741 100644 --- a/pkg/scheduler/apis/config/v1beta1/zz_generated.defaults.go +++ b/pkg/scheduler/apis/config/v1beta1/zz_generated.defaults.go @@ -39,6 +39,7 @@ func RegisterDefaults(scheme *runtime.Scheme) error { scheme.AddTypeDefaultingFunc(&v1beta1.NodeResourcesMostAllocatedArgs{}, func(obj interface{}) { SetObjectDefaults_NodeResourcesMostAllocatedArgs(obj.(*v1beta1.NodeResourcesMostAllocatedArgs)) }) + scheme.AddTypeDefaultingFunc(&v1beta1.PodTopologySpreadArgs{}, func(obj interface{}) { SetObjectDefaults_PodTopologySpreadArgs(obj.(*v1beta1.PodTopologySpreadArgs)) }) scheme.AddTypeDefaultingFunc(&v1beta1.RequestedToCapacityRatioArgs{}, func(obj interface{}) { SetObjectDefaults_RequestedToCapacityRatioArgs(obj.(*v1beta1.RequestedToCapacityRatioArgs)) }) @@ -62,6 +63,10 @@ func SetObjectDefaults_NodeResourcesMostAllocatedArgs(in *v1beta1.NodeResourcesM SetDefaults_NodeResourcesMostAllocatedArgs(in) } +func SetObjectDefaults_PodTopologySpreadArgs(in *v1beta1.PodTopologySpreadArgs) { + SetDefaults_PodTopologySpreadArgs(in) +} + func SetObjectDefaults_RequestedToCapacityRatioArgs(in *v1beta1.RequestedToCapacityRatioArgs) { SetDefaults_RequestedToCapacityRatioArgs(in) }