mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-08-10 20:42:26 +00:00
Graduate PodSchedulingReadiness to stable
This commit is contained in:
parent
a67973a45c
commit
01db4ae9e7
@ -239,23 +239,6 @@ leaderElection:
|
||||
"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",
|
||||
flags: []string{
|
||||
|
@ -382,7 +382,6 @@ func GetValidationOptionsFromPodSpecAndMeta(podSpec, oldPodSpec *api.PodSpec, po
|
||||
AllowIndivisibleHugePagesValues: false,
|
||||
AllowInvalidLabelValueInSelector: false,
|
||||
AllowInvalidTopologySpreadConstraintLabelSelector: false,
|
||||
AllowMutableNodeSelectorAndNodeAffinity: utilfeature.DefaultFeatureGate.Enabled(features.PodSchedulingReadiness),
|
||||
AllowNamespacedSysctlsForHostNetAndHostIPC: 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)
|
||||
|
||||
dropDisabledTopologySpreadConstraintsFields(podSpec, oldPodSpec)
|
||||
@ -983,14 +977,6 @@ func appArmorInUse(podAnnotations map[string]string) bool {
|
||||
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
|
||||
// it has any init container with ContainerRestartPolicyAlways.
|
||||
func restartableInitContainersInUse(podSpec *api.PodSpec) bool {
|
||||
|
@ -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) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
|
@ -3352,9 +3352,6 @@ type PodSpec struct {
|
||||
//
|
||||
// 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
|
||||
SchedulingGates []PodSchedulingGate
|
||||
// ResourceClaims defines which ResourceClaims must be allocated
|
||||
|
@ -3974,8 +3974,6 @@ type PodValidationOptions struct {
|
||||
AllowHostIPsField bool
|
||||
// Allow invalid topologySpreadConstraint labelSelector for backward compatibility
|
||||
AllowInvalidTopologySpreadConstraintLabelSelector bool
|
||||
// Allow node selector additions for gated pods.
|
||||
AllowMutableNodeSelectorAndNodeAffinity bool
|
||||
// Allow projected token volumes with non-local paths
|
||||
AllowNonLocalProjectedTokenPath bool
|
||||
// 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.
|
||||
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.
|
||||
if !apiequality.Semantic.DeepEqual(mungedPodSpec.NodeSelector, oldPod.Spec.NodeSelector) {
|
||||
allErrs = append(allErrs, validateNodeSelectorMutation(specPath.Child("nodeSelector"), mungedPodSpec.NodeSelector, oldPod.Spec.NodeSelector)...)
|
||||
|
@ -12298,22 +12298,8 @@ func TestValidatePodCreateWithSchedulingGates(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
pod *core.Pod
|
||||
featureEnabled bool
|
||||
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",
|
||||
pod: &core.Pod{
|
||||
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")},
|
||||
}, {
|
||||
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",
|
||||
pod: &core.Pod{
|
||||
@ -12348,15 +12321,12 @@ func TestValidatePodCreateWithSchedulingGates(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
featureEnabled: true,
|
||||
wantFieldErrors: nil,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.PodSchedulingReadiness, tt.featureEnabled)()
|
||||
|
||||
applyEssentials(tt.pod)
|
||||
errs := ValidatePodCreate(tt.pod, PodValidationOptions{})
|
||||
if diff := cmp.Diff(tt.wantFieldErrors, errs); diff != "" {
|
||||
@ -12383,7 +12353,6 @@ func TestValidatePodUpdate(t *testing.T) {
|
||||
tests := []struct {
|
||||
new core.Pod
|
||||
old core.Pod
|
||||
opts PodValidationOptions
|
||||
err string
|
||||
test string
|
||||
}{
|
||||
@ -13716,25 +13685,6 @@ func TestValidatePodUpdate(t *testing.T) {
|
||||
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",
|
||||
}, {
|
||||
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",
|
||||
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"}},
|
||||
},
|
||||
},
|
||||
opts: PodValidationOptions{
|
||||
AllowMutableNodeSelectorAndNodeAffinity: true,
|
||||
},
|
||||
err: "spec.nodeSelector: Invalid value:",
|
||||
test: "removing node selector is not allowed for gated pods",
|
||||
}, {
|
||||
@ -13785,9 +13729,6 @@ func TestValidatePodUpdate(t *testing.T) {
|
||||
},
|
||||
},
|
||||
new: core.Pod{},
|
||||
opts: PodValidationOptions{
|
||||
AllowMutableNodeSelectorAndNodeAffinity: true,
|
||||
},
|
||||
err: "Forbidden: pod updates may not change fields other than `spec.containers[*].image",
|
||||
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",
|
||||
}, {
|
||||
old: core.Pod{
|
||||
@ -13828,9 +13766,6 @@ func TestValidatePodUpdate(t *testing.T) {
|
||||
SchedulingGates: []core.PodSchedulingGate{{Name: "baz"}},
|
||||
},
|
||||
},
|
||||
opts: PodValidationOptions{
|
||||
AllowMutableNodeSelectorAndNodeAffinity: true,
|
||||
},
|
||||
err: "spec.nodeSelector: Invalid value:",
|
||||
test: "modifying value of existing node selector is not allowed",
|
||||
}, {
|
||||
@ -13880,9 +13815,6 @@ func TestValidatePodUpdate(t *testing.T) {
|
||||
SchedulingGates: []core.PodSchedulingGate{{Name: "baz"}},
|
||||
},
|
||||
},
|
||||
opts: PodValidationOptions{
|
||||
AllowMutableNodeSelectorAndNodeAffinity: true,
|
||||
},
|
||||
test: "addition to nodeAffinity is allowed for gated pods",
|
||||
}, {
|
||||
old: core.Pod{
|
||||
@ -13911,9 +13843,6 @@ func TestValidatePodUpdate(t *testing.T) {
|
||||
SchedulingGates: []core.PodSchedulingGate{{Name: "baz"}},
|
||||
},
|
||||
},
|
||||
opts: PodValidationOptions{
|
||||
AllowMutableNodeSelectorAndNodeAffinity: true,
|
||||
},
|
||||
err: "spec.affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms: Invalid value:",
|
||||
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",
|
||||
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",
|
||||
}, {
|
||||
old: core.Pod{
|
||||
@ -14053,9 +13976,6 @@ func TestValidatePodUpdate(t *testing.T) {
|
||||
SchedulingGates: []core.PodSchedulingGate{{Name: "baz"}},
|
||||
},
|
||||
},
|
||||
opts: PodValidationOptions{
|
||||
AllowMutableNodeSelectorAndNodeAffinity: true,
|
||||
},
|
||||
err: "spec.affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms[0]: Invalid value:",
|
||||
test: "nodeAffinity deletion from MatchExpressions not allowed",
|
||||
}, {
|
||||
@ -14101,9 +14021,6 @@ func TestValidatePodUpdate(t *testing.T) {
|
||||
SchedulingGates: []core.PodSchedulingGate{{Name: "baz"}},
|
||||
},
|
||||
},
|
||||
opts: PodValidationOptions{
|
||||
AllowMutableNodeSelectorAndNodeAffinity: true,
|
||||
},
|
||||
err: "spec.affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms[0]: Invalid value:",
|
||||
test: "nodeAffinity deletion from MatchFields not allowed",
|
||||
}, {
|
||||
@ -14154,9 +14071,6 @@ func TestValidatePodUpdate(t *testing.T) {
|
||||
SchedulingGates: []core.PodSchedulingGate{{Name: "baz"}},
|
||||
},
|
||||
},
|
||||
opts: PodValidationOptions{
|
||||
AllowMutableNodeSelectorAndNodeAffinity: true,
|
||||
},
|
||||
err: "spec.affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms[0]: Invalid value:",
|
||||
test: "nodeAffinity modification of item in MatchExpressions not allowed",
|
||||
}, {
|
||||
@ -14206,9 +14120,6 @@ func TestValidatePodUpdate(t *testing.T) {
|
||||
SchedulingGates: []core.PodSchedulingGate{{Name: "baz"}},
|
||||
},
|
||||
},
|
||||
opts: PodValidationOptions{
|
||||
AllowMutableNodeSelectorAndNodeAffinity: true,
|
||||
},
|
||||
err: "spec.affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms[0]: Invalid value:",
|
||||
test: "nodeAffinity modification of item in MatchFields not allowed",
|
||||
}, {
|
||||
@ -14269,9 +14180,6 @@ func TestValidatePodUpdate(t *testing.T) {
|
||||
SchedulingGates: []core.PodSchedulingGate{{Name: "baz"}},
|
||||
},
|
||||
},
|
||||
opts: PodValidationOptions{
|
||||
AllowMutableNodeSelectorAndNodeAffinity: true,
|
||||
},
|
||||
err: "spec.affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms: Invalid value:",
|
||||
test: "nodeSelectorTerms addition on gated pod should fail",
|
||||
}, {
|
||||
@ -14313,9 +14221,6 @@ func TestValidatePodUpdate(t *testing.T) {
|
||||
SchedulingGates: []core.PodSchedulingGate{{Name: "baz"}},
|
||||
},
|
||||
},
|
||||
opts: PodValidationOptions{
|
||||
AllowMutableNodeSelectorAndNodeAffinity: true,
|
||||
},
|
||||
test: "preferredDuringSchedulingIgnoredDuringExecution can modified for gated pods",
|
||||
}, {
|
||||
old: core.Pod{
|
||||
@ -14365,9 +14270,6 @@ func TestValidatePodUpdate(t *testing.T) {
|
||||
SchedulingGates: []core.PodSchedulingGate{{Name: "baz"}},
|
||||
},
|
||||
},
|
||||
opts: PodValidationOptions{
|
||||
AllowMutableNodeSelectorAndNodeAffinity: true,
|
||||
},
|
||||
test: "preferredDuringSchedulingIgnoredDuringExecution can have additions for gated pods",
|
||||
}, {
|
||||
old: core.Pod{
|
||||
@ -14394,9 +14296,6 @@ func TestValidatePodUpdate(t *testing.T) {
|
||||
SchedulingGates: []core.PodSchedulingGate{{Name: "baz"}},
|
||||
},
|
||||
},
|
||||
opts: PodValidationOptions{
|
||||
AllowMutableNodeSelectorAndNodeAffinity: true,
|
||||
},
|
||||
test: "preferredDuringSchedulingIgnoredDuringExecution can have removals for gated pods",
|
||||
}, {
|
||||
old: core.Pod{
|
||||
@ -14423,9 +14322,6 @@ func TestValidatePodUpdate(t *testing.T) {
|
||||
SchedulingGates: []core.PodSchedulingGate{{Name: "baz"}},
|
||||
},
|
||||
},
|
||||
opts: PodValidationOptions{
|
||||
AllowMutableNodeSelectorAndNodeAffinity: true,
|
||||
},
|
||||
err: "spec.affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms: Invalid value:",
|
||||
test: "new node affinity is nil",
|
||||
}, {
|
||||
@ -14453,9 +14349,6 @@ func TestValidatePodUpdate(t *testing.T) {
|
||||
SchedulingGates: []core.PodSchedulingGate{{Name: "baz"}},
|
||||
},
|
||||
},
|
||||
opts: PodValidationOptions{
|
||||
AllowMutableNodeSelectorAndNodeAffinity: true,
|
||||
},
|
||||
test: "preferredDuringSchedulingIgnoredDuringExecution can have removals for gated pods",
|
||||
}, {
|
||||
old: core.Pod{
|
||||
@ -14490,9 +14383,6 @@ func TestValidatePodUpdate(t *testing.T) {
|
||||
SchedulingGates: []core.PodSchedulingGate{{Name: "baz"}},
|
||||
},
|
||||
},
|
||||
opts: PodValidationOptions{
|
||||
AllowMutableNodeSelectorAndNodeAffinity: true,
|
||||
},
|
||||
err: "spec.affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms[0]: Invalid value:",
|
||||
test: "empty NodeSelectorTerm (selects nothing) cannot become populated (selects something)",
|
||||
}, {
|
||||
@ -14520,9 +14410,6 @@ func TestValidatePodUpdate(t *testing.T) {
|
||||
SchedulingGates: []core.PodSchedulingGate{{Name: "baz"}},
|
||||
},
|
||||
},
|
||||
opts: PodValidationOptions{
|
||||
AllowMutableNodeSelectorAndNodeAffinity: true,
|
||||
},
|
||||
test: "nil affinity can be mutated for gated pods",
|
||||
},
|
||||
{
|
||||
@ -14560,9 +14447,6 @@ func TestValidatePodUpdate(t *testing.T) {
|
||||
SchedulingGates: []core.PodSchedulingGate{{Name: "baz"}},
|
||||
},
|
||||
},
|
||||
opts: PodValidationOptions{
|
||||
AllowMutableNodeSelectorAndNodeAffinity: true,
|
||||
},
|
||||
err: "pod updates may not change fields other than",
|
||||
test: "the podAffinity cannot be updated on gated pods",
|
||||
},
|
||||
@ -14601,9 +14485,6 @@ func TestValidatePodUpdate(t *testing.T) {
|
||||
SchedulingGates: []core.PodSchedulingGate{{Name: "baz"}},
|
||||
},
|
||||
},
|
||||
opts: PodValidationOptions{
|
||||
AllowMutableNodeSelectorAndNodeAffinity: true,
|
||||
},
|
||||
err: "pod updates may not change fields other than",
|
||||
test: "the podAntiAffinity cannot be updated on gated pods",
|
||||
},
|
||||
@ -14634,7 +14515,7 @@ func TestValidatePodUpdate(t *testing.T) {
|
||||
test.old.Spec.RestartPolicy = "Always"
|
||||
}
|
||||
|
||||
errs := ValidatePodUpdate(&test.new, &test.old, test.opts)
|
||||
errs := ValidatePodUpdate(&test.new, &test.old, PodValidationOptions{})
|
||||
if test.err == "" {
|
||||
if len(errs) != 0 {
|
||||
t.Errorf("unexpected invalid: %s (%+v)\nA: %+v\nB: %+v", test.test, errs, test.new, test.old)
|
||||
|
@ -612,6 +612,7 @@ const (
|
||||
// kep: https://kep.k8s.io/3521
|
||||
// alpha: v1.26
|
||||
// beta: v1.27
|
||||
// stable: v1.30
|
||||
//
|
||||
// Enable users to specify when a Pod is ready for scheduling.
|
||||
PodSchedulingReadiness featuregate.Feature = "PodSchedulingReadiness"
|
||||
@ -1092,7 +1093,7 @@ var defaultKubernetesFeatureGates = map[featuregate.Feature]featuregate.FeatureS
|
||||
|
||||
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},
|
||||
|
||||
|
@ -42,10 +42,7 @@ import (
|
||||
apiserverstorage "k8s.io/apiserver/pkg/storage"
|
||||
storeerr "k8s.io/apiserver/pkg/storage/errors"
|
||||
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"
|
||||
"k8s.io/kubernetes/pkg/features"
|
||||
"k8s.io/kubernetes/pkg/registry/registrytest"
|
||||
"k8s.io/kubernetes/pkg/securitycontext"
|
||||
)
|
||||
@ -752,22 +749,11 @@ func TestEtcdCreateWithConflict(t *testing.T) {
|
||||
func TestEtcdCreateWithSchedulingGates(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
featureEnabled bool
|
||||
schedulingGates []api.PodSchedulingGate
|
||||
wantErr error
|
||||
}{
|
||||
{
|
||||
name: "pod with non-nil schedulingGates, feature disabled",
|
||||
featureEnabled: false,
|
||||
schedulingGates: []api.PodSchedulingGate{
|
||||
{Name: "foo"},
|
||||
{Name: "bar"},
|
||||
},
|
||||
wantErr: nil,
|
||||
},
|
||||
{
|
||||
name: "pod with non-nil schedulingGates, feature enabled",
|
||||
featureEnabled: true,
|
||||
name: "pod with non-nil schedulingGates",
|
||||
schedulingGates: []api.PodSchedulingGate{
|
||||
{Name: "foo"},
|
||||
{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`),
|
||||
},
|
||||
{
|
||||
name: "pod with nil schedulingGates, feature disabled",
|
||||
featureEnabled: false,
|
||||
schedulingGates: nil,
|
||||
wantErr: nil,
|
||||
},
|
||||
{
|
||||
name: "pod with nil schedulingGates, feature enabled",
|
||||
featureEnabled: true,
|
||||
name: "pod with nil schedulingGates",
|
||||
schedulingGates: nil,
|
||||
wantErr: nil,
|
||||
},
|
||||
}
|
||||
|
||||
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) {
|
||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.PodSchedulingReadiness, tt.featureEnabled)()
|
||||
storage, bindingStorage, _, server := newStorage(t)
|
||||
defer server.Terminate(t)
|
||||
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 {
|
||||
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{
|
||||
ObjectMeta: metav1.ObjectMeta{Namespace: metav1.NamespaceDefault, Name: "foo"},
|
||||
Target: api.ObjectReference{Name: "machine"},
|
||||
@ -825,7 +796,6 @@ func TestEtcdCreateWithSchedulingGates(t *testing.T) {
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func validNewBinding() *api.Binding {
|
||||
|
@ -740,8 +740,7 @@ func mutatePodAffinity(pod *api.Pod) {
|
||||
// applySchedulingGatedCondition adds a {type:PodScheduled, reason:SchedulingGated} condition
|
||||
// to a new-created Pod if necessary.
|
||||
func applySchedulingGatedCondition(pod *api.Pod) {
|
||||
if !utilfeature.DefaultFeatureGate.Enabled(features.PodSchedulingReadiness) ||
|
||||
len(pod.Spec.SchedulingGates) == 0 {
|
||||
if len(pod.Spec.SchedulingGates) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -316,39 +316,20 @@ func TestSchedulingGatedCondition(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
pod *api.Pod
|
||||
featureEnabled bool
|
||||
want api.PodCondition
|
||||
}{
|
||||
{
|
||||
name: "pod without .spec.schedulingGates, feature disabled",
|
||||
name: "pod without .spec.schedulingGates",
|
||||
pod: &api.Pod{},
|
||||
featureEnabled: false,
|
||||
want: api.PodCondition{},
|
||||
},
|
||||
{
|
||||
name: "pod without .spec.schedulingGates, feature enabled",
|
||||
pod: &api.Pod{},
|
||||
featureEnabled: true,
|
||||
want: api.PodCondition{},
|
||||
},
|
||||
{
|
||||
name: "pod with .spec.schedulingGates, feature disabled",
|
||||
name: "pod with .spec.schedulingGates",
|
||||
pod: &api.Pod{
|
||||
Spec: api.PodSpec{
|
||||
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{
|
||||
Type: api.PodScheduled,
|
||||
Status: api.ConditionFalse,
|
||||
@ -360,8 +341,6 @@ func TestSchedulingGatedCondition(t *testing.T) {
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.PodSchedulingReadiness, tt.featureEnabled)()
|
||||
|
||||
Strategy.PrepareForCreate(genericapirequest.NewContext(), tt.pod)
|
||||
var got api.PodCondition
|
||||
for _, condition := range tt.pod.Status.Conditions {
|
||||
|
@ -25,6 +25,7 @@ import (
|
||||
var PluginsV1 = &config.Plugins{
|
||||
MultiPoint: config.PluginSet{
|
||||
Enabled: []config.Plugin{
|
||||
{Name: names.SchedulingGates},
|
||||
{Name: names.PrioritySort},
|
||||
{Name: names.NodeUnschedulable},
|
||||
{Name: names.NodeName},
|
||||
@ -45,7 +46,6 @@ var PluginsV1 = &config.Plugins{
|
||||
{Name: names.NodeResourcesBalancedAllocation, Weight: 1},
|
||||
{Name: names.ImageLocality, Weight: 1},
|
||||
{Name: names.DefaultBinder},
|
||||
{Name: names.SchedulingGates},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
@ -31,6 +31,7 @@ func getDefaultPlugins() *v1.Plugins {
|
||||
plugins := &v1.Plugins{
|
||||
MultiPoint: v1.PluginSet{
|
||||
Enabled: []v1.Plugin{
|
||||
{Name: names.SchedulingGates},
|
||||
{Name: names.PrioritySort},
|
||||
{Name: names.NodeUnschedulable},
|
||||
{Name: names.NodeName},
|
||||
@ -60,9 +61,6 @@ func getDefaultPlugins() *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) {
|
||||
// This plugin should come before DefaultPreemption because if
|
||||
// there is a problem with a Pod and PostFilter gets called to
|
||||
|
@ -37,66 +37,34 @@ func TestApplyFeatureGates(t *testing.T) {
|
||||
wantConfig *v1.Plugins
|
||||
}{
|
||||
{
|
||||
name: "Feature gates disabled",
|
||||
name: "Feature gate DynamicResourceAllocation disabled",
|
||||
features: map[featuregate.Feature]bool{
|
||||
features.PodSchedulingReadiness: false,
|
||||
features.DynamicResourceAllocation: false,
|
||||
},
|
||||
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: "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.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{
|
||||
MultiPoint: v1.PluginSet{
|
||||
Enabled: []v1.Plugin{
|
||||
{Name: names.SchedulingGates},
|
||||
{Name: names.PrioritySort},
|
||||
{Name: names.NodeUnschedulable},
|
||||
{Name: names.NodeName},
|
||||
@ -130,7 +99,6 @@ func TestApplyFeatureGates(t *testing.T) {
|
||||
{Name: names.NodeResourcesBalancedAllocation, Weight: ptr.To[int32](1)},
|
||||
{Name: names.ImageLocality, Weight: ptr.To[int32](1)},
|
||||
{Name: names.DefaultBinder},
|
||||
{Name: names.SchedulingGates},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -331,6 +331,7 @@ func TestSchedulerDefaults(t *testing.T) {
|
||||
Plugins: &configv1.Plugins{
|
||||
MultiPoint: configv1.PluginSet{
|
||||
Enabled: []configv1.Plugin{
|
||||
{Name: names.SchedulingGates},
|
||||
{Name: names.PrioritySort},
|
||||
{Name: names.NodeUnschedulable},
|
||||
{Name: names.NodeName},
|
||||
@ -351,7 +352,6 @@ func TestSchedulerDefaults(t *testing.T) {
|
||||
{Name: names.NodeResourcesBalancedAllocation, Weight: ptr.To[int32](1)},
|
||||
{Name: names.ImageLocality, Weight: ptr.To[int32](1)},
|
||||
{Name: names.DefaultBinder},
|
||||
{Name: names.SchedulingGates},
|
||||
},
|
||||
},
|
||||
Bind: configv1.PluginSet{
|
||||
|
@ -25,7 +25,6 @@ type Features struct {
|
||||
EnableMinDomainsInPodTopologySpread bool
|
||||
EnableNodeInclusionPolicyInPodTopologySpread bool
|
||||
EnableMatchLabelKeysInPodTopologySpread bool
|
||||
EnablePodSchedulingReadiness bool
|
||||
EnablePodDisruptionConditions bool
|
||||
EnableInPlacePodVerticalScaling bool
|
||||
EnableSidecarContainers bool
|
||||
|
@ -51,7 +51,6 @@ func NewInTreeRegistry() runtime.Registry {
|
||||
EnableMinDomainsInPodTopologySpread: feature.DefaultFeatureGate.Enabled(features.MinDomainsInPodTopologySpread),
|
||||
EnableNodeInclusionPolicyInPodTopologySpread: feature.DefaultFeatureGate.Enabled(features.NodeInclusionPolicyInPodTopologySpread),
|
||||
EnableMatchLabelKeysInPodTopologySpread: feature.DefaultFeatureGate.Enabled(features.MatchLabelKeysInPodTopologySpread),
|
||||
EnablePodSchedulingReadiness: feature.DefaultFeatureGate.Enabled(features.PodSchedulingReadiness),
|
||||
EnablePodDisruptionConditions: feature.DefaultFeatureGate.Enabled(features.PodDisruptionConditions),
|
||||
EnableInPlacePodVerticalScaling: feature.DefaultFeatureGate.Enabled(features.InPlacePodVerticalScaling),
|
||||
EnableSidecarContainers: feature.DefaultFeatureGate.Enabled(features.SidecarContainers),
|
||||
@ -80,7 +79,7 @@ func NewInTreeRegistry() runtime.Registry {
|
||||
queuesort.Name: queuesort.New,
|
||||
defaultbinder.Name: defaultbinder.New,
|
||||
defaultpreemption.Name: runtime.FactoryAdapter(fts, defaultpreemption.New),
|
||||
schedulinggates.Name: runtime.FactoryAdapter(fts, schedulinggates.New),
|
||||
schedulinggates.Name: schedulinggates.New,
|
||||
}
|
||||
|
||||
return registry
|
||||
|
@ -23,7 +23,6 @@ import (
|
||||
v1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/kubernetes/pkg/scheduler/framework"
|
||||
"k8s.io/kubernetes/pkg/scheduler/framework/plugins/feature"
|
||||
"k8s.io/kubernetes/pkg/scheduler/framework/plugins/names"
|
||||
)
|
||||
|
||||
@ -31,9 +30,7 @@ import (
|
||||
const Name = names.SchedulingGates
|
||||
|
||||
// SchedulingGates checks if a Pod carries .spec.schedulingGates.
|
||||
type SchedulingGates struct {
|
||||
EnablePodSchedulingReadiness bool
|
||||
}
|
||||
type SchedulingGates struct{}
|
||||
|
||||
var _ framework.PreEnqueuePlugin = &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 {
|
||||
if !pl.EnablePodSchedulingReadiness || len(p.Spec.SchedulingGates) == 0 {
|
||||
if len(p.Spec.SchedulingGates) == 0 {
|
||||
return nil
|
||||
}
|
||||
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.
|
||||
func New(_ context.Context, _ runtime.Object, _ framework.Handle, fts feature.Features) (framework.Plugin, error) {
|
||||
return &SchedulingGates{EnablePodSchedulingReadiness: fts.EnablePodSchedulingReadiness}, nil
|
||||
func New(_ context.Context, _ runtime.Object, _ framework.Handle) (framework.Plugin, error) {
|
||||
return &SchedulingGates{}, nil
|
||||
}
|
||||
|
@ -23,7 +23,6 @@ import (
|
||||
|
||||
v1 "k8s.io/api/core/v1"
|
||||
"k8s.io/kubernetes/pkg/scheduler/framework"
|
||||
"k8s.io/kubernetes/pkg/scheduler/framework/plugins/feature"
|
||||
st "k8s.io/kubernetes/pkg/scheduler/testing"
|
||||
"k8s.io/kubernetes/test/utils/ktesting"
|
||||
)
|
||||
@ -32,31 +31,16 @@ func TestPreEnqueue(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
pod *v1.Pod
|
||||
enablePodSchedulingReadiness bool
|
||||
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(),
|
||||
enablePodSchedulingReadiness: false,
|
||||
want: nil,
|
||||
},
|
||||
{
|
||||
name: "pod does not carry scheduling gates, feature enabled",
|
||||
pod: st.MakePod().Name("p").Obj(),
|
||||
enablePodSchedulingReadiness: true,
|
||||
want: nil,
|
||||
},
|
||||
{
|
||||
name: "pod carries scheduling gates, feature disabled",
|
||||
name: "pod carries scheduling gates",
|
||||
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]"),
|
||||
},
|
||||
}
|
||||
@ -64,7 +48,7 @@ func TestPreEnqueue(t *testing.T) {
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.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 {
|
||||
t.Fatalf("Creating plugin: %v", err)
|
||||
}
|
||||
|
@ -3797,13 +3797,10 @@ type PodSpec struct {
|
||||
//
|
||||
// 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
|
||||
// +patchStrategy=merge
|
||||
// +listType=map
|
||||
// +listMapKey=name
|
||||
// +featureGate=PodSchedulingReadiness
|
||||
// +optional
|
||||
SchedulingGates []PodSchedulingGate `json:"schedulingGates,omitempty" patchStrategy:"merge" patchMergeKey:"name" protobuf:"bytes,38,opt,name=schedulingGates"`
|
||||
// ResourceClaims defines which ResourceClaims must be allocated
|
||||
|
@ -25,12 +25,9 @@ import (
|
||||
v1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||
clientset "k8s.io/client-go/kubernetes"
|
||||
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"
|
||||
"k8s.io/kubernetes/pkg/features"
|
||||
"k8s.io/kubernetes/test/integration"
|
||||
"k8s.io/kubernetes/test/integration/framework"
|
||||
)
|
||||
@ -692,44 +689,8 @@ func TestMutablePodSchedulingDirectives(t *testing.T) {
|
||||
name string
|
||||
create *v1.Pod
|
||||
update *v1.Pod
|
||||
enableSchedulingGates bool
|
||||
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",
|
||||
create: &v1.Pod{
|
||||
@ -763,7 +724,6 @@ func TestMutablePodSchedulingDirectives(t *testing.T) {
|
||||
SchedulingGates: []v1.PodSchedulingGate{{Name: "baz"}},
|
||||
},
|
||||
},
|
||||
enableSchedulingGates: true,
|
||||
},
|
||||
{
|
||||
name: "addition to nodeAffinity is allowed for gated pods",
|
||||
@ -842,7 +802,6 @@ func TestMutablePodSchedulingDirectives(t *testing.T) {
|
||||
SchedulingGates: []v1.PodSchedulingGate{{Name: "baz"}},
|
||||
},
|
||||
},
|
||||
enableSchedulingGates: true,
|
||||
},
|
||||
{
|
||||
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"}},
|
||||
},
|
||||
},
|
||||
enableSchedulingGates: true,
|
||||
},
|
||||
}
|
||||
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 {
|
||||
t.Errorf("Failed to create pod: %v", err)
|
||||
}
|
||||
|
@ -33,12 +33,9 @@ import (
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
"k8s.io/apiserver/pkg/util/feature"
|
||||
clientset "k8s.io/client-go/kubernetes"
|
||||
listersv1 "k8s.io/client-go/listers/core/v1"
|
||||
featuregatetesting "k8s.io/component-base/featuregate/testing"
|
||||
configv1 "k8s.io/kube-scheduler/config/v1"
|
||||
"k8s.io/kubernetes/pkg/features"
|
||||
"k8s.io/kubernetes/pkg/scheduler"
|
||||
schedulerconfig "k8s.io/kubernetes/pkg/scheduler/apis/config"
|
||||
configtesting "k8s.io/kubernetes/pkg/scheduler/apis/config/testing"
|
||||
@ -2199,8 +2196,6 @@ func TestPreScorePlugin(t *testing.T) {
|
||||
|
||||
// TestPreEnqueuePlugin tests invocation of enqueue plugins.
|
||||
func TestPreEnqueuePlugin(t *testing.T) {
|
||||
defer featuregatetesting.SetFeatureGateDuringTest(t, feature.DefaultFeatureGate, features.PodSchedulingReadiness, true)()
|
||||
|
||||
testContext := testutils.InitTestAPIServer(t, "enqueue-plugin", nil)
|
||||
|
||||
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.
|
||||
func TestSchedulingGatesPluginEventsToRegister(t *testing.T) {
|
||||
defer featuregatetesting.SetFeatureGateDuringTest(t, feature.DefaultFeatureGate, features.PodSchedulingReadiness, true)()
|
||||
|
||||
testContext := testutils.InitTestAPIServer(t, "preenqueue-plugin", nil)
|
||||
|
||||
num := func(pl framework.Plugin) int {
|
||||
@ -2659,12 +2652,12 @@ func TestSchedulingGatesPluginEventsToRegister(t *testing.T) {
|
||||
}{
|
||||
{
|
||||
name: "preEnqueue plugin without event registered",
|
||||
enqueuePlugin: &SchedulingGatesPluginWOEvents{SchedulingGates: schedulinggates.SchedulingGates{EnablePodSchedulingReadiness: true}},
|
||||
enqueuePlugin: &SchedulingGatesPluginWOEvents{SchedulingGates: schedulinggates.SchedulingGates{}},
|
||||
count: 2,
|
||||
},
|
||||
{
|
||||
name: "preEnqueue plugin with event registered",
|
||||
enqueuePlugin: &SchedulingGatesPluginWithEvents{SchedulingGates: schedulinggates.SchedulingGates{EnablePodSchedulingReadiness: true}},
|
||||
enqueuePlugin: &SchedulingGatesPluginWithEvents{SchedulingGates: schedulinggates.SchedulingGates{}},
|
||||
count: 3,
|
||||
},
|
||||
}
|
||||
|
@ -58,55 +58,33 @@ func TestSchedulingGates(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
pods []*v1.Pod
|
||||
featureEnabled bool
|
||||
want []string
|
||||
rmPodsSchedulingGates []int
|
||||
wantPostGatesRemoval []string
|
||||
}{
|
||||
{
|
||||
name: "feature disabled, regular pods",
|
||||
name: "regular pods",
|
||||
pods: []*v1.Pod{
|
||||
st.MakePod().Name("p1").Container("pause").Obj(),
|
||||
st.MakePod().Name("p2").Container("pause").Obj(),
|
||||
},
|
||||
featureEnabled: false,
|
||||
want: []string{"p1", "p2"},
|
||||
},
|
||||
{
|
||||
name: "feature enabled, regular pods",
|
||||
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",
|
||||
name: "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: 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"},
|
||||
},
|
||||
{
|
||||
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{
|
||||
st.MakePod().Name("p1").SchedulingGates([]string{"foo"}).Container("pause").Obj(),
|
||||
st.MakePod().Name("p2").SchedulingGates([]string{"bar"}).Container("pause").Obj(),
|
||||
st.MakePod().Name("p3").Container("pause").Obj(),
|
||||
},
|
||||
featureEnabled: true,
|
||||
want: []string{"p3"},
|
||||
rmPodsSchedulingGates: []int{1}, // remove gates of 'p2'
|
||||
wantPostGatesRemoval: []string{"p2"},
|
||||
@ -115,8 +93,6 @@ func TestSchedulingGates(t *testing.T) {
|
||||
|
||||
for _, tt := range tests {
|
||||
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.
|
||||
// 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.
|
||||
|
Loading…
Reference in New Issue
Block a user