Merge pull request #123575 from Huang-Wei/pod-scheduling-readiness-stable

Graduate PodSchedulingReadiness to stable
This commit is contained in:
Kubernetes Prow Robot 2024-03-03 22:29:38 -08:00 committed by GitHub
commit e4a14fe0f5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
29 changed files with 97 additions and 521 deletions

View File

@ -9046,7 +9046,7 @@
"type": "string" "type": "string"
}, },
"schedulingGates": { "schedulingGates": {
"description": "SchedulingGates is an opaque list of values that if specified will block scheduling the pod. If schedulingGates is not empty, the pod will stay in the SchedulingGated state and the scheduler will not attempt to schedule the pod.\n\nSchedulingGates can only be set at pod creation time, and be removed only afterwards.\n\nThis is a beta feature enabled by the PodSchedulingReadiness feature gate.", "description": "SchedulingGates is an opaque list of values that if specified will block scheduling the pod. If schedulingGates is not empty, the pod will stay in the SchedulingGated state and the scheduler will not attempt to schedule the pod.\n\nSchedulingGates can only be set at pod creation time, and be removed only afterwards.",
"items": { "items": {
"$ref": "#/definitions/io.k8s.api.core.v1.PodSchedulingGate" "$ref": "#/definitions/io.k8s.api.core.v1.PodSchedulingGate"
}, },

View File

@ -5465,7 +5465,7 @@
"type": "string" "type": "string"
}, },
"schedulingGates": { "schedulingGates": {
"description": "SchedulingGates is an opaque list of values that if specified will block scheduling the pod. If schedulingGates is not empty, the pod will stay in the SchedulingGated state and the scheduler will not attempt to schedule the pod.\n\nSchedulingGates can only be set at pod creation time, and be removed only afterwards.\n\nThis is a beta feature enabled by the PodSchedulingReadiness feature gate.", "description": "SchedulingGates is an opaque list of values that if specified will block scheduling the pod. If schedulingGates is not empty, the pod will stay in the SchedulingGated state and the scheduler will not attempt to schedule the pod.\n\nSchedulingGates can only be set at pod creation time, and be removed only afterwards.",
"items": { "items": {
"allOf": [ "allOf": [
{ {

View File

@ -3882,7 +3882,7 @@
"type": "string" "type": "string"
}, },
"schedulingGates": { "schedulingGates": {
"description": "SchedulingGates is an opaque list of values that if specified will block scheduling the pod. If schedulingGates is not empty, the pod will stay in the SchedulingGated state and the scheduler will not attempt to schedule the pod.\n\nSchedulingGates can only be set at pod creation time, and be removed only afterwards.\n\nThis is a beta feature enabled by the PodSchedulingReadiness feature gate.", "description": "SchedulingGates is an opaque list of values that if specified will block scheduling the pod. If schedulingGates is not empty, the pod will stay in the SchedulingGated state and the scheduler will not attempt to schedule the pod.\n\nSchedulingGates can only be set at pod creation time, and be removed only afterwards.",
"items": { "items": {
"allOf": [ "allOf": [
{ {

View File

@ -3037,7 +3037,7 @@
"type": "string" "type": "string"
}, },
"schedulingGates": { "schedulingGates": {
"description": "SchedulingGates is an opaque list of values that if specified will block scheduling the pod. If schedulingGates is not empty, the pod will stay in the SchedulingGated state and the scheduler will not attempt to schedule the pod.\n\nSchedulingGates can only be set at pod creation time, and be removed only afterwards.\n\nThis is a beta feature enabled by the PodSchedulingReadiness feature gate.", "description": "SchedulingGates is an opaque list of values that if specified will block scheduling the pod. If schedulingGates is not empty, the pod will stay in the SchedulingGated state and the scheduler will not attempt to schedule the pod.\n\nSchedulingGates can only be set at pod creation time, and be removed only afterwards.",
"items": { "items": {
"allOf": [ "allOf": [
{ {

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

@ -2563,88 +2563,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",
}, { }, {
@ -13784,10 +13728,7 @@ 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

@ -614,6 +614,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"
@ -1097,7 +1098,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

@ -26465,7 +26465,7 @@ func schema_k8sio_api_core_v1_PodSpec(ref common.ReferenceCallback) common.OpenA
}, },
}, },
SchemaProps: spec.SchemaProps{ SchemaProps: spec.SchemaProps{
Description: "SchedulingGates is an opaque list of values that if specified will block scheduling the pod. If schedulingGates is not empty, the pod will stay in the SchedulingGated state and the scheduler will not attempt to schedule the pod.\n\nSchedulingGates can only be set at pod creation time, and be removed only afterwards.\n\nThis is a beta feature enabled by the PodSchedulingReadiness feature gate.", Description: "SchedulingGates is an opaque list of values that if specified will block scheduling the pod. If schedulingGates is not empty, the pod will stay in the SchedulingGated state and the scheduler will not attempt to schedule the pod.\n\nSchedulingGates can only be set at pod creation time, and be removed only afterwards.",
Type: []string{"array"}, Type: []string{"array"},
Items: &spec.SchemaOrArray{ Items: &spec.SchemaOrArray{
Schema: &spec.Schema{ Schema: &spec.Schema{

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,56 +761,40 @@ 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} { t.Run(tt.name, func(t *testing.T) {
if flipFeatureGateBeforeBinding { storage, bindingStorage, _, server := newStorage(t)
tt.name = fmt.Sprintf("%v and flipped before binding", tt.name) defer server.Terminate(t)
} defer storage.Store.DestroyFunc()
t.Run(tt.name, func(t *testing.T) { ctx := genericapirequest.NewDefaultContext()
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.PodSchedulingReadiness, tt.featureEnabled)()
storage, bindingStorage, _, server := newStorage(t)
defer server.Terminate(t)
defer storage.Store.DestroyFunc()
ctx := genericapirequest.NewDefaultContext()
pod := validNewPod() pod := validNewPod()
pod.Spec.SchedulingGates = tt.schedulingGates pod.Spec.SchedulingGates = tt.schedulingGates
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)
}
_, err := bindingStorage.Create(ctx, "foo", &api.Binding{
ObjectMeta: metav1.ObjectMeta{Namespace: metav1.NamespaceDefault, Name: "foo"},
Target: api.ObjectReference{Name: "machine"},
}, rest.ValidateAllObjectFunc, &metav1.CreateOptions{})
if tt.wantErr == nil {
if err != nil {
t.Errorf("Want nil err, but got %v", err)
} }
if flipFeatureGateBeforeBinding { } else {
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.PodSchedulingReadiness, !tt.featureEnabled)() if err == nil {
t.Errorf("Want %v, but got nil err", tt.wantErr)
} else if tt.wantErr.Error() != err.Error() {
t.Errorf("Want %v, but got %v", tt.wantErr, err)
} }
_, err := bindingStorage.Create(ctx, "foo", &api.Binding{ }
ObjectMeta: metav1.ObjectMeta{Namespace: metav1.NamespaceDefault, Name: "foo"}, })
Target: api.ObjectReference{Name: "machine"},
}, rest.ValidateAllObjectFunc, &metav1.CreateOptions{})
if tt.wantErr == nil {
if err != nil {
t.Errorf("Want nil err, but got %v", err)
}
} else {
if err == nil {
t.Errorf("Want %v, but got nil err", tt.wantErr)
} else if tt.wantErr.Error() != err.Error() {
t.Errorf("Want %v, but got %v", tt.wantErr, err)
}
}
})
}
} }
} }

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

@ -314,41 +314,22 @@ func TestGetPodQOS(t *testing.T) {
func TestSchedulingGatedCondition(t *testing.T) { 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,48 +23,32 @@ 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"
) )
func TestPreEnqueue(t *testing.T) { 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(), pod: st.MakePod().Name("p").SchedulingGates([]string{"foo", "bar"}).Obj(),
enablePodSchedulingReadiness: true, want: framework.NewStatus(framework.UnschedulableAndUnresolvable, "waiting for scheduling gates: [foo bar]"),
want: nil,
},
{
name: "pod carries scheduling gates, feature disabled",
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]"),
}, },
} }
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

@ -4194,13 +4194,10 @@ message PodSpec {
// //
// 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
repeated PodSchedulingGate schedulingGates = 38; repeated PodSchedulingGate schedulingGates = 38;

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

@ -1759,7 +1759,7 @@ var map_PodSpec = map[string]string{
"setHostnameAsFQDN": "If true the pod's hostname will be configured as the pod's FQDN, rather than the leaf name (the default). In Linux containers, this means setting the FQDN in the hostname field of the kernel (the nodename field of struct utsname). In Windows containers, this means setting the registry value of hostname for the registry key HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters to FQDN. If a pod does not have FQDN, this has no effect. Default to false.", "setHostnameAsFQDN": "If true the pod's hostname will be configured as the pod's FQDN, rather than the leaf name (the default). In Linux containers, this means setting the FQDN in the hostname field of the kernel (the nodename field of struct utsname). In Windows containers, this means setting the registry value of hostname for the registry key HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters to FQDN. If a pod does not have FQDN, this has no effect. Default to false.",
"os": "Specifies the OS of the containers in the pod. Some pod and container fields are restricted if this is set.\n\nIf the OS field is set to linux, the following fields must be unset: -securityContext.windowsOptions\n\nIf the OS field is set to windows, following fields must be unset: - spec.hostPID - spec.hostIPC - spec.hostUsers - spec.securityContext.seLinuxOptions - spec.securityContext.seccompProfile - spec.securityContext.fsGroup - spec.securityContext.fsGroupChangePolicy - spec.securityContext.sysctls - spec.shareProcessNamespace - spec.securityContext.runAsUser - spec.securityContext.runAsGroup - spec.securityContext.supplementalGroups - spec.containers[*].securityContext.seLinuxOptions - spec.containers[*].securityContext.seccompProfile - spec.containers[*].securityContext.capabilities - spec.containers[*].securityContext.readOnlyRootFilesystem - spec.containers[*].securityContext.privileged - spec.containers[*].securityContext.allowPrivilegeEscalation - spec.containers[*].securityContext.procMount - spec.containers[*].securityContext.runAsUser - spec.containers[*].securityContext.runAsGroup", "os": "Specifies the OS of the containers in the pod. Some pod and container fields are restricted if this is set.\n\nIf the OS field is set to linux, the following fields must be unset: -securityContext.windowsOptions\n\nIf the OS field is set to windows, following fields must be unset: - spec.hostPID - spec.hostIPC - spec.hostUsers - spec.securityContext.seLinuxOptions - spec.securityContext.seccompProfile - spec.securityContext.fsGroup - spec.securityContext.fsGroupChangePolicy - spec.securityContext.sysctls - spec.shareProcessNamespace - spec.securityContext.runAsUser - spec.securityContext.runAsGroup - spec.securityContext.supplementalGroups - spec.containers[*].securityContext.seLinuxOptions - spec.containers[*].securityContext.seccompProfile - spec.containers[*].securityContext.capabilities - spec.containers[*].securityContext.readOnlyRootFilesystem - spec.containers[*].securityContext.privileged - spec.containers[*].securityContext.allowPrivilegeEscalation - spec.containers[*].securityContext.procMount - spec.containers[*].securityContext.runAsUser - spec.containers[*].securityContext.runAsGroup",
"hostUsers": "Use the host's user namespace. Optional: Default to true. If set to true or not present, the pod will be run in the host user namespace, useful for when the pod needs a feature only available to the host user namespace, such as loading a kernel module with CAP_SYS_MODULE. When set to false, a new userns is created for the pod. Setting false is useful for mitigating container breakout vulnerabilities even allowing users to run their containers as root without actually having root privileges on the host. This field is alpha-level and is only honored by servers that enable the UserNamespacesSupport feature.", "hostUsers": "Use the host's user namespace. Optional: Default to true. If set to true or not present, the pod will be run in the host user namespace, useful for when the pod needs a feature only available to the host user namespace, such as loading a kernel module with CAP_SYS_MODULE. When set to false, a new userns is created for the pod. Setting false is useful for mitigating container breakout vulnerabilities even allowing users to run their containers as root without actually having root privileges on the host. This field is alpha-level and is only honored by servers that enable the UserNamespacesSupport feature.",
"schedulingGates": "SchedulingGates is an opaque list of values that if specified will block scheduling the pod. If schedulingGates is not empty, the pod will stay in the SchedulingGated state and the scheduler will not attempt to schedule the pod.\n\nSchedulingGates can only be set at pod creation time, and be removed only afterwards.\n\nThis is a beta feature enabled by the PodSchedulingReadiness feature gate.", "schedulingGates": "SchedulingGates is an opaque list of values that if specified will block scheduling the pod. If schedulingGates is not empty, the pod will stay in the SchedulingGated state and the scheduler will not attempt to schedule the pod.\n\nSchedulingGates can only be set at pod creation time, and be removed only afterwards.",
"resourceClaims": "ResourceClaims defines which ResourceClaims must be allocated and reserved before the Pod is allowed to start. The resources will be made available to those containers which consume them by name.\n\nThis is an alpha field and requires enabling the DynamicResourceAllocation feature gate.\n\nThis field is immutable.", "resourceClaims": "ResourceClaims defines which ResourceClaims must be allocated and reserved before the Pod is allowed to start. The resources will be made available to those containers which consume them by name.\n\nThis is an alpha field and requires enabling the DynamicResourceAllocation feature gate.\n\nThis field is immutable.",
} }

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"
) )
@ -689,47 +686,11 @@ func TestMutablePodSchedulingDirectives(t *testing.T) {
defer framework.DeleteNamespaceOrDie(client, ns, t) defer framework.DeleteNamespaceOrDie(client, ns, t)
cases := []struct { cases := []struct {
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{"p2"},
want: []string{"p1", "p2"},
}, },
{ {
name: "feature enabled, one pod carrying scheduling gates", name: "two pod carrying scheduling gates, and remove gates of one pod",
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"},
},
{
name: "feature enabled, 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.