Graduate PodSchedulingReadiness to stable

This commit is contained in:
Wei Huang 2024-02-28 21:22:12 -08:00
parent a67973a45c
commit 01db4ae9e7
No known key found for this signature in database
GPG Key ID: 17AFE05D01EA77B2
22 changed files with 91 additions and 512 deletions

View File

@ -239,23 +239,6 @@ leaderElection:
"simplified-scheduler": defaults.ExpandedPluginsV1, "simplified-scheduler": defaults.ExpandedPluginsV1,
}, },
}, },
{
name: "default config with a beta feature disabled",
flags: []string{
"--kubeconfig", configKubeconfig,
"--feature-gates=PodSchedulingReadiness=false",
},
wantPlugins: map[string]*config.Plugins{
"default-scheduler": func() *config.Plugins {
plugins := defaults.ExpandedPluginsV1.DeepCopy()
plugins.PreEnqueue = config.PluginSet{}
return plugins
}(),
},
restoreFeatures: map[featuregate.Feature]bool{
features.PodSchedulingReadiness: true,
},
},
{ {
name: "default config", name: "default config",
flags: []string{ flags: []string{

View File

@ -382,7 +382,6 @@ func GetValidationOptionsFromPodSpecAndMeta(podSpec, oldPodSpec *api.PodSpec, po
AllowIndivisibleHugePagesValues: false, AllowIndivisibleHugePagesValues: false,
AllowInvalidLabelValueInSelector: false, AllowInvalidLabelValueInSelector: false,
AllowInvalidTopologySpreadConstraintLabelSelector: false, AllowInvalidTopologySpreadConstraintLabelSelector: false,
AllowMutableNodeSelectorAndNodeAffinity: utilfeature.DefaultFeatureGate.Enabled(features.PodSchedulingReadiness),
AllowNamespacedSysctlsForHostNetAndHostIPC: false, AllowNamespacedSysctlsForHostNetAndHostIPC: false,
AllowNonLocalProjectedTokenPath: false, AllowNonLocalProjectedTokenPath: false,
} }
@ -558,11 +557,6 @@ func dropDisabledFields(
} }
} }
// If the feature is disabled and not in use, drop the schedulingGates field.
if !utilfeature.DefaultFeatureGate.Enabled(features.PodSchedulingReadiness) && !schedulingGatesInUse(oldPodSpec) {
podSpec.SchedulingGates = nil
}
dropDisabledProcMountField(podSpec, oldPodSpec) dropDisabledProcMountField(podSpec, oldPodSpec)
dropDisabledTopologySpreadConstraintsFields(podSpec, oldPodSpec) dropDisabledTopologySpreadConstraintsFields(podSpec, oldPodSpec)
@ -983,14 +977,6 @@ func appArmorInUse(podAnnotations map[string]string) bool {
return false return false
} }
// schedulingGatesInUse returns true if the pod spec is non-nil and it has SchedulingGates field set.
func schedulingGatesInUse(podSpec *api.PodSpec) bool {
if podSpec == nil {
return false
}
return len(podSpec.SchedulingGates) != 0
}
// restartableInitContainersInUse returns true if the pod spec is non-nil and // restartableInitContainersInUse returns true if the pod spec is non-nil and
// it has any init container with ContainerRestartPolicyAlways. // it has any init container with ContainerRestartPolicyAlways.
func restartableInitContainersInUse(podSpec *api.PodSpec) bool { func restartableInitContainersInUse(podSpec *api.PodSpec) bool {

View File

@ -2602,88 +2602,6 @@ func TestDropHostUsers(t *testing.T) {
} }
func TestDropSchedulingGates(t *testing.T) {
podWithSchedulingGates := func() *api.Pod {
return &api.Pod{
Spec: api.PodSpec{
SchedulingGates: []api.PodSchedulingGate{
{Name: "foo"},
{Name: "bar"},
},
},
}
}
podWithoutSchedulingGates := func() *api.Pod { return &api.Pod{} }
podInfo := []struct {
description string
hasSchedulingGatesField bool
pod func() *api.Pod
}{
{
description: "has SchedulingGates field",
hasSchedulingGatesField: true,
pod: podWithSchedulingGates,
},
{
description: "does not have SchedulingGates field",
hasSchedulingGatesField: false,
pod: podWithoutSchedulingGates,
},
{
description: "is nil",
hasSchedulingGatesField: false,
pod: func() *api.Pod { return nil },
},
}
for _, enabled := range []bool{true, false} {
for _, oldPodInfo := range podInfo {
for _, newPodInfo := range podInfo {
oldPodHasSchedulingGates, oldPod := oldPodInfo.hasSchedulingGatesField, oldPodInfo.pod()
newPodHasSchedulingGates, newPod := newPodInfo.hasSchedulingGatesField, newPodInfo.pod()
if newPod == nil {
continue
}
t.Run(fmt.Sprintf("feature enabled=%v, old pod %v, new pod %v", enabled, oldPodInfo.description, newPodInfo.description), func(t *testing.T) {
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.PodSchedulingReadiness, enabled)()
var oldPodSpec *api.PodSpec
if oldPod != nil {
oldPodSpec = &oldPod.Spec
}
dropDisabledFields(&newPod.Spec, nil, oldPodSpec, nil)
// Old Pod should never be changed.
if diff := cmp.Diff(oldPod, oldPodInfo.pod()); diff != "" {
t.Errorf("old pod changed: %v", diff)
}
switch {
case enabled || oldPodHasSchedulingGates:
// New Pod should not be changed if the feature is enabled, or if the old Pod had schedulingGates.
if diff := cmp.Diff(newPod, newPodInfo.pod()); diff != "" {
t.Errorf("new pod changed: %v", diff)
}
case newPodHasSchedulingGates:
// New Pod should be changed.
if reflect.DeepEqual(newPod, newPodInfo.pod()) {
t.Errorf("new pod was not changed")
}
// New Pod should not have SchedulingGates field.
if diff := cmp.Diff(newPod, podWithoutSchedulingGates()); diff != "" {
t.Errorf("new pod has SchedulingGates field: %v", diff)
}
default:
// New pod should not need to be changed.
if diff := cmp.Diff(newPod, newPodInfo.pod()); diff != "" {
t.Errorf("new pod changed: %v", diff)
}
}
})
}
}
}
}
func TestValidateTopologySpreadConstraintLabelSelectorOption(t *testing.T) { func TestValidateTopologySpreadConstraintLabelSelectorOption(t *testing.T) {
testCases := []struct { testCases := []struct {
name string name string

View File

@ -3352,9 +3352,6 @@ type PodSpec struct {
// //
// SchedulingGates can only be set at pod creation time, and be removed only afterwards. // SchedulingGates can only be set at pod creation time, and be removed only afterwards.
// //
// This is a beta feature enabled by the PodSchedulingReadiness feature gate.
//
// +featureGate=PodSchedulingReadiness
// +optional // +optional
SchedulingGates []PodSchedulingGate SchedulingGates []PodSchedulingGate
// ResourceClaims defines which ResourceClaims must be allocated // ResourceClaims defines which ResourceClaims must be allocated

View File

@ -3974,8 +3974,6 @@ type PodValidationOptions struct {
AllowHostIPsField bool AllowHostIPsField bool
// Allow invalid topologySpreadConstraint labelSelector for backward compatibility // Allow invalid topologySpreadConstraint labelSelector for backward compatibility
AllowInvalidTopologySpreadConstraintLabelSelector bool AllowInvalidTopologySpreadConstraintLabelSelector bool
// Allow node selector additions for gated pods.
AllowMutableNodeSelectorAndNodeAffinity bool
// Allow projected token volumes with non-local paths // Allow projected token volumes with non-local paths
AllowNonLocalProjectedTokenPath bool AllowNonLocalProjectedTokenPath bool
// Allow namespaced sysctls in hostNet and hostIPC pods // Allow namespaced sysctls in hostNet and hostIPC pods
@ -5056,7 +5054,7 @@ func ValidatePodUpdate(newPod, oldPod *core.Pod, opts PodValidationOptions) fiel
// Handle validations specific to gated pods. // Handle validations specific to gated pods.
podIsGated := len(oldPod.Spec.SchedulingGates) > 0 podIsGated := len(oldPod.Spec.SchedulingGates) > 0
if opts.AllowMutableNodeSelectorAndNodeAffinity && podIsGated { if podIsGated {
// Additions to spec.nodeSelector are allowed (no deletions or mutations) for gated pods. // Additions to spec.nodeSelector are allowed (no deletions or mutations) for gated pods.
if !apiequality.Semantic.DeepEqual(mungedPodSpec.NodeSelector, oldPod.Spec.NodeSelector) { if !apiequality.Semantic.DeepEqual(mungedPodSpec.NodeSelector, oldPod.Spec.NodeSelector) {
allErrs = append(allErrs, validateNodeSelectorMutation(specPath.Child("nodeSelector"), mungedPodSpec.NodeSelector, oldPod.Spec.NodeSelector)...) allErrs = append(allErrs, validateNodeSelectorMutation(specPath.Child("nodeSelector"), mungedPodSpec.NodeSelector, oldPod.Spec.NodeSelector)...)

View File

@ -12298,22 +12298,8 @@ func TestValidatePodCreateWithSchedulingGates(t *testing.T) {
tests := []struct { tests := []struct {
name string name string
pod *core.Pod pod *core.Pod
featureEnabled bool
wantFieldErrors field.ErrorList wantFieldErrors field.ErrorList
}{{ }{{
name: "create a Pod with nodeName and schedulingGates, feature disabled",
pod: &core.Pod{
ObjectMeta: metav1.ObjectMeta{Name: "pod", Namespace: "ns"},
Spec: core.PodSpec{
NodeName: "node",
SchedulingGates: []core.PodSchedulingGate{
{Name: "foo"},
},
},
},
featureEnabled: false,
wantFieldErrors: []*field.Error{field.Forbidden(fldPath.Child("nodeName"), "cannot be set until all schedulingGates have been cleared")},
}, {
name: "create a Pod with nodeName and schedulingGates, feature enabled", name: "create a Pod with nodeName and schedulingGates, feature enabled",
pod: &core.Pod{ pod: &core.Pod{
ObjectMeta: metav1.ObjectMeta{Name: "pod", Namespace: "ns"}, ObjectMeta: metav1.ObjectMeta{Name: "pod", Namespace: "ns"},
@ -12324,20 +12310,7 @@ func TestValidatePodCreateWithSchedulingGates(t *testing.T) {
}, },
}, },
}, },
featureEnabled: true,
wantFieldErrors: []*field.Error{field.Forbidden(fldPath.Child("nodeName"), "cannot be set until all schedulingGates have been cleared")}, wantFieldErrors: []*field.Error{field.Forbidden(fldPath.Child("nodeName"), "cannot be set until all schedulingGates have been cleared")},
}, {
name: "create a Pod with schedulingGates, feature disabled",
pod: &core.Pod{
ObjectMeta: metav1.ObjectMeta{Name: "pod", Namespace: "ns"},
Spec: core.PodSpec{
SchedulingGates: []core.PodSchedulingGate{
{Name: "foo"},
},
},
},
featureEnabled: false,
wantFieldErrors: nil,
}, { }, {
name: "create a Pod with schedulingGates, feature enabled", name: "create a Pod with schedulingGates, feature enabled",
pod: &core.Pod{ pod: &core.Pod{
@ -12348,15 +12321,12 @@ func TestValidatePodCreateWithSchedulingGates(t *testing.T) {
}, },
}, },
}, },
featureEnabled: true,
wantFieldErrors: nil, wantFieldErrors: nil,
}, },
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.PodSchedulingReadiness, tt.featureEnabled)()
applyEssentials(tt.pod) applyEssentials(tt.pod)
errs := ValidatePodCreate(tt.pod, PodValidationOptions{}) errs := ValidatePodCreate(tt.pod, PodValidationOptions{})
if diff := cmp.Diff(tt.wantFieldErrors, errs); diff != "" { if diff := cmp.Diff(tt.wantFieldErrors, errs); diff != "" {
@ -12383,7 +12353,6 @@ func TestValidatePodUpdate(t *testing.T) {
tests := []struct { tests := []struct {
new core.Pod new core.Pod
old core.Pod old core.Pod
opts PodValidationOptions
err string err string
test string test string
}{ }{
@ -13716,25 +13685,6 @@ func TestValidatePodUpdate(t *testing.T) {
SchedulingGates: []core.PodSchedulingGate{{Name: "baz"}}, SchedulingGates: []core.PodSchedulingGate{{Name: "baz"}},
}, },
}, },
err: "Forbidden: pod updates may not change fields other than `spec.containers[*].image",
test: "node selector is immutable when AllowMutableNodeSelector is false",
}, {
old: core.Pod{
Spec: core.PodSpec{
SchedulingGates: []core.PodSchedulingGate{{Name: "baz"}},
},
},
new: core.Pod{
Spec: core.PodSpec{
NodeSelector: map[string]string{
"foo": "bar",
},
SchedulingGates: []core.PodSchedulingGate{{Name: "baz"}},
},
},
opts: PodValidationOptions{
AllowMutableNodeSelectorAndNodeAffinity: true,
},
test: "adding node selector is allowed for gated pods", test: "adding node selector is allowed for gated pods",
}, { }, {
old: core.Pod{ old: core.Pod{
@ -13752,9 +13702,6 @@ func TestValidatePodUpdate(t *testing.T) {
}, },
}, },
}, },
opts: PodValidationOptions{
AllowMutableNodeSelectorAndNodeAffinity: true,
},
err: "Forbidden: pod updates may not change fields other than `spec.containers[*].image", err: "Forbidden: pod updates may not change fields other than `spec.containers[*].image",
test: "adding node selector is not allowed for non-gated pods", test: "adding node selector is not allowed for non-gated pods",
}, { }, {
@ -13771,9 +13718,6 @@ func TestValidatePodUpdate(t *testing.T) {
SchedulingGates: []core.PodSchedulingGate{{Name: "baz"}}, SchedulingGates: []core.PodSchedulingGate{{Name: "baz"}},
}, },
}, },
opts: PodValidationOptions{
AllowMutableNodeSelectorAndNodeAffinity: true,
},
err: "spec.nodeSelector: Invalid value:", err: "spec.nodeSelector: Invalid value:",
test: "removing node selector is not allowed for gated pods", test: "removing node selector is not allowed for gated pods",
}, { }, {
@ -13785,9 +13729,6 @@ func TestValidatePodUpdate(t *testing.T) {
}, },
}, },
new: core.Pod{}, new: core.Pod{},
opts: PodValidationOptions{
AllowMutableNodeSelectorAndNodeAffinity: true,
},
err: "Forbidden: pod updates may not change fields other than `spec.containers[*].image", err: "Forbidden: pod updates may not change fields other than `spec.containers[*].image",
test: "removing node selector is not allowed for non-gated pods", test: "removing node selector is not allowed for non-gated pods",
}, { }, {
@ -13807,9 +13748,6 @@ func TestValidatePodUpdate(t *testing.T) {
}, },
}, },
}, },
opts: PodValidationOptions{
AllowMutableNodeSelectorAndNodeAffinity: true,
},
test: "old pod spec has scheduling gate, new pod spec does not, and node selector is added", test: "old pod spec has scheduling gate, new pod spec does not, and node selector is added",
}, { }, {
old: core.Pod{ old: core.Pod{
@ -13828,9 +13766,6 @@ func TestValidatePodUpdate(t *testing.T) {
SchedulingGates: []core.PodSchedulingGate{{Name: "baz"}}, SchedulingGates: []core.PodSchedulingGate{{Name: "baz"}},
}, },
}, },
opts: PodValidationOptions{
AllowMutableNodeSelectorAndNodeAffinity: true,
},
err: "spec.nodeSelector: Invalid value:", err: "spec.nodeSelector: Invalid value:",
test: "modifying value of existing node selector is not allowed", test: "modifying value of existing node selector is not allowed",
}, { }, {
@ -13880,9 +13815,6 @@ func TestValidatePodUpdate(t *testing.T) {
SchedulingGates: []core.PodSchedulingGate{{Name: "baz"}}, SchedulingGates: []core.PodSchedulingGate{{Name: "baz"}},
}, },
}, },
opts: PodValidationOptions{
AllowMutableNodeSelectorAndNodeAffinity: true,
},
test: "addition to nodeAffinity is allowed for gated pods", test: "addition to nodeAffinity is allowed for gated pods",
}, { }, {
old: core.Pod{ old: core.Pod{
@ -13911,9 +13843,6 @@ func TestValidatePodUpdate(t *testing.T) {
SchedulingGates: []core.PodSchedulingGate{{Name: "baz"}}, SchedulingGates: []core.PodSchedulingGate{{Name: "baz"}},
}, },
}, },
opts: PodValidationOptions{
AllowMutableNodeSelectorAndNodeAffinity: true,
},
err: "spec.affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms: Invalid value:", err: "spec.affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms: Invalid value:",
test: "old RequiredDuringSchedulingIgnoredDuringExecution is non-nil, new RequiredDuringSchedulingIgnoredDuringExecution is nil, pod is gated", test: "old RequiredDuringSchedulingIgnoredDuringExecution is non-nil, new RequiredDuringSchedulingIgnoredDuringExecution is nil, pod is gated",
}, { }, {
@ -13961,9 +13890,6 @@ func TestValidatePodUpdate(t *testing.T) {
}, },
}, },
}, },
opts: PodValidationOptions{
AllowMutableNodeSelectorAndNodeAffinity: true,
},
err: "Forbidden: pod updates may not change fields other than `spec.containers[*].image", err: "Forbidden: pod updates may not change fields other than `spec.containers[*].image",
test: "addition to nodeAffinity is not allowed for non-gated pods", test: "addition to nodeAffinity is not allowed for non-gated pods",
}, { }, {
@ -14012,9 +13938,6 @@ func TestValidatePodUpdate(t *testing.T) {
}, },
}, },
}, },
opts: PodValidationOptions{
AllowMutableNodeSelectorAndNodeAffinity: true,
},
test: "old pod spec has scheduling gate, new pod spec does not, and node affinity addition occurs", test: "old pod spec has scheduling gate, new pod spec does not, and node affinity addition occurs",
}, { }, {
old: core.Pod{ old: core.Pod{
@ -14053,9 +13976,6 @@ func TestValidatePodUpdate(t *testing.T) {
SchedulingGates: []core.PodSchedulingGate{{Name: "baz"}}, SchedulingGates: []core.PodSchedulingGate{{Name: "baz"}},
}, },
}, },
opts: PodValidationOptions{
AllowMutableNodeSelectorAndNodeAffinity: true,
},
err: "spec.affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms[0]: Invalid value:", err: "spec.affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms[0]: Invalid value:",
test: "nodeAffinity deletion from MatchExpressions not allowed", test: "nodeAffinity deletion from MatchExpressions not allowed",
}, { }, {
@ -14101,9 +14021,6 @@ func TestValidatePodUpdate(t *testing.T) {
SchedulingGates: []core.PodSchedulingGate{{Name: "baz"}}, SchedulingGates: []core.PodSchedulingGate{{Name: "baz"}},
}, },
}, },
opts: PodValidationOptions{
AllowMutableNodeSelectorAndNodeAffinity: true,
},
err: "spec.affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms[0]: Invalid value:", err: "spec.affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms[0]: Invalid value:",
test: "nodeAffinity deletion from MatchFields not allowed", test: "nodeAffinity deletion from MatchFields not allowed",
}, { }, {
@ -14154,9 +14071,6 @@ func TestValidatePodUpdate(t *testing.T) {
SchedulingGates: []core.PodSchedulingGate{{Name: "baz"}}, SchedulingGates: []core.PodSchedulingGate{{Name: "baz"}},
}, },
}, },
opts: PodValidationOptions{
AllowMutableNodeSelectorAndNodeAffinity: true,
},
err: "spec.affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms[0]: Invalid value:", err: "spec.affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms[0]: Invalid value:",
test: "nodeAffinity modification of item in MatchExpressions not allowed", test: "nodeAffinity modification of item in MatchExpressions not allowed",
}, { }, {
@ -14206,9 +14120,6 @@ func TestValidatePodUpdate(t *testing.T) {
SchedulingGates: []core.PodSchedulingGate{{Name: "baz"}}, SchedulingGates: []core.PodSchedulingGate{{Name: "baz"}},
}, },
}, },
opts: PodValidationOptions{
AllowMutableNodeSelectorAndNodeAffinity: true,
},
err: "spec.affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms[0]: Invalid value:", err: "spec.affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms[0]: Invalid value:",
test: "nodeAffinity modification of item in MatchFields not allowed", test: "nodeAffinity modification of item in MatchFields not allowed",
}, { }, {
@ -14269,9 +14180,6 @@ func TestValidatePodUpdate(t *testing.T) {
SchedulingGates: []core.PodSchedulingGate{{Name: "baz"}}, SchedulingGates: []core.PodSchedulingGate{{Name: "baz"}},
}, },
}, },
opts: PodValidationOptions{
AllowMutableNodeSelectorAndNodeAffinity: true,
},
err: "spec.affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms: Invalid value:", err: "spec.affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms: Invalid value:",
test: "nodeSelectorTerms addition on gated pod should fail", test: "nodeSelectorTerms addition on gated pod should fail",
}, { }, {
@ -14313,9 +14221,6 @@ func TestValidatePodUpdate(t *testing.T) {
SchedulingGates: []core.PodSchedulingGate{{Name: "baz"}}, SchedulingGates: []core.PodSchedulingGate{{Name: "baz"}},
}, },
}, },
opts: PodValidationOptions{
AllowMutableNodeSelectorAndNodeAffinity: true,
},
test: "preferredDuringSchedulingIgnoredDuringExecution can modified for gated pods", test: "preferredDuringSchedulingIgnoredDuringExecution can modified for gated pods",
}, { }, {
old: core.Pod{ old: core.Pod{
@ -14365,9 +14270,6 @@ func TestValidatePodUpdate(t *testing.T) {
SchedulingGates: []core.PodSchedulingGate{{Name: "baz"}}, SchedulingGates: []core.PodSchedulingGate{{Name: "baz"}},
}, },
}, },
opts: PodValidationOptions{
AllowMutableNodeSelectorAndNodeAffinity: true,
},
test: "preferredDuringSchedulingIgnoredDuringExecution can have additions for gated pods", test: "preferredDuringSchedulingIgnoredDuringExecution can have additions for gated pods",
}, { }, {
old: core.Pod{ old: core.Pod{
@ -14394,9 +14296,6 @@ func TestValidatePodUpdate(t *testing.T) {
SchedulingGates: []core.PodSchedulingGate{{Name: "baz"}}, SchedulingGates: []core.PodSchedulingGate{{Name: "baz"}},
}, },
}, },
opts: PodValidationOptions{
AllowMutableNodeSelectorAndNodeAffinity: true,
},
test: "preferredDuringSchedulingIgnoredDuringExecution can have removals for gated pods", test: "preferredDuringSchedulingIgnoredDuringExecution can have removals for gated pods",
}, { }, {
old: core.Pod{ old: core.Pod{
@ -14423,9 +14322,6 @@ func TestValidatePodUpdate(t *testing.T) {
SchedulingGates: []core.PodSchedulingGate{{Name: "baz"}}, SchedulingGates: []core.PodSchedulingGate{{Name: "baz"}},
}, },
}, },
opts: PodValidationOptions{
AllowMutableNodeSelectorAndNodeAffinity: true,
},
err: "spec.affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms: Invalid value:", err: "spec.affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms: Invalid value:",
test: "new node affinity is nil", test: "new node affinity is nil",
}, { }, {
@ -14453,9 +14349,6 @@ func TestValidatePodUpdate(t *testing.T) {
SchedulingGates: []core.PodSchedulingGate{{Name: "baz"}}, SchedulingGates: []core.PodSchedulingGate{{Name: "baz"}},
}, },
}, },
opts: PodValidationOptions{
AllowMutableNodeSelectorAndNodeAffinity: true,
},
test: "preferredDuringSchedulingIgnoredDuringExecution can have removals for gated pods", test: "preferredDuringSchedulingIgnoredDuringExecution can have removals for gated pods",
}, { }, {
old: core.Pod{ old: core.Pod{
@ -14490,9 +14383,6 @@ func TestValidatePodUpdate(t *testing.T) {
SchedulingGates: []core.PodSchedulingGate{{Name: "baz"}}, SchedulingGates: []core.PodSchedulingGate{{Name: "baz"}},
}, },
}, },
opts: PodValidationOptions{
AllowMutableNodeSelectorAndNodeAffinity: true,
},
err: "spec.affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms[0]: Invalid value:", err: "spec.affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms[0]: Invalid value:",
test: "empty NodeSelectorTerm (selects nothing) cannot become populated (selects something)", test: "empty NodeSelectorTerm (selects nothing) cannot become populated (selects something)",
}, { }, {
@ -14520,9 +14410,6 @@ func TestValidatePodUpdate(t *testing.T) {
SchedulingGates: []core.PodSchedulingGate{{Name: "baz"}}, SchedulingGates: []core.PodSchedulingGate{{Name: "baz"}},
}, },
}, },
opts: PodValidationOptions{
AllowMutableNodeSelectorAndNodeAffinity: true,
},
test: "nil affinity can be mutated for gated pods", test: "nil affinity can be mutated for gated pods",
}, },
{ {
@ -14560,9 +14447,6 @@ func TestValidatePodUpdate(t *testing.T) {
SchedulingGates: []core.PodSchedulingGate{{Name: "baz"}}, SchedulingGates: []core.PodSchedulingGate{{Name: "baz"}},
}, },
}, },
opts: PodValidationOptions{
AllowMutableNodeSelectorAndNodeAffinity: true,
},
err: "pod updates may not change fields other than", err: "pod updates may not change fields other than",
test: "the podAffinity cannot be updated on gated pods", test: "the podAffinity cannot be updated on gated pods",
}, },
@ -14601,9 +14485,6 @@ func TestValidatePodUpdate(t *testing.T) {
SchedulingGates: []core.PodSchedulingGate{{Name: "baz"}}, SchedulingGates: []core.PodSchedulingGate{{Name: "baz"}},
}, },
}, },
opts: PodValidationOptions{
AllowMutableNodeSelectorAndNodeAffinity: true,
},
err: "pod updates may not change fields other than", err: "pod updates may not change fields other than",
test: "the podAntiAffinity cannot be updated on gated pods", test: "the podAntiAffinity cannot be updated on gated pods",
}, },
@ -14634,7 +14515,7 @@ func TestValidatePodUpdate(t *testing.T) {
test.old.Spec.RestartPolicy = "Always" test.old.Spec.RestartPolicy = "Always"
} }
errs := ValidatePodUpdate(&test.new, &test.old, test.opts) errs := ValidatePodUpdate(&test.new, &test.old, PodValidationOptions{})
if test.err == "" { if test.err == "" {
if len(errs) != 0 { if len(errs) != 0 {
t.Errorf("unexpected invalid: %s (%+v)\nA: %+v\nB: %+v", test.test, errs, test.new, test.old) t.Errorf("unexpected invalid: %s (%+v)\nA: %+v\nB: %+v", test.test, errs, test.new, test.old)

View File

@ -612,6 +612,7 @@ const (
// kep: https://kep.k8s.io/3521 // kep: https://kep.k8s.io/3521
// alpha: v1.26 // alpha: v1.26
// beta: v1.27 // beta: v1.27
// stable: v1.30
// //
// Enable users to specify when a Pod is ready for scheduling. // Enable users to specify when a Pod is ready for scheduling.
PodSchedulingReadiness featuregate.Feature = "PodSchedulingReadiness" PodSchedulingReadiness featuregate.Feature = "PodSchedulingReadiness"
@ -1092,7 +1093,7 @@ var defaultKubernetesFeatureGates = map[featuregate.Feature]featuregate.FeatureS
PodLifecycleSleepAction: {Default: true, PreRelease: featuregate.Beta}, PodLifecycleSleepAction: {Default: true, PreRelease: featuregate.Beta},
PodSchedulingReadiness: {Default: true, PreRelease: featuregate.Beta}, PodSchedulingReadiness: {Default: true, PreRelease: featuregate.GA, LockToDefault: true}, // GA in 1.30; remove in 1.32
ProcMountType: {Default: false, PreRelease: featuregate.Alpha}, ProcMountType: {Default: false, PreRelease: featuregate.Alpha},

View File

@ -42,10 +42,7 @@ import (
apiserverstorage "k8s.io/apiserver/pkg/storage" apiserverstorage "k8s.io/apiserver/pkg/storage"
storeerr "k8s.io/apiserver/pkg/storage/errors" storeerr "k8s.io/apiserver/pkg/storage/errors"
etcd3testing "k8s.io/apiserver/pkg/storage/etcd3/testing" etcd3testing "k8s.io/apiserver/pkg/storage/etcd3/testing"
utilfeature "k8s.io/apiserver/pkg/util/feature"
featuregatetesting "k8s.io/component-base/featuregate/testing"
api "k8s.io/kubernetes/pkg/apis/core" api "k8s.io/kubernetes/pkg/apis/core"
"k8s.io/kubernetes/pkg/features"
"k8s.io/kubernetes/pkg/registry/registrytest" "k8s.io/kubernetes/pkg/registry/registrytest"
"k8s.io/kubernetes/pkg/securitycontext" "k8s.io/kubernetes/pkg/securitycontext"
) )
@ -752,22 +749,11 @@ func TestEtcdCreateWithConflict(t *testing.T) {
func TestEtcdCreateWithSchedulingGates(t *testing.T) { func TestEtcdCreateWithSchedulingGates(t *testing.T) {
tests := []struct { tests := []struct {
name string name string
featureEnabled bool
schedulingGates []api.PodSchedulingGate schedulingGates []api.PodSchedulingGate
wantErr error wantErr error
}{ }{
{ {
name: "pod with non-nil schedulingGates, feature disabled", name: "pod with non-nil schedulingGates",
featureEnabled: false,
schedulingGates: []api.PodSchedulingGate{
{Name: "foo"},
{Name: "bar"},
},
wantErr: nil,
},
{
name: "pod with non-nil schedulingGates, feature enabled",
featureEnabled: true,
schedulingGates: []api.PodSchedulingGate{ schedulingGates: []api.PodSchedulingGate{
{Name: "foo"}, {Name: "foo"},
{Name: "bar"}, {Name: "bar"},
@ -775,26 +761,14 @@ func TestEtcdCreateWithSchedulingGates(t *testing.T) {
wantErr: goerrors.New(`Operation cannot be fulfilled on pods/binding "foo": pod foo has non-empty .spec.schedulingGates`), wantErr: goerrors.New(`Operation cannot be fulfilled on pods/binding "foo": pod foo has non-empty .spec.schedulingGates`),
}, },
{ {
name: "pod with nil schedulingGates, feature disabled", name: "pod with nil schedulingGates",
featureEnabled: false,
schedulingGates: nil,
wantErr: nil,
},
{
name: "pod with nil schedulingGates, feature enabled",
featureEnabled: true,
schedulingGates: nil, schedulingGates: nil,
wantErr: nil, wantErr: nil,
}, },
} }
for _, tt := range tests { for _, tt := range tests {
for _, flipFeatureGateBeforeBinding := range []bool{false, true} {
if flipFeatureGateBeforeBinding {
tt.name = fmt.Sprintf("%v and flipped before binding", tt.name)
}
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.PodSchedulingReadiness, tt.featureEnabled)()
storage, bindingStorage, _, server := newStorage(t) storage, bindingStorage, _, server := newStorage(t)
defer server.Terminate(t) defer server.Terminate(t)
defer storage.Store.DestroyFunc() defer storage.Store.DestroyFunc()
@ -805,9 +779,6 @@ func TestEtcdCreateWithSchedulingGates(t *testing.T) {
if _, err := storage.Create(ctx, pod, rest.ValidateAllObjectFunc, &metav1.CreateOptions{}); err != nil { if _, err := storage.Create(ctx, pod, rest.ValidateAllObjectFunc, &metav1.CreateOptions{}); err != nil {
t.Fatalf("Unexpected error: %v", err) t.Fatalf("Unexpected error: %v", err)
} }
if flipFeatureGateBeforeBinding {
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.PodSchedulingReadiness, !tt.featureEnabled)()
}
_, err := bindingStorage.Create(ctx, "foo", &api.Binding{ _, err := bindingStorage.Create(ctx, "foo", &api.Binding{
ObjectMeta: metav1.ObjectMeta{Namespace: metav1.NamespaceDefault, Name: "foo"}, ObjectMeta: metav1.ObjectMeta{Namespace: metav1.NamespaceDefault, Name: "foo"},
Target: api.ObjectReference{Name: "machine"}, Target: api.ObjectReference{Name: "machine"},
@ -825,7 +796,6 @@ func TestEtcdCreateWithSchedulingGates(t *testing.T) {
} }
}) })
} }
}
} }
func validNewBinding() *api.Binding { func validNewBinding() *api.Binding {

View File

@ -740,8 +740,7 @@ func mutatePodAffinity(pod *api.Pod) {
// applySchedulingGatedCondition adds a {type:PodScheduled, reason:SchedulingGated} condition // applySchedulingGatedCondition adds a {type:PodScheduled, reason:SchedulingGated} condition
// to a new-created Pod if necessary. // to a new-created Pod if necessary.
func applySchedulingGatedCondition(pod *api.Pod) { func applySchedulingGatedCondition(pod *api.Pod) {
if !utilfeature.DefaultFeatureGate.Enabled(features.PodSchedulingReadiness) || if len(pod.Spec.SchedulingGates) == 0 {
len(pod.Spec.SchedulingGates) == 0 {
return return
} }

View File

@ -316,39 +316,20 @@ func TestSchedulingGatedCondition(t *testing.T) {
tests := []struct { tests := []struct {
name string name string
pod *api.Pod pod *api.Pod
featureEnabled bool
want api.PodCondition want api.PodCondition
}{ }{
{ {
name: "pod without .spec.schedulingGates, feature disabled", name: "pod without .spec.schedulingGates",
pod: &api.Pod{}, pod: &api.Pod{},
featureEnabled: false,
want: api.PodCondition{}, want: api.PodCondition{},
}, },
{ {
name: "pod without .spec.schedulingGates, feature enabled", name: "pod with .spec.schedulingGates",
pod: &api.Pod{},
featureEnabled: true,
want: api.PodCondition{},
},
{
name: "pod with .spec.schedulingGates, feature disabled",
pod: &api.Pod{ pod: &api.Pod{
Spec: api.PodSpec{ Spec: api.PodSpec{
SchedulingGates: []api.PodSchedulingGate{{Name: "foo"}}, SchedulingGates: []api.PodSchedulingGate{{Name: "foo"}},
}, },
}, },
featureEnabled: false,
want: api.PodCondition{},
},
{
name: "pod with .spec.schedulingGates, feature enabled",
pod: &api.Pod{
Spec: api.PodSpec{
SchedulingGates: []api.PodSchedulingGate{{Name: "foo"}},
},
},
featureEnabled: true,
want: api.PodCondition{ want: api.PodCondition{
Type: api.PodScheduled, Type: api.PodScheduled,
Status: api.ConditionFalse, Status: api.ConditionFalse,
@ -360,8 +341,6 @@ func TestSchedulingGatedCondition(t *testing.T) {
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.PodSchedulingReadiness, tt.featureEnabled)()
Strategy.PrepareForCreate(genericapirequest.NewContext(), tt.pod) Strategy.PrepareForCreate(genericapirequest.NewContext(), tt.pod)
var got api.PodCondition var got api.PodCondition
for _, condition := range tt.pod.Status.Conditions { for _, condition := range tt.pod.Status.Conditions {

View File

@ -25,6 +25,7 @@ import (
var PluginsV1 = &config.Plugins{ var PluginsV1 = &config.Plugins{
MultiPoint: config.PluginSet{ MultiPoint: config.PluginSet{
Enabled: []config.Plugin{ Enabled: []config.Plugin{
{Name: names.SchedulingGates},
{Name: names.PrioritySort}, {Name: names.PrioritySort},
{Name: names.NodeUnschedulable}, {Name: names.NodeUnschedulable},
{Name: names.NodeName}, {Name: names.NodeName},
@ -45,7 +46,6 @@ var PluginsV1 = &config.Plugins{
{Name: names.NodeResourcesBalancedAllocation, Weight: 1}, {Name: names.NodeResourcesBalancedAllocation, Weight: 1},
{Name: names.ImageLocality, Weight: 1}, {Name: names.ImageLocality, Weight: 1},
{Name: names.DefaultBinder}, {Name: names.DefaultBinder},
{Name: names.SchedulingGates},
}, },
}, },
} }

View File

@ -31,6 +31,7 @@ func getDefaultPlugins() *v1.Plugins {
plugins := &v1.Plugins{ plugins := &v1.Plugins{
MultiPoint: v1.PluginSet{ MultiPoint: v1.PluginSet{
Enabled: []v1.Plugin{ Enabled: []v1.Plugin{
{Name: names.SchedulingGates},
{Name: names.PrioritySort}, {Name: names.PrioritySort},
{Name: names.NodeUnschedulable}, {Name: names.NodeUnschedulable},
{Name: names.NodeName}, {Name: names.NodeName},
@ -60,9 +61,6 @@ func getDefaultPlugins() *v1.Plugins {
} }
func applyFeatureGates(config *v1.Plugins) { func applyFeatureGates(config *v1.Plugins) {
if utilfeature.DefaultFeatureGate.Enabled(features.PodSchedulingReadiness) {
config.MultiPoint.Enabled = append(config.MultiPoint.Enabled, v1.Plugin{Name: names.SchedulingGates})
}
if utilfeature.DefaultFeatureGate.Enabled(features.DynamicResourceAllocation) { if utilfeature.DefaultFeatureGate.Enabled(features.DynamicResourceAllocation) {
// This plugin should come before DefaultPreemption because if // This plugin should come before DefaultPreemption because if
// there is a problem with a Pod and PostFilter gets called to // there is a problem with a Pod and PostFilter gets called to

View File

@ -37,66 +37,34 @@ func TestApplyFeatureGates(t *testing.T) {
wantConfig *v1.Plugins wantConfig *v1.Plugins
}{ }{
{ {
name: "Feature gates disabled", name: "Feature gate DynamicResourceAllocation disabled",
features: map[featuregate.Feature]bool{ features: map[featuregate.Feature]bool{
features.PodSchedulingReadiness: false, features.DynamicResourceAllocation: false,
}, },
wantConfig: &v1.Plugins{ wantConfig: &v1.Plugins{
MultiPoint: v1.PluginSet{ MultiPoint: v1.PluginSet{
Enabled: []v1.Plugin{ Enabled: []v1.Plugin{
{Name: names.PrioritySort},
{Name: names.NodeUnschedulable},
{Name: names.NodeName},
{Name: names.TaintToleration, Weight: ptr.To[int32](3)},
{Name: names.NodeAffinity, Weight: ptr.To[int32](2)},
{Name: names.NodePorts},
{Name: names.NodeResourcesFit, Weight: ptr.To[int32](1)},
{Name: names.VolumeRestrictions},
{Name: names.EBSLimits},
{Name: names.GCEPDLimits},
{Name: names.NodeVolumeLimits},
{Name: names.AzureDiskLimits},
{Name: names.VolumeBinding},
{Name: names.VolumeZone},
{Name: names.PodTopologySpread, Weight: ptr.To[int32](2)},
{Name: names.InterPodAffinity, Weight: ptr.To[int32](2)},
{Name: names.DefaultPreemption},
{Name: names.NodeResourcesBalancedAllocation, Weight: ptr.To[int32](1)},
{Name: names.ImageLocality, Weight: ptr.To[int32](1)},
{Name: names.DefaultBinder},
},
},
},
},
{
name: "Feature gate PodSchedulingReadiness enabled",
features: map[featuregate.Feature]bool{
features.PodSchedulingReadiness: true,
},
wantConfig: &v1.Plugins{
MultiPoint: v1.PluginSet{
Enabled: []v1.Plugin{
{Name: names.PrioritySort},
{Name: names.NodeUnschedulable},
{Name: names.NodeName},
{Name: names.TaintToleration, Weight: ptr.To[int32](3)},
{Name: names.NodeAffinity, Weight: ptr.To[int32](2)},
{Name: names.NodePorts},
{Name: names.NodeResourcesFit, Weight: ptr.To[int32](1)},
{Name: names.VolumeRestrictions},
{Name: names.EBSLimits},
{Name: names.GCEPDLimits},
{Name: names.NodeVolumeLimits},
{Name: names.AzureDiskLimits},
{Name: names.VolumeBinding},
{Name: names.VolumeZone},
{Name: names.PodTopologySpread, Weight: ptr.To[int32](2)},
{Name: names.InterPodAffinity, Weight: ptr.To[int32](2)},
{Name: names.DefaultPreemption},
{Name: names.NodeResourcesBalancedAllocation, Weight: ptr.To[int32](1)},
{Name: names.ImageLocality, Weight: ptr.To[int32](1)},
{Name: names.DefaultBinder},
{Name: names.SchedulingGates}, {Name: names.SchedulingGates},
{Name: names.PrioritySort},
{Name: names.NodeUnschedulable},
{Name: names.NodeName},
{Name: names.TaintToleration, Weight: ptr.To[int32](3)},
{Name: names.NodeAffinity, Weight: ptr.To[int32](2)},
{Name: names.NodePorts},
{Name: names.NodeResourcesFit, Weight: ptr.To[int32](1)},
{Name: names.VolumeRestrictions},
{Name: names.EBSLimits},
{Name: names.GCEPDLimits},
{Name: names.NodeVolumeLimits},
{Name: names.AzureDiskLimits},
{Name: names.VolumeBinding},
{Name: names.VolumeZone},
{Name: names.PodTopologySpread, Weight: ptr.To[int32](2)},
{Name: names.InterPodAffinity, Weight: ptr.To[int32](2)},
{Name: names.DefaultPreemption},
{Name: names.NodeResourcesBalancedAllocation, Weight: ptr.To[int32](1)},
{Name: names.ImageLocality, Weight: ptr.To[int32](1)},
{Name: names.DefaultBinder},
}, },
}, },
}, },
@ -109,6 +77,7 @@ func TestApplyFeatureGates(t *testing.T) {
wantConfig: &v1.Plugins{ wantConfig: &v1.Plugins{
MultiPoint: v1.PluginSet{ MultiPoint: v1.PluginSet{
Enabled: []v1.Plugin{ Enabled: []v1.Plugin{
{Name: names.SchedulingGates},
{Name: names.PrioritySort}, {Name: names.PrioritySort},
{Name: names.NodeUnschedulable}, {Name: names.NodeUnschedulable},
{Name: names.NodeName}, {Name: names.NodeName},
@ -130,7 +99,6 @@ func TestApplyFeatureGates(t *testing.T) {
{Name: names.NodeResourcesBalancedAllocation, Weight: ptr.To[int32](1)}, {Name: names.NodeResourcesBalancedAllocation, Weight: ptr.To[int32](1)},
{Name: names.ImageLocality, Weight: ptr.To[int32](1)}, {Name: names.ImageLocality, Weight: ptr.To[int32](1)},
{Name: names.DefaultBinder}, {Name: names.DefaultBinder},
{Name: names.SchedulingGates},
}, },
}, },
}, },

View File

@ -331,6 +331,7 @@ func TestSchedulerDefaults(t *testing.T) {
Plugins: &configv1.Plugins{ Plugins: &configv1.Plugins{
MultiPoint: configv1.PluginSet{ MultiPoint: configv1.PluginSet{
Enabled: []configv1.Plugin{ Enabled: []configv1.Plugin{
{Name: names.SchedulingGates},
{Name: names.PrioritySort}, {Name: names.PrioritySort},
{Name: names.NodeUnschedulable}, {Name: names.NodeUnschedulable},
{Name: names.NodeName}, {Name: names.NodeName},
@ -351,7 +352,6 @@ func TestSchedulerDefaults(t *testing.T) {
{Name: names.NodeResourcesBalancedAllocation, Weight: ptr.To[int32](1)}, {Name: names.NodeResourcesBalancedAllocation, Weight: ptr.To[int32](1)},
{Name: names.ImageLocality, Weight: ptr.To[int32](1)}, {Name: names.ImageLocality, Weight: ptr.To[int32](1)},
{Name: names.DefaultBinder}, {Name: names.DefaultBinder},
{Name: names.SchedulingGates},
}, },
}, },
Bind: configv1.PluginSet{ Bind: configv1.PluginSet{

View File

@ -25,7 +25,6 @@ type Features struct {
EnableMinDomainsInPodTopologySpread bool EnableMinDomainsInPodTopologySpread bool
EnableNodeInclusionPolicyInPodTopologySpread bool EnableNodeInclusionPolicyInPodTopologySpread bool
EnableMatchLabelKeysInPodTopologySpread bool EnableMatchLabelKeysInPodTopologySpread bool
EnablePodSchedulingReadiness bool
EnablePodDisruptionConditions bool EnablePodDisruptionConditions bool
EnableInPlacePodVerticalScaling bool EnableInPlacePodVerticalScaling bool
EnableSidecarContainers bool EnableSidecarContainers bool

View File

@ -51,7 +51,6 @@ func NewInTreeRegistry() runtime.Registry {
EnableMinDomainsInPodTopologySpread: feature.DefaultFeatureGate.Enabled(features.MinDomainsInPodTopologySpread), EnableMinDomainsInPodTopologySpread: feature.DefaultFeatureGate.Enabled(features.MinDomainsInPodTopologySpread),
EnableNodeInclusionPolicyInPodTopologySpread: feature.DefaultFeatureGate.Enabled(features.NodeInclusionPolicyInPodTopologySpread), EnableNodeInclusionPolicyInPodTopologySpread: feature.DefaultFeatureGate.Enabled(features.NodeInclusionPolicyInPodTopologySpread),
EnableMatchLabelKeysInPodTopologySpread: feature.DefaultFeatureGate.Enabled(features.MatchLabelKeysInPodTopologySpread), EnableMatchLabelKeysInPodTopologySpread: feature.DefaultFeatureGate.Enabled(features.MatchLabelKeysInPodTopologySpread),
EnablePodSchedulingReadiness: feature.DefaultFeatureGate.Enabled(features.PodSchedulingReadiness),
EnablePodDisruptionConditions: feature.DefaultFeatureGate.Enabled(features.PodDisruptionConditions), EnablePodDisruptionConditions: feature.DefaultFeatureGate.Enabled(features.PodDisruptionConditions),
EnableInPlacePodVerticalScaling: feature.DefaultFeatureGate.Enabled(features.InPlacePodVerticalScaling), EnableInPlacePodVerticalScaling: feature.DefaultFeatureGate.Enabled(features.InPlacePodVerticalScaling),
EnableSidecarContainers: feature.DefaultFeatureGate.Enabled(features.SidecarContainers), EnableSidecarContainers: feature.DefaultFeatureGate.Enabled(features.SidecarContainers),
@ -80,7 +79,7 @@ func NewInTreeRegistry() runtime.Registry {
queuesort.Name: queuesort.New, queuesort.Name: queuesort.New,
defaultbinder.Name: defaultbinder.New, defaultbinder.Name: defaultbinder.New,
defaultpreemption.Name: runtime.FactoryAdapter(fts, defaultpreemption.New), defaultpreemption.Name: runtime.FactoryAdapter(fts, defaultpreemption.New),
schedulinggates.Name: runtime.FactoryAdapter(fts, schedulinggates.New), schedulinggates.Name: schedulinggates.New,
} }
return registry return registry

View File

@ -23,7 +23,6 @@ import (
v1 "k8s.io/api/core/v1" v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
"k8s.io/kubernetes/pkg/scheduler/framework" "k8s.io/kubernetes/pkg/scheduler/framework"
"k8s.io/kubernetes/pkg/scheduler/framework/plugins/feature"
"k8s.io/kubernetes/pkg/scheduler/framework/plugins/names" "k8s.io/kubernetes/pkg/scheduler/framework/plugins/names"
) )
@ -31,9 +30,7 @@ import (
const Name = names.SchedulingGates const Name = names.SchedulingGates
// SchedulingGates checks if a Pod carries .spec.schedulingGates. // SchedulingGates checks if a Pod carries .spec.schedulingGates.
type SchedulingGates struct { type SchedulingGates struct{}
EnablePodSchedulingReadiness bool
}
var _ framework.PreEnqueuePlugin = &SchedulingGates{} var _ framework.PreEnqueuePlugin = &SchedulingGates{}
var _ framework.EnqueueExtensions = &SchedulingGates{} var _ framework.EnqueueExtensions = &SchedulingGates{}
@ -43,7 +40,7 @@ func (pl *SchedulingGates) Name() string {
} }
func (pl *SchedulingGates) PreEnqueue(ctx context.Context, p *v1.Pod) *framework.Status { func (pl *SchedulingGates) PreEnqueue(ctx context.Context, p *v1.Pod) *framework.Status {
if !pl.EnablePodSchedulingReadiness || len(p.Spec.SchedulingGates) == 0 { if len(p.Spec.SchedulingGates) == 0 {
return nil return nil
} }
gates := make([]string, 0, len(p.Spec.SchedulingGates)) gates := make([]string, 0, len(p.Spec.SchedulingGates))
@ -60,6 +57,6 @@ func (pl *SchedulingGates) EventsToRegister() []framework.ClusterEventWithHint {
} }
// New initializes a new plugin and returns it. // New initializes a new plugin and returns it.
func New(_ context.Context, _ runtime.Object, _ framework.Handle, fts feature.Features) (framework.Plugin, error) { func New(_ context.Context, _ runtime.Object, _ framework.Handle) (framework.Plugin, error) {
return &SchedulingGates{EnablePodSchedulingReadiness: fts.EnablePodSchedulingReadiness}, nil return &SchedulingGates{}, nil
} }

View File

@ -23,7 +23,6 @@ import (
v1 "k8s.io/api/core/v1" v1 "k8s.io/api/core/v1"
"k8s.io/kubernetes/pkg/scheduler/framework" "k8s.io/kubernetes/pkg/scheduler/framework"
"k8s.io/kubernetes/pkg/scheduler/framework/plugins/feature"
st "k8s.io/kubernetes/pkg/scheduler/testing" st "k8s.io/kubernetes/pkg/scheduler/testing"
"k8s.io/kubernetes/test/utils/ktesting" "k8s.io/kubernetes/test/utils/ktesting"
) )
@ -32,31 +31,16 @@ func TestPreEnqueue(t *testing.T) {
tests := []struct { tests := []struct {
name string name string
pod *v1.Pod pod *v1.Pod
enablePodSchedulingReadiness bool
want *framework.Status want *framework.Status
}{ }{
{ {
name: "pod does not carry scheduling gates, feature disabled", name: "pod does not carry scheduling gates",
pod: st.MakePod().Name("p").Obj(), pod: st.MakePod().Name("p").Obj(),
enablePodSchedulingReadiness: false,
want: nil, want: nil,
}, },
{ {
name: "pod does not carry scheduling gates, feature enabled", name: "pod carries scheduling gates",
pod: st.MakePod().Name("p").Obj(),
enablePodSchedulingReadiness: true,
want: nil,
},
{
name: "pod carries scheduling gates, feature disabled",
pod: st.MakePod().Name("p").SchedulingGates([]string{"foo", "bar"}).Obj(), pod: st.MakePod().Name("p").SchedulingGates([]string{"foo", "bar"}).Obj(),
enablePodSchedulingReadiness: false,
want: nil,
},
{
name: "pod carries scheduling gates, feature enabled",
pod: st.MakePod().Name("p").SchedulingGates([]string{"foo", "bar"}).Obj(),
enablePodSchedulingReadiness: true,
want: framework.NewStatus(framework.UnschedulableAndUnresolvable, "waiting for scheduling gates: [foo bar]"), want: framework.NewStatus(framework.UnschedulableAndUnresolvable, "waiting for scheduling gates: [foo bar]"),
}, },
} }
@ -64,7 +48,7 @@ func TestPreEnqueue(t *testing.T) {
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
_, ctx := ktesting.NewTestContext(t) _, ctx := ktesting.NewTestContext(t)
p, err := New(ctx, nil, nil, feature.Features{EnablePodSchedulingReadiness: tt.enablePodSchedulingReadiness}) p, err := New(ctx, nil, nil)
if err != nil { if err != nil {
t.Fatalf("Creating plugin: %v", err) t.Fatalf("Creating plugin: %v", err)
} }

View File

@ -3797,13 +3797,10 @@ type PodSpec struct {
// //
// SchedulingGates can only be set at pod creation time, and be removed only afterwards. // SchedulingGates can only be set at pod creation time, and be removed only afterwards.
// //
// This is a beta feature enabled by the PodSchedulingReadiness feature gate.
//
// +patchMergeKey=name // +patchMergeKey=name
// +patchStrategy=merge // +patchStrategy=merge
// +listType=map // +listType=map
// +listMapKey=name // +listMapKey=name
// +featureGate=PodSchedulingReadiness
// +optional // +optional
SchedulingGates []PodSchedulingGate `json:"schedulingGates,omitempty" patchStrategy:"merge" patchMergeKey:"name" protobuf:"bytes,38,opt,name=schedulingGates"` SchedulingGates []PodSchedulingGate `json:"schedulingGates,omitempty" patchStrategy:"merge" patchMergeKey:"name" protobuf:"bytes,38,opt,name=schedulingGates"`
// ResourceClaims defines which ResourceClaims must be allocated // ResourceClaims defines which ResourceClaims must be allocated

View File

@ -25,12 +25,9 @@ import (
v1 "k8s.io/api/core/v1" v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/types"
utilfeature "k8s.io/apiserver/pkg/util/feature"
clientset "k8s.io/client-go/kubernetes" clientset "k8s.io/client-go/kubernetes"
typedv1 "k8s.io/client-go/kubernetes/typed/core/v1" typedv1 "k8s.io/client-go/kubernetes/typed/core/v1"
featuregatetesting "k8s.io/component-base/featuregate/testing"
kubeapiservertesting "k8s.io/kubernetes/cmd/kube-apiserver/app/testing" kubeapiservertesting "k8s.io/kubernetes/cmd/kube-apiserver/app/testing"
"k8s.io/kubernetes/pkg/features"
"k8s.io/kubernetes/test/integration" "k8s.io/kubernetes/test/integration"
"k8s.io/kubernetes/test/integration/framework" "k8s.io/kubernetes/test/integration/framework"
) )
@ -692,44 +689,8 @@ func TestMutablePodSchedulingDirectives(t *testing.T) {
name string name string
create *v1.Pod create *v1.Pod
update *v1.Pod update *v1.Pod
enableSchedulingGates bool
err string err string
}{ }{
{
name: "node selector is immutable when AllowMutableNodeSelector is false",
create: &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "test-pod",
},
Spec: v1.PodSpec{
Containers: []v1.Container{
{
Name: "fake-name",
Image: "fakeimage",
},
},
SchedulingGates: []v1.PodSchedulingGate{{Name: "baz"}},
},
},
update: &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "test-pod",
},
Spec: v1.PodSpec{
Containers: []v1.Container{
{
Name: "fake-name",
Image: "fakeimage",
},
},
NodeSelector: map[string]string{
"foo": "bar",
},
SchedulingGates: []v1.PodSchedulingGate{{Name: "baz"}},
},
},
err: "Forbidden: pod updates may not change fields other than `spec.containers[*].image",
},
{ {
name: "adding node selector is allowed for gated pods", name: "adding node selector is allowed for gated pods",
create: &v1.Pod{ create: &v1.Pod{
@ -763,7 +724,6 @@ func TestMutablePodSchedulingDirectives(t *testing.T) {
SchedulingGates: []v1.PodSchedulingGate{{Name: "baz"}}, SchedulingGates: []v1.PodSchedulingGate{{Name: "baz"}},
}, },
}, },
enableSchedulingGates: true,
}, },
{ {
name: "addition to nodeAffinity is allowed for gated pods", name: "addition to nodeAffinity is allowed for gated pods",
@ -842,7 +802,6 @@ func TestMutablePodSchedulingDirectives(t *testing.T) {
SchedulingGates: []v1.PodSchedulingGate{{Name: "baz"}}, SchedulingGates: []v1.PodSchedulingGate{{Name: "baz"}},
}, },
}, },
enableSchedulingGates: true,
}, },
{ {
name: "addition to nodeAffinity is allowed for gated pods with nil affinity", name: "addition to nodeAffinity is allowed for gated pods with nil affinity",
@ -899,12 +858,9 @@ func TestMutablePodSchedulingDirectives(t *testing.T) {
SchedulingGates: []v1.PodSchedulingGate{{Name: "baz"}}, SchedulingGates: []v1.PodSchedulingGate{{Name: "baz"}},
}, },
}, },
enableSchedulingGates: true,
}, },
} }
for _, tc := range cases { for _, tc := range cases {
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.PodSchedulingReadiness, tc.enableSchedulingGates)()
if _, err := client.CoreV1().Pods(ns.Name).Create(context.TODO(), tc.create, metav1.CreateOptions{}); err != nil { if _, err := client.CoreV1().Pods(ns.Name).Create(context.TODO(), tc.create, metav1.CreateOptions{}); err != nil {
t.Errorf("Failed to create pod: %v", err) t.Errorf("Failed to create pod: %v", err)
} }

View File

@ -33,12 +33,9 @@ import (
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/wait" "k8s.io/apimachinery/pkg/util/wait"
"k8s.io/apiserver/pkg/util/feature"
clientset "k8s.io/client-go/kubernetes" clientset "k8s.io/client-go/kubernetes"
listersv1 "k8s.io/client-go/listers/core/v1" listersv1 "k8s.io/client-go/listers/core/v1"
featuregatetesting "k8s.io/component-base/featuregate/testing"
configv1 "k8s.io/kube-scheduler/config/v1" configv1 "k8s.io/kube-scheduler/config/v1"
"k8s.io/kubernetes/pkg/features"
"k8s.io/kubernetes/pkg/scheduler" "k8s.io/kubernetes/pkg/scheduler"
schedulerconfig "k8s.io/kubernetes/pkg/scheduler/apis/config" schedulerconfig "k8s.io/kubernetes/pkg/scheduler/apis/config"
configtesting "k8s.io/kubernetes/pkg/scheduler/apis/config/testing" configtesting "k8s.io/kubernetes/pkg/scheduler/apis/config/testing"
@ -2199,8 +2196,6 @@ func TestPreScorePlugin(t *testing.T) {
// TestPreEnqueuePlugin tests invocation of enqueue plugins. // TestPreEnqueuePlugin tests invocation of enqueue plugins.
func TestPreEnqueuePlugin(t *testing.T) { func TestPreEnqueuePlugin(t *testing.T) {
defer featuregatetesting.SetFeatureGateDuringTest(t, feature.DefaultFeatureGate, features.PodSchedulingReadiness, true)()
testContext := testutils.InitTestAPIServer(t, "enqueue-plugin", nil) testContext := testutils.InitTestAPIServer(t, "enqueue-plugin", nil)
tests := []struct { tests := []struct {
@ -2636,8 +2631,6 @@ func (pl *SchedulingGatesPluginWOEvents) EventsToRegister() []framework.ClusterE
// This test helps to verify registering nil events for schedulingGates plugin works as expected. // This test helps to verify registering nil events for schedulingGates plugin works as expected.
func TestSchedulingGatesPluginEventsToRegister(t *testing.T) { func TestSchedulingGatesPluginEventsToRegister(t *testing.T) {
defer featuregatetesting.SetFeatureGateDuringTest(t, feature.DefaultFeatureGate, features.PodSchedulingReadiness, true)()
testContext := testutils.InitTestAPIServer(t, "preenqueue-plugin", nil) testContext := testutils.InitTestAPIServer(t, "preenqueue-plugin", nil)
num := func(pl framework.Plugin) int { num := func(pl framework.Plugin) int {
@ -2659,12 +2652,12 @@ func TestSchedulingGatesPluginEventsToRegister(t *testing.T) {
}{ }{
{ {
name: "preEnqueue plugin without event registered", name: "preEnqueue plugin without event registered",
enqueuePlugin: &SchedulingGatesPluginWOEvents{SchedulingGates: schedulinggates.SchedulingGates{EnablePodSchedulingReadiness: true}}, enqueuePlugin: &SchedulingGatesPluginWOEvents{SchedulingGates: schedulinggates.SchedulingGates{}},
count: 2, count: 2,
}, },
{ {
name: "preEnqueue plugin with event registered", name: "preEnqueue plugin with event registered",
enqueuePlugin: &SchedulingGatesPluginWithEvents{SchedulingGates: schedulinggates.SchedulingGates{EnablePodSchedulingReadiness: true}}, enqueuePlugin: &SchedulingGatesPluginWithEvents{SchedulingGates: schedulinggates.SchedulingGates{}},
count: 3, count: 3,
}, },
} }

View File

@ -58,55 +58,33 @@ func TestSchedulingGates(t *testing.T) {
tests := []struct { tests := []struct {
name string name string
pods []*v1.Pod pods []*v1.Pod
featureEnabled bool
want []string want []string
rmPodsSchedulingGates []int rmPodsSchedulingGates []int
wantPostGatesRemoval []string wantPostGatesRemoval []string
}{ }{
{ {
name: "feature disabled, regular pods", name: "regular pods",
pods: []*v1.Pod{ pods: []*v1.Pod{
st.MakePod().Name("p1").Container("pause").Obj(), st.MakePod().Name("p1").Container("pause").Obj(),
st.MakePod().Name("p2").Container("pause").Obj(), st.MakePod().Name("p2").Container("pause").Obj(),
}, },
featureEnabled: false,
want: []string{"p1", "p2"}, want: []string{"p1", "p2"},
}, },
{ {
name: "feature enabled, regular pods", name: "one pod carrying scheduling gates",
pods: []*v1.Pod{
st.MakePod().Name("p1").Container("pause").Obj(),
st.MakePod().Name("p2").Container("pause").Obj(),
},
featureEnabled: true,
want: []string{"p1", "p2"},
},
{
name: "feature disabled, one pod carrying scheduling gates",
pods: []*v1.Pod{ pods: []*v1.Pod{
st.MakePod().Name("p1").SchedulingGates([]string{"foo"}).Container("pause").Obj(), st.MakePod().Name("p1").SchedulingGates([]string{"foo"}).Container("pause").Obj(),
st.MakePod().Name("p2").Container("pause").Obj(), st.MakePod().Name("p2").Container("pause").Obj(),
}, },
featureEnabled: false,
want: []string{"p1", "p2"},
},
{
name: "feature enabled, one pod carrying scheduling gates",
pods: []*v1.Pod{
st.MakePod().Name("p1").SchedulingGates([]string{"foo"}).Container("pause").Obj(),
st.MakePod().Name("p2").Container("pause").Obj(),
},
featureEnabled: true,
want: []string{"p2"}, want: []string{"p2"},
}, },
{ {
name: "feature enabled, two pod carrying scheduling gates, and remove gates of one pod", name: "two pod carrying scheduling gates, and remove gates of one pod",
pods: []*v1.Pod{ pods: []*v1.Pod{
st.MakePod().Name("p1").SchedulingGates([]string{"foo"}).Container("pause").Obj(), st.MakePod().Name("p1").SchedulingGates([]string{"foo"}).Container("pause").Obj(),
st.MakePod().Name("p2").SchedulingGates([]string{"bar"}).Container("pause").Obj(), st.MakePod().Name("p2").SchedulingGates([]string{"bar"}).Container("pause").Obj(),
st.MakePod().Name("p3").Container("pause").Obj(), st.MakePod().Name("p3").Container("pause").Obj(),
}, },
featureEnabled: true,
want: []string{"p3"}, want: []string{"p3"},
rmPodsSchedulingGates: []int{1}, // remove gates of 'p2' rmPodsSchedulingGates: []int{1}, // remove gates of 'p2'
wantPostGatesRemoval: []string{"p2"}, wantPostGatesRemoval: []string{"p2"},
@ -115,8 +93,6 @@ func TestSchedulingGates(t *testing.T) {
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.PodSchedulingReadiness, tt.featureEnabled)()
// Use zero backoff seconds to bypass backoffQ. // Use zero backoff seconds to bypass backoffQ.
// It's intended to not start the scheduler's queue, and hence to // It's intended to not start the scheduler's queue, and hence to
// not start any flushing logic. We will pop and schedule the Pods manually later. // not start any flushing logic. We will pop and schedule the Pods manually later.