Merge pull request #111441 from denkensk/respect-topology
Respect PodTopologySpread after rolling upgrades
This commit is contained in:
commit
3902a53419
8
api/openapi-spec/swagger.json
generated
8
api/openapi-spec/swagger.json
generated
@ -9652,6 +9652,14 @@
|
||||
"$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector",
|
||||
"description": "LabelSelector is used to find matching pods. Pods that match this label selector are counted to determine the number of pods in their corresponding topology domain."
|
||||
},
|
||||
"matchLabelKeys": {
|
||||
"description": "MatchLabelKeys is a set of pod label keys to select the pods over which spreading will be calculated. The keys are used to lookup values from the incoming pod labels, those key-value labels are ANDed with labelSelector to select the group of existing pods over which spreading will be calculated for the incoming pod. Keys that don't exist in the incoming pod labels will be ignored. A null or empty list means only match against labelSelector.",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": "array",
|
||||
"x-kubernetes-list-type": "atomic"
|
||||
},
|
||||
"maxSkew": {
|
||||
"description": "MaxSkew describes the degree to which pods may be unevenly distributed. When `whenUnsatisfiable=DoNotSchedule`, it is the maximum permitted difference between the number of matching pods in the target topology and the global minimum. The global minimum is the minimum number of matching pods in an eligible domain or zero if the number of eligible domains is less than MinDomains. For example, in a 3-zone cluster, MaxSkew is set to 1, and pods with the same labelSelector spread as 2/2/1: In this case, the global minimum is 1. | zone1 | zone2 | zone3 | | P P | P P | P | - if MaxSkew is 1, incoming pod can only be scheduled to zone3 to become 2/2/2; scheduling it onto zone1(zone2) would make the ActualSkew(3-1) on zone1(zone2) violate MaxSkew(1). - if MaxSkew is 2, incoming pod can be scheduled onto any zone. When `whenUnsatisfiable=ScheduleAnyway`, it is used to give higher precedence to topologies that satisfy it. It's a required field. Default value is 1 and 0 is not allowed.",
|
||||
"format": "int32",
|
||||
|
@ -7251,6 +7251,15 @@
|
||||
],
|
||||
"description": "LabelSelector is used to find matching pods. Pods that match this label selector are counted to determine the number of pods in their corresponding topology domain."
|
||||
},
|
||||
"matchLabelKeys": {
|
||||
"description": "MatchLabelKeys is a set of pod label keys to select the pods over which spreading will be calculated. The keys are used to lookup values from the incoming pod labels, those key-value labels are ANDed with labelSelector to select the group of existing pods over which spreading will be calculated for the incoming pod. Keys that don't exist in the incoming pod labels will be ignored. A null or empty list means only match against labelSelector.",
|
||||
"items": {
|
||||
"default": "",
|
||||
"type": "string"
|
||||
},
|
||||
"type": "array",
|
||||
"x-kubernetes-list-type": "atomic"
|
||||
},
|
||||
"maxSkew": {
|
||||
"default": 0,
|
||||
"description": "MaxSkew describes the degree to which pods may be unevenly distributed. When `whenUnsatisfiable=DoNotSchedule`, it is the maximum permitted difference between the number of matching pods in the target topology and the global minimum. The global minimum is the minimum number of matching pods in an eligible domain or zero if the number of eligible domains is less than MinDomains. For example, in a 3-zone cluster, MaxSkew is set to 1, and pods with the same labelSelector spread as 2/2/1: In this case, the global minimum is 1. | zone1 | zone2 | zone3 | | P P | P P | P | - if MaxSkew is 1, incoming pod can only be scheduled to zone3 to become 2/2/2; scheduling it onto zone1(zone2) would make the ActualSkew(3-1) on zone1(zone2) violate MaxSkew(1). - if MaxSkew is 2, incoming pod can be scheduled onto any zone. When `whenUnsatisfiable=ScheduleAnyway`, it is used to give higher precedence to topologies that satisfy it. It's a required field. Default value is 1 and 0 is not allowed.",
|
||||
|
@ -4348,6 +4348,15 @@
|
||||
],
|
||||
"description": "LabelSelector is used to find matching pods. Pods that match this label selector are counted to determine the number of pods in their corresponding topology domain."
|
||||
},
|
||||
"matchLabelKeys": {
|
||||
"description": "MatchLabelKeys is a set of pod label keys to select the pods over which spreading will be calculated. The keys are used to lookup values from the incoming pod labels, those key-value labels are ANDed with labelSelector to select the group of existing pods over which spreading will be calculated for the incoming pod. Keys that don't exist in the incoming pod labels will be ignored. A null or empty list means only match against labelSelector.",
|
||||
"items": {
|
||||
"default": "",
|
||||
"type": "string"
|
||||
},
|
||||
"type": "array",
|
||||
"x-kubernetes-list-type": "atomic"
|
||||
},
|
||||
"maxSkew": {
|
||||
"default": 0,
|
||||
"description": "MaxSkew describes the degree to which pods may be unevenly distributed. When `whenUnsatisfiable=DoNotSchedule`, it is the maximum permitted difference between the number of matching pods in the target topology and the global minimum. The global minimum is the minimum number of matching pods in an eligible domain or zero if the number of eligible domains is less than MinDomains. For example, in a 3-zone cluster, MaxSkew is set to 1, and pods with the same labelSelector spread as 2/2/1: In this case, the global minimum is 1. | zone1 | zone2 | zone3 | | P P | P P | P | - if MaxSkew is 1, incoming pod can only be scheduled to zone3 to become 2/2/2; scheduling it onto zone1(zone2) would make the ActualSkew(3-1) on zone1(zone2) violate MaxSkew(1). - if MaxSkew is 2, incoming pod can be scheduled onto any zone. When `whenUnsatisfiable=ScheduleAnyway`, it is used to give higher precedence to topologies that satisfy it. It's a required field. Default value is 1 and 0 is not allowed.",
|
||||
|
@ -3427,6 +3427,15 @@
|
||||
],
|
||||
"description": "LabelSelector is used to find matching pods. Pods that match this label selector are counted to determine the number of pods in their corresponding topology domain."
|
||||
},
|
||||
"matchLabelKeys": {
|
||||
"description": "MatchLabelKeys is a set of pod label keys to select the pods over which spreading will be calculated. The keys are used to lookup values from the incoming pod labels, those key-value labels are ANDed with labelSelector to select the group of existing pods over which spreading will be calculated for the incoming pod. Keys that don't exist in the incoming pod labels will be ignored. A null or empty list means only match against labelSelector.",
|
||||
"items": {
|
||||
"default": "",
|
||||
"type": "string"
|
||||
},
|
||||
"type": "array",
|
||||
"x-kubernetes-list-type": "atomic"
|
||||
},
|
||||
"maxSkew": {
|
||||
"default": 0,
|
||||
"description": "MaxSkew describes the degree to which pods may be unevenly distributed. When `whenUnsatisfiable=DoNotSchedule`, it is the maximum permitted difference between the number of matching pods in the target topology and the global minimum. The global minimum is the minimum number of matching pods in an eligible domain or zero if the number of eligible domains is less than MinDomains. For example, in a 3-zone cluster, MaxSkew is set to 1, and pods with the same labelSelector spread as 2/2/1: In this case, the global minimum is 1. | zone1 | zone2 | zone3 | | P P | P P | P | - if MaxSkew is 1, incoming pod can only be scheduled to zone3 to become 2/2/2; scheduling it onto zone1(zone2) would make the ActualSkew(3-1) on zone1(zone2) violate MaxSkew(1). - if MaxSkew is 2, incoming pod can be scheduled onto any zone. When `whenUnsatisfiable=ScheduleAnyway`, it is used to give higher precedence to topologies that satisfy it. It's a required field. Default value is 1 and 0 is not allowed.",
|
||||
|
@ -545,6 +545,7 @@ func dropDisabledFields(
|
||||
|
||||
dropDisabledTopologySpreadConstraintsFields(podSpec, oldPodSpec)
|
||||
dropDisabledNodeInclusionPolicyFields(podSpec, oldPodSpec)
|
||||
dropDisabledMatchLabelKeysField(podSpec, oldPodSpec)
|
||||
}
|
||||
|
||||
// dropDisabledTopologySpreadConstraintsFields removes disabled fields from PodSpec related
|
||||
@ -618,6 +619,31 @@ func dropDisabledNodeInclusionPolicyFields(podSpec, oldPodSpec *api.PodSpec) {
|
||||
}
|
||||
}
|
||||
|
||||
// dropDisabledMatchLabelKeysField removes disabled fields from PodSpec related
|
||||
// to MatchLabelKeys only if it is not already used by the old spec.
|
||||
func dropDisabledMatchLabelKeysField(podSpec, oldPodSpec *api.PodSpec) {
|
||||
if !utilfeature.DefaultFeatureGate.Enabled(features.MatchLabelKeysInPodTopologySpread) && !matchLabelKeysInUse(oldPodSpec) {
|
||||
for i := range podSpec.TopologySpreadConstraints {
|
||||
podSpec.TopologySpreadConstraints[i].MatchLabelKeys = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// matchLabelKeysInUse returns true if the pod spec is non-nil
|
||||
// and has MatchLabelKeys field set in TopologySpreadConstraints.
|
||||
func matchLabelKeysInUse(podSpec *api.PodSpec) bool {
|
||||
if podSpec == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
for _, c := range podSpec.TopologySpreadConstraints {
|
||||
if len(c.MatchLabelKeys) > 0 {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// nodeAffinityPolicyInUse returns true if the pod spec is non-nil and has NodeAffinityPolicy field set
|
||||
// in TopologySpreadConstraints
|
||||
func nodeAffinityPolicyInUse(podSpec *api.PodSpec) bool {
|
||||
|
@ -1814,3 +1814,138 @@ func TestDropNodeInclusionPolicyFields(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDropDisabledMatchLabelKeysField(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
enabled bool
|
||||
podSpec *api.PodSpec
|
||||
oldPodSpec *api.PodSpec
|
||||
wantPodSpec *api.PodSpec
|
||||
}{
|
||||
{
|
||||
name: "feature disabled, both pods don't use MatchLabelKeys fields",
|
||||
enabled: false,
|
||||
oldPodSpec: &api.PodSpec{
|
||||
TopologySpreadConstraints: []api.TopologySpreadConstraint{},
|
||||
},
|
||||
podSpec: &api.PodSpec{
|
||||
TopologySpreadConstraints: []api.TopologySpreadConstraint{},
|
||||
},
|
||||
wantPodSpec: &api.PodSpec{
|
||||
TopologySpreadConstraints: []api.TopologySpreadConstraint{},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "feature disabled, only old pod uses MatchLabelKeys field",
|
||||
enabled: false,
|
||||
oldPodSpec: &api.PodSpec{
|
||||
TopologySpreadConstraints: []api.TopologySpreadConstraint{
|
||||
{MatchLabelKeys: []string{"foo"}},
|
||||
},
|
||||
},
|
||||
podSpec: &api.PodSpec{
|
||||
TopologySpreadConstraints: []api.TopologySpreadConstraint{},
|
||||
},
|
||||
wantPodSpec: &api.PodSpec{
|
||||
TopologySpreadConstraints: []api.TopologySpreadConstraint{},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "feature disabled, only current pod uses MatchLabelKeys field",
|
||||
enabled: false,
|
||||
oldPodSpec: &api.PodSpec{
|
||||
TopologySpreadConstraints: []api.TopologySpreadConstraint{},
|
||||
},
|
||||
podSpec: &api.PodSpec{
|
||||
TopologySpreadConstraints: []api.TopologySpreadConstraint{
|
||||
{MatchLabelKeys: []string{"foo"}},
|
||||
},
|
||||
},
|
||||
wantPodSpec: &api.PodSpec{
|
||||
TopologySpreadConstraints: []api.TopologySpreadConstraint{{}},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "feature disabled, both pods use MatchLabelKeys fields",
|
||||
enabled: false,
|
||||
oldPodSpec: &api.PodSpec{
|
||||
TopologySpreadConstraints: []api.TopologySpreadConstraint{
|
||||
{MatchLabelKeys: []string{"foo"}},
|
||||
},
|
||||
},
|
||||
podSpec: &api.PodSpec{
|
||||
TopologySpreadConstraints: []api.TopologySpreadConstraint{
|
||||
{MatchLabelKeys: []string{"foo"}},
|
||||
},
|
||||
},
|
||||
wantPodSpec: &api.PodSpec{
|
||||
TopologySpreadConstraints: []api.TopologySpreadConstraint{
|
||||
{MatchLabelKeys: []string{"foo"}},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "feature enabled, only old pod uses MatchLabelKeys field",
|
||||
enabled: true,
|
||||
oldPodSpec: &api.PodSpec{
|
||||
TopologySpreadConstraints: []api.TopologySpreadConstraint{
|
||||
{MatchLabelKeys: []string{"foo"}},
|
||||
},
|
||||
},
|
||||
podSpec: &api.PodSpec{
|
||||
TopologySpreadConstraints: []api.TopologySpreadConstraint{},
|
||||
},
|
||||
wantPodSpec: &api.PodSpec{
|
||||
TopologySpreadConstraints: []api.TopologySpreadConstraint{},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "feature enabled, only current pod uses MatchLabelKeys field",
|
||||
enabled: true,
|
||||
oldPodSpec: &api.PodSpec{
|
||||
TopologySpreadConstraints: []api.TopologySpreadConstraint{},
|
||||
},
|
||||
podSpec: &api.PodSpec{
|
||||
TopologySpreadConstraints: []api.TopologySpreadConstraint{
|
||||
{MatchLabelKeys: []string{"foo"}},
|
||||
},
|
||||
},
|
||||
wantPodSpec: &api.PodSpec{
|
||||
TopologySpreadConstraints: []api.TopologySpreadConstraint{
|
||||
{MatchLabelKeys: []string{"foo"}},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "feature enabled, both pods use MatchLabelKeys fields",
|
||||
enabled: false,
|
||||
oldPodSpec: &api.PodSpec{
|
||||
TopologySpreadConstraints: []api.TopologySpreadConstraint{
|
||||
{MatchLabelKeys: []string{"foo"}},
|
||||
},
|
||||
},
|
||||
podSpec: &api.PodSpec{
|
||||
TopologySpreadConstraints: []api.TopologySpreadConstraint{
|
||||
{MatchLabelKeys: []string{"foo"}},
|
||||
},
|
||||
},
|
||||
wantPodSpec: &api.PodSpec{
|
||||
TopologySpreadConstraints: []api.TopologySpreadConstraint{
|
||||
{MatchLabelKeys: []string{"foo"}},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.MatchLabelKeysInPodTopologySpread, test.enabled)()
|
||||
|
||||
dropDisabledFields(test.podSpec, nil, test.oldPodSpec, nil)
|
||||
if diff := cmp.Diff(test.wantPodSpec, test.podSpec); diff != "" {
|
||||
t.Errorf("unexpected pod spec (-want, +got):\n%s", diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -5733,6 +5733,15 @@ type TopologySpreadConstraint struct {
|
||||
// This is a alpha-level feature enabled by the NodeInclusionPolicyInPodTopologySpread feature flag.
|
||||
// +optional
|
||||
NodeTaintsPolicy *NodeInclusionPolicy
|
||||
// MatchLabelKeys is a set of pod label keys to select the pods over which
|
||||
// spreading will be calculated. The keys are used to lookup values from the
|
||||
// incoming pod labels, those key-value labels are ANDed with labelSelector
|
||||
// to select the group of existing pods over which spreading will be calculated
|
||||
// for the incoming pod. Keys that don't exist in the incoming pod labels will
|
||||
// be ignored. A null or empty list means only match against labelSelector.
|
||||
// +listType=atomic
|
||||
// +optional
|
||||
MatchLabelKeys []string
|
||||
}
|
||||
|
||||
// These are the built-in errors for PortStatus.
|
||||
|
2
pkg/apis/core/v1/zz_generated.conversion.go
generated
2
pkg/apis/core/v1/zz_generated.conversion.go
generated
@ -7999,6 +7999,7 @@ func autoConvert_v1_TopologySpreadConstraint_To_core_TopologySpreadConstraint(in
|
||||
out.MinDomains = (*int32)(unsafe.Pointer(in.MinDomains))
|
||||
out.NodeAffinityPolicy = (*core.NodeInclusionPolicy)(unsafe.Pointer(in.NodeAffinityPolicy))
|
||||
out.NodeTaintsPolicy = (*core.NodeInclusionPolicy)(unsafe.Pointer(in.NodeTaintsPolicy))
|
||||
out.MatchLabelKeys = *(*[]string)(unsafe.Pointer(&in.MatchLabelKeys))
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -8015,6 +8016,7 @@ func autoConvert_core_TopologySpreadConstraint_To_v1_TopologySpreadConstraint(in
|
||||
out.MinDomains = (*int32)(unsafe.Pointer(in.MinDomains))
|
||||
out.NodeAffinityPolicy = (*v1.NodeInclusionPolicy)(unsafe.Pointer(in.NodeAffinityPolicy))
|
||||
out.NodeTaintsPolicy = (*v1.NodeInclusionPolicy)(unsafe.Pointer(in.NodeTaintsPolicy))
|
||||
out.MatchLabelKeys = *(*[]string)(unsafe.Pointer(&in.MatchLabelKeys))
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -6520,6 +6520,7 @@ func validateTopologySpreadConstraints(constraints []core.TopologySpreadConstrai
|
||||
if err := validateNodeInclusionPolicy(subFldPath.Child("nodeTaintsPolicy"), constraint.NodeTaintsPolicy); err != nil {
|
||||
allErrs = append(allErrs, err)
|
||||
}
|
||||
allErrs = append(allErrs, validateMatchLabelKeys(subFldPath.Child("matchLabelKeys"), constraint.MatchLabelKeys, constraint.LabelSelector)...)
|
||||
}
|
||||
|
||||
return allErrs
|
||||
@ -6593,6 +6594,33 @@ func validateNodeInclusionPolicy(fldPath *field.Path, policy *core.NodeInclusion
|
||||
return nil
|
||||
}
|
||||
|
||||
// validateMatchLabelKeys tests that the elements are a valid label name and are not already included in labelSelector.
|
||||
func validateMatchLabelKeys(fldPath *field.Path, matchLabelKeys []string, labelSelector *metav1.LabelSelector) field.ErrorList {
|
||||
if len(matchLabelKeys) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
labelSelectorKeys := sets.String{}
|
||||
if labelSelector != nil {
|
||||
for key := range labelSelector.MatchLabels {
|
||||
labelSelectorKeys.Insert(key)
|
||||
}
|
||||
for _, matchExpression := range labelSelector.MatchExpressions {
|
||||
labelSelectorKeys.Insert(matchExpression.Key)
|
||||
}
|
||||
}
|
||||
|
||||
allErrs := field.ErrorList{}
|
||||
for i, key := range matchLabelKeys {
|
||||
allErrs = append(allErrs, unversionedvalidation.ValidateLabelName(key, fldPath.Index(i))...)
|
||||
if labelSelectorKeys.Has(key) {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Index(i), key, "exists in both matchLabelKeys and labelSelector"))
|
||||
}
|
||||
}
|
||||
|
||||
return allErrs
|
||||
}
|
||||
|
||||
// ValidateServiceClusterIPsRelatedFields validates .spec.ClusterIPs,,
|
||||
// .spec.IPFamilies, .spec.ipFamilyPolicy. This is exported because it is used
|
||||
// during IP init and allocation.
|
||||
|
@ -19539,6 +19539,7 @@ func TestValidateTopologySpreadConstraints(t *testing.T) {
|
||||
fieldPathTopologyKey := subFldPath0.Child("topologyKey")
|
||||
fieldPathWhenUnsatisfiable := subFldPath0.Child("whenUnsatisfiable")
|
||||
fieldPathTopologyKeyAndWhenUnsatisfiable := subFldPath0.Child("{topologyKey, whenUnsatisfiable}")
|
||||
fieldPathMatchLabelKeys := subFldPath0.Child("matchLabelKeys")
|
||||
nodeAffinityField := subFldPath0.Child("nodeAffinityPolicy")
|
||||
nodeTaintsField := subFldPath0.Child("nodeTaintsPolicy")
|
||||
unknown := core.NodeInclusionPolicy("Unknown")
|
||||
@ -19717,6 +19718,39 @@ func TestValidateTopologySpreadConstraints(t *testing.T) {
|
||||
field.NotSupported(nodeTaintsField, &unknown, supportedPodTopologySpreadNodePolicies.List()),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "key in MatchLabelKeys isn't correctly defined",
|
||||
constraints: []core.TopologySpreadConstraint{
|
||||
{
|
||||
MaxSkew: 1,
|
||||
TopologyKey: "k8s.io/zone",
|
||||
WhenUnsatisfiable: core.DoNotSchedule,
|
||||
MatchLabelKeys: []string{"/simple"},
|
||||
},
|
||||
},
|
||||
wantFieldErrors: []*field.Error{field.Invalid(fieldPathMatchLabelKeys.Index(0), "/simple", "prefix part must be non-empty")},
|
||||
},
|
||||
{
|
||||
name: "key exists in both matchLabelKeys and labelSelector",
|
||||
constraints: []core.TopologySpreadConstraint{
|
||||
{
|
||||
MaxSkew: 1,
|
||||
TopologyKey: "k8s.io/zone",
|
||||
WhenUnsatisfiable: core.DoNotSchedule,
|
||||
MatchLabelKeys: []string{"foo"},
|
||||
LabelSelector: &metav1.LabelSelector{
|
||||
MatchExpressions: []metav1.LabelSelectorRequirement{
|
||||
{
|
||||
Key: "foo",
|
||||
Operator: metav1.LabelSelectorOpNotIn,
|
||||
Values: []string{"value1", "value2"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
wantFieldErrors: []*field.Error{field.Invalid(fieldPathMatchLabelKeys.Index(0), "foo", "exists in both matchLabelKeys and labelSelector")},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
|
5
pkg/apis/core/zz_generated.deepcopy.go
generated
5
pkg/apis/core/zz_generated.deepcopy.go
generated
@ -5649,6 +5649,11 @@ func (in *TopologySpreadConstraint) DeepCopyInto(out *TopologySpreadConstraint)
|
||||
*out = new(NodeInclusionPolicy)
|
||||
**out = **in
|
||||
}
|
||||
if in.MatchLabelKeys != nil {
|
||||
in, out := &in.MatchLabelKeys, &out.MatchLabelKeys
|
||||
*out = make([]string, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -518,6 +518,13 @@ const (
|
||||
// Enables scaling down replicas via logarithmic comparison of creation/ready timestamps
|
||||
LogarithmicScaleDown featuregate.Feature = "LogarithmicScaleDown"
|
||||
|
||||
// owner: @denkensk
|
||||
// kep: http://kep.k8s.io/3243
|
||||
// alpha: v1.25
|
||||
//
|
||||
// Enable MatchLabelKeys in PodTopologySpread.
|
||||
MatchLabelKeysInPodTopologySpread featuregate.Feature = "MatchLabelKeysInPodTopologySpread"
|
||||
|
||||
// owner: @krmayankk
|
||||
// alpha: v1.24
|
||||
//
|
||||
@ -951,6 +958,8 @@ var defaultKubernetesFeatureGates = map[featuregate.Feature]featuregate.FeatureS
|
||||
|
||||
LogarithmicScaleDown: {Default: true, PreRelease: featuregate.Beta},
|
||||
|
||||
MatchLabelKeysInPodTopologySpread: {Default: false, PreRelease: featuregate.Alpha},
|
||||
|
||||
MaxUnavailableStatefulSet: {Default: false, PreRelease: featuregate.Alpha},
|
||||
|
||||
MemoryManager: {Default: true, PreRelease: featuregate.Beta},
|
||||
|
20
pkg/generated/openapi/zz_generated.openapi.go
generated
20
pkg/generated/openapi/zz_generated.openapi.go
generated
@ -25478,6 +25478,26 @@ func schema_k8sio_api_core_v1_TopologySpreadConstraint(ref common.ReferenceCallb
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"matchLabelKeys": {
|
||||
VendorExtensible: spec.VendorExtensible{
|
||||
Extensions: spec.Extensions{
|
||||
"x-kubernetes-list-type": "atomic",
|
||||
},
|
||||
},
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "MatchLabelKeys is a set of pod label keys to select the pods over which spreading will be calculated. The keys are used to lookup values from the incoming pod labels, those key-value labels are ANDed with labelSelector to select the group of existing pods over which spreading will be calculated for the incoming pod. Keys that don't exist in the incoming pod labels will be ignored. A null or empty list means only match against labelSelector.",
|
||||
Type: []string{"array"},
|
||||
Items: &spec.SchemaOrArray{
|
||||
Schema: &spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Default: "",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Required: []string{"maxSkew", "topologyKey", "whenUnsatisfiable"},
|
||||
},
|
||||
|
@ -644,8 +644,8 @@ func TestDryRunPreemption(t *testing.T) {
|
||||
nodeNames: []string{"node-a/zone1", "node-b/zone1", "node-x/zone2"},
|
||||
testPods: []*v1.Pod{
|
||||
st.MakePod().Name("p").UID("p").Label("foo", "").Priority(highPriority).
|
||||
SpreadConstraint(1, "zone", v1.DoNotSchedule, st.MakeLabelSelector().Exists("foo").Obj(), nil, nil, nil).
|
||||
SpreadConstraint(1, "hostname", v1.DoNotSchedule, st.MakeLabelSelector().Exists("foo").Obj(), nil, nil, nil).
|
||||
SpreadConstraint(1, "zone", v1.DoNotSchedule, st.MakeLabelSelector().Exists("foo").Obj(), nil, nil, nil, nil).
|
||||
SpreadConstraint(1, "hostname", v1.DoNotSchedule, st.MakeLabelSelector().Exists("foo").Obj(), nil, nil, nil, nil).
|
||||
Obj(),
|
||||
},
|
||||
initPods: []*v1.Pod{
|
||||
@ -1486,8 +1486,8 @@ func TestPreempt(t *testing.T) {
|
||||
{
|
||||
name: "preemption for topology spread constraints",
|
||||
pod: st.MakePod().Name("p").UID("p").Namespace(v1.NamespaceDefault).Label("foo", "").Priority(highPriority).
|
||||
SpreadConstraint(1, "zone", v1.DoNotSchedule, st.MakeLabelSelector().Exists("foo").Obj(), nil, nil, nil).
|
||||
SpreadConstraint(1, "hostname", v1.DoNotSchedule, st.MakeLabelSelector().Exists("foo").Obj(), nil, nil, nil).
|
||||
SpreadConstraint(1, "zone", v1.DoNotSchedule, st.MakeLabelSelector().Exists("foo").Obj(), nil, nil, nil, nil).
|
||||
SpreadConstraint(1, "hostname", v1.DoNotSchedule, st.MakeLabelSelector().Exists("foo").Obj(), nil, nil, nil, nil).
|
||||
Obj(),
|
||||
pods: []*v1.Pod{
|
||||
st.MakePod().Name("p-a1").UID("p-a1").Namespace(v1.NamespaceDefault).Node("node-a").Label("foo", "").Priority(highPriority).Obj(),
|
||||
|
@ -24,4 +24,5 @@ type Features struct {
|
||||
EnableVolumeCapacityPriority bool
|
||||
EnableMinDomainsInPodTopologySpread bool
|
||||
EnableNodeInclusionPolicyInPodTopologySpread bool
|
||||
EnableMatchLabelKeysInPodTopologySpread bool
|
||||
}
|
||||
|
@ -63,7 +63,7 @@ func (tsc *topologySpreadConstraint) matchNodeInclusionPolicies(pod *v1.Pod, nod
|
||||
// .DefaultConstraints and the selectors from the services, replication
|
||||
// controllers, replica sets and stateful sets that match the pod.
|
||||
func (pl *PodTopologySpread) buildDefaultConstraints(p *v1.Pod, action v1.UnsatisfiableConstraintAction) ([]topologySpreadConstraint, error) {
|
||||
constraints, err := filterTopologySpreadConstraints(pl.defaultConstraints, action, pl.enableMinDomainsInPodTopologySpread, pl.enableNodeInclusionPolicyInPodTopologySpread)
|
||||
constraints, err := pl.filterTopologySpreadConstraints(pl.defaultConstraints, p.Labels, action)
|
||||
if err != nil || len(constraints) == 0 {
|
||||
return nil, err
|
||||
}
|
||||
@ -87,7 +87,7 @@ func nodeLabelsMatchSpreadConstraints(nodeLabels map[string]string, constraints
|
||||
return true
|
||||
}
|
||||
|
||||
func filterTopologySpreadConstraints(constraints []v1.TopologySpreadConstraint, action v1.UnsatisfiableConstraintAction, enableMinDomainsInPodTopologySpread, enableNodeInclusionPolicyInPodTopologySpread bool) ([]topologySpreadConstraint, error) {
|
||||
func (pl *PodTopologySpread) filterTopologySpreadConstraints(constraints []v1.TopologySpreadConstraint, podLabels map[string]string, action v1.UnsatisfiableConstraintAction) ([]topologySpreadConstraint, error) {
|
||||
var result []topologySpreadConstraint
|
||||
for _, c := range constraints {
|
||||
if c.WhenUnsatisfiable == action {
|
||||
@ -95,6 +95,19 @@ func filterTopologySpreadConstraints(constraints []v1.TopologySpreadConstraint,
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if pl.enableMatchLabelKeysInPodTopologySpread && len(c.MatchLabelKeys) > 0 {
|
||||
matchLabels := make(labels.Set)
|
||||
for _, labelKey := range c.MatchLabelKeys {
|
||||
if value, ok := podLabels[labelKey]; ok {
|
||||
matchLabels[labelKey] = value
|
||||
}
|
||||
}
|
||||
if len(matchLabels) > 0 {
|
||||
selector = mergeLabelSetWithSelector(matchLabels, selector)
|
||||
}
|
||||
}
|
||||
|
||||
tsc := topologySpreadConstraint{
|
||||
MaxSkew: c.MaxSkew,
|
||||
TopologyKey: c.TopologyKey,
|
||||
@ -103,10 +116,10 @@ func filterTopologySpreadConstraints(constraints []v1.TopologySpreadConstraint,
|
||||
NodeAffinityPolicy: v1.NodeInclusionPolicyHonor, // If NodeAffinityPolicy is nil, we treat NodeAffinityPolicy as "Honor".
|
||||
NodeTaintsPolicy: v1.NodeInclusionPolicyIgnore, // If NodeTaintsPolicy is nil, we treat NodeTaintsPolicy as "Ignore".
|
||||
}
|
||||
if enableMinDomainsInPodTopologySpread && c.MinDomains != nil {
|
||||
if pl.enableMinDomainsInPodTopologySpread && c.MinDomains != nil {
|
||||
tsc.MinDomains = *c.MinDomains
|
||||
}
|
||||
if enableNodeInclusionPolicyInPodTopologySpread {
|
||||
if pl.enableNodeInclusionPolicyInPodTopologySpread {
|
||||
if c.NodeAffinityPolicy != nil {
|
||||
tsc.NodeAffinityPolicy = *c.NodeAffinityPolicy
|
||||
}
|
||||
@ -120,6 +133,17 @@ func filterTopologySpreadConstraints(constraints []v1.TopologySpreadConstraint,
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func mergeLabelSetWithSelector(matchLabels labels.Set, s labels.Selector) labels.Selector {
|
||||
mergedSelector := labels.SelectorFromSet(matchLabels)
|
||||
if requirements, ok := s.Requirements(); ok {
|
||||
for _, r := range requirements {
|
||||
mergedSelector = mergedSelector.Add(r)
|
||||
}
|
||||
}
|
||||
|
||||
return mergedSelector
|
||||
}
|
||||
|
||||
func countPodsMatchSelector(podInfos []*framework.PodInfo, selector labels.Selector, ns string) int {
|
||||
count := 0
|
||||
for _, p := range podInfos {
|
||||
|
@ -244,11 +244,10 @@ func (pl *PodTopologySpread) calPreFilterState(ctx context.Context, pod *v1.Pod)
|
||||
if len(pod.Spec.TopologySpreadConstraints) > 0 {
|
||||
// We have feature gating in APIServer to strip the spec
|
||||
// so don't need to re-check feature gate, just check length of Constraints.
|
||||
constraints, err = filterTopologySpreadConstraints(
|
||||
constraints, err = pl.filterTopologySpreadConstraints(
|
||||
pod.Spec.TopologySpreadConstraints,
|
||||
pod.Labels,
|
||||
v1.DoNotSchedule,
|
||||
pl.enableMinDomainsInPodTopologySpread,
|
||||
pl.enableNodeInclusionPolicyInPodTopologySpread,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("obtaining pod's hard topology spread constraints: %w", err)
|
||||
|
@ -55,7 +55,8 @@ var (
|
||||
honorPolicy = v1.NodeInclusionPolicyHonor
|
||||
fooSelector = st.MakeLabelSelector().Exists("foo").Obj()
|
||||
barSelector = st.MakeLabelSelector().Exists("bar").Obj()
|
||||
taints = []v1.Taint{{Key: v1.TaintNodeUnschedulable, Value: "", Effect: v1.TaintEffectPreferNoSchedule}}
|
||||
|
||||
taints = []v1.Taint{{Key: v1.TaintNodeUnschedulable, Value: "", Effect: v1.TaintEffectPreferNoSchedule}}
|
||||
)
|
||||
|
||||
func (p *criticalPaths) sort() {
|
||||
@ -77,11 +78,12 @@ func TestPreFilterState(t *testing.T) {
|
||||
want *preFilterState
|
||||
enableMinDomains bool
|
||||
enableNodeInclustionPolicy bool
|
||||
enableMatchLabelKeys bool
|
||||
}{
|
||||
{
|
||||
name: "clean cluster with one spreadConstraint",
|
||||
pod: st.MakePod().Name("p").Label("foo", "").
|
||||
SpreadConstraint(5, "zone", v1.DoNotSchedule, st.MakeLabelSelector().Label("foo", "bar").Obj(), nil, nil, nil).
|
||||
SpreadConstraint(5, "zone", v1.DoNotSchedule, st.MakeLabelSelector().Label("foo", "bar").Obj(), nil, nil, nil, nil).
|
||||
Obj(),
|
||||
nodes: []*v1.Node{
|
||||
st.MakeNode().Name("node-a").Label("zone", "zone1").Label("node", "node-a").Obj(),
|
||||
@ -112,7 +114,7 @@ func TestPreFilterState(t *testing.T) {
|
||||
{
|
||||
name: "normal case with one spreadConstraint",
|
||||
pod: st.MakePod().Name("p").Label("foo", "").
|
||||
SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil).
|
||||
SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil).
|
||||
Obj(),
|
||||
nodes: []*v1.Node{
|
||||
st.MakeNode().Name("node-a").Label("zone", "zone1").Label("node", "node-a").Obj(),
|
||||
@ -150,7 +152,7 @@ func TestPreFilterState(t *testing.T) {
|
||||
{
|
||||
name: "normal case with one spreadConstraint, on a 3-zone cluster",
|
||||
pod: st.MakePod().Name("p").Label("foo", "").
|
||||
SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil).
|
||||
SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil).
|
||||
Obj(),
|
||||
nodes: []*v1.Node{
|
||||
st.MakeNode().Name("node-a").Label("zone", "zone1").Label("node", "node-a").Obj(),
|
||||
@ -191,7 +193,7 @@ func TestPreFilterState(t *testing.T) {
|
||||
{
|
||||
name: "namespace mismatch doesn't count",
|
||||
pod: st.MakePod().Name("p").Label("foo", "").
|
||||
SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil).
|
||||
SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil).
|
||||
Obj(),
|
||||
nodes: []*v1.Node{
|
||||
st.MakeNode().Name("node-a").Label("zone", "zone1").Label("node", "node-a").Obj(),
|
||||
@ -229,8 +231,8 @@ func TestPreFilterState(t *testing.T) {
|
||||
{
|
||||
name: "normal case with two spreadConstraints",
|
||||
pod: st.MakePod().Name("p").Label("foo", "").
|
||||
SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil).
|
||||
SpreadConstraint(1, "node", v1.DoNotSchedule, fooSelector, nil, nil, nil).
|
||||
SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil).
|
||||
SpreadConstraint(1, "node", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil).
|
||||
Obj(),
|
||||
nodes: []*v1.Node{
|
||||
st.MakeNode().Name("node-a").Label("zone", "zone1").Label("node", "node-a").Obj(),
|
||||
@ -283,10 +285,10 @@ func TestPreFilterState(t *testing.T) {
|
||||
{
|
||||
name: "soft spreadConstraints should be bypassed",
|
||||
pod: st.MakePod().Name("p").Label("foo", "").
|
||||
SpreadConstraint(1, "zone", v1.ScheduleAnyway, fooSelector, nil, nil, nil).
|
||||
SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil).
|
||||
SpreadConstraint(1, "node", v1.ScheduleAnyway, fooSelector, nil, nil, nil).
|
||||
SpreadConstraint(1, "node", v1.DoNotSchedule, fooSelector, nil, nil, nil).
|
||||
SpreadConstraint(1, "zone", v1.ScheduleAnyway, fooSelector, nil, nil, nil, nil).
|
||||
SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil).
|
||||
SpreadConstraint(1, "node", v1.ScheduleAnyway, fooSelector, nil, nil, nil, nil).
|
||||
SpreadConstraint(1, "node", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil).
|
||||
Obj(),
|
||||
nodes: []*v1.Node{
|
||||
st.MakeNode().Name("node-a").Label("zone", "zone1").Label("node", "node-a").Obj(),
|
||||
@ -337,8 +339,8 @@ func TestPreFilterState(t *testing.T) {
|
||||
{
|
||||
name: "different labelSelectors - simple version",
|
||||
pod: st.MakePod().Name("p").Label("foo", "").Label("bar", "").
|
||||
SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil).
|
||||
SpreadConstraint(1, "node", v1.DoNotSchedule, barSelector, nil, nil, nil).
|
||||
SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil).
|
||||
SpreadConstraint(1, "node", v1.DoNotSchedule, barSelector, nil, nil, nil, nil).
|
||||
Obj(),
|
||||
nodes: []*v1.Node{
|
||||
st.MakeNode().Name("node-a").Label("zone", "zone1").Label("node", "node-a").Obj(),
|
||||
@ -384,8 +386,8 @@ func TestPreFilterState(t *testing.T) {
|
||||
{
|
||||
name: "different labelSelectors - complex pods",
|
||||
pod: st.MakePod().Name("p").Label("foo", "").Label("bar", "").
|
||||
SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil).
|
||||
SpreadConstraint(1, "node", v1.DoNotSchedule, barSelector, nil, nil, nil).
|
||||
SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil).
|
||||
SpreadConstraint(1, "node", v1.DoNotSchedule, barSelector, nil, nil, nil, nil).
|
||||
Obj(),
|
||||
nodes: []*v1.Node{
|
||||
st.MakeNode().Name("node-a").Label("zone", "zone1").Label("node", "node-a").Obj(),
|
||||
@ -437,8 +439,8 @@ func TestPreFilterState(t *testing.T) {
|
||||
name: "two spreadConstraints, and with podAffinity",
|
||||
pod: st.MakePod().Name("p").Label("foo", "").
|
||||
NodeAffinityNotIn("node", []string{"node-x"}). // exclude node-x
|
||||
SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil).
|
||||
SpreadConstraint(1, "node", v1.DoNotSchedule, fooSelector, nil, nil, nil).
|
||||
SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil).
|
||||
SpreadConstraint(1, "node", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil).
|
||||
Obj(),
|
||||
nodes: []*v1.Node{
|
||||
st.MakeNode().Name("node-a").Label("zone", "zone1").Label("node", "node-a").Obj(),
|
||||
@ -538,8 +540,8 @@ func TestPreFilterState(t *testing.T) {
|
||||
{
|
||||
name: "default constraints and a service, but pod has constraints",
|
||||
pod: st.MakePod().Name("p").Label("foo", "bar").Label("baz", "tar").
|
||||
SpreadConstraint(1, "zone", v1.DoNotSchedule, st.MakeLabelSelector().Label("baz", "tar").Obj(), nil, nil, nil).
|
||||
SpreadConstraint(2, "planet", v1.ScheduleAnyway, st.MakeLabelSelector().Label("fot", "rok").Obj(), nil, nil, nil).
|
||||
SpreadConstraint(1, "zone", v1.DoNotSchedule, st.MakeLabelSelector().Label("baz", "tar").Obj(), nil, nil, nil, nil).
|
||||
SpreadConstraint(2, "planet", v1.ScheduleAnyway, st.MakeLabelSelector().Label("fot", "rok").Obj(), nil, nil, nil, nil).
|
||||
Obj(),
|
||||
defaultConstraints: []v1.TopologySpreadConstraint{
|
||||
{MaxSkew: 2, TopologyKey: "node", WhenUnsatisfiable: v1.DoNotSchedule},
|
||||
@ -578,8 +580,8 @@ func TestPreFilterState(t *testing.T) {
|
||||
{
|
||||
name: "TpKeyToDomainsNum is calculated when MinDomains is enabled",
|
||||
pod: st.MakePod().Name("p").Label("foo", "").
|
||||
SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil).
|
||||
SpreadConstraint(1, "node", v1.DoNotSchedule, fooSelector, nil, nil, nil).
|
||||
SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil).
|
||||
SpreadConstraint(1, "node", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil).
|
||||
Obj(),
|
||||
nodes: []*v1.Node{
|
||||
st.MakeNode().Name("node-a").Label("zone", "zone1").Label("node", "node-a").Obj(),
|
||||
@ -638,7 +640,7 @@ func TestPreFilterState(t *testing.T) {
|
||||
name: "feature gate disabled with NodeAffinityPolicy",
|
||||
pod: st.MakePod().Name("p").Label("foo", "").
|
||||
NodeSelector(map[string]string{"foo": ""}).
|
||||
SpreadConstraint(1, "node", v1.DoNotSchedule, barSelector, nil, nil, nil).
|
||||
SpreadConstraint(1, "node", v1.DoNotSchedule, barSelector, nil, nil, nil, nil).
|
||||
Obj(),
|
||||
nodes: []*v1.Node{
|
||||
st.MakeNode().Name("node-a").Label("node", "node-a").Label("foo", "").Obj(),
|
||||
@ -676,7 +678,7 @@ func TestPreFilterState(t *testing.T) {
|
||||
name: "NodeAffinityPolicy honored with labelSelectors",
|
||||
pod: st.MakePod().Name("p").Label("foo", "").
|
||||
NodeSelector(map[string]string{"foo": ""}).
|
||||
SpreadConstraint(1, "node", v1.DoNotSchedule, barSelector, nil, nil, nil).
|
||||
SpreadConstraint(1, "node", v1.DoNotSchedule, barSelector, nil, nil, nil, nil).
|
||||
Obj(),
|
||||
nodes: []*v1.Node{
|
||||
st.MakeNode().Name("node-a").Label("node", "node-a").Label("foo", "").Obj(),
|
||||
@ -714,7 +716,7 @@ func TestPreFilterState(t *testing.T) {
|
||||
name: "NodeAffinityPolicy ignored with labelSelectors",
|
||||
pod: st.MakePod().Name("p").Label("foo", "").
|
||||
NodeSelector(map[string]string{"foo": ""}).
|
||||
SpreadConstraint(1, "node", v1.DoNotSchedule, barSelector, nil, &ignorePolicy, nil).
|
||||
SpreadConstraint(1, "node", v1.DoNotSchedule, barSelector, nil, &ignorePolicy, nil, nil).
|
||||
Obj(),
|
||||
nodes: []*v1.Node{
|
||||
st.MakeNode().Name("node-a").Label("node", "node-a").Label("foo", "").Obj(),
|
||||
@ -753,7 +755,7 @@ func TestPreFilterState(t *testing.T) {
|
||||
name: "NodeAffinityPolicy honored with nodeAffinity",
|
||||
pod: st.MakePod().Name("p").Label("foo", "").
|
||||
NodeAffinityIn("foo", []string{""}).
|
||||
SpreadConstraint(1, "node", v1.DoNotSchedule, barSelector, nil, nil, nil).
|
||||
SpreadConstraint(1, "node", v1.DoNotSchedule, barSelector, nil, nil, nil, nil).
|
||||
Obj(),
|
||||
nodes: []*v1.Node{
|
||||
st.MakeNode().Name("node-a").Label("node", "node-a").Label("foo", "").Obj(),
|
||||
@ -791,7 +793,7 @@ func TestPreFilterState(t *testing.T) {
|
||||
name: "NodeAffinityPolicy ignored with nodeAffinity",
|
||||
pod: st.MakePod().Name("p").Label("foo", "").
|
||||
NodeAffinityIn("foo", []string{""}).
|
||||
SpreadConstraint(1, "node", v1.DoNotSchedule, barSelector, nil, &ignorePolicy, nil).
|
||||
SpreadConstraint(1, "node", v1.DoNotSchedule, barSelector, nil, &ignorePolicy, nil, nil).
|
||||
Obj(),
|
||||
nodes: []*v1.Node{
|
||||
st.MakeNode().Name("node-a").Label("node", "node-a").Label("foo", "").Obj(),
|
||||
@ -829,7 +831,7 @@ func TestPreFilterState(t *testing.T) {
|
||||
{
|
||||
name: "feature gate disabled with NodeTaintsPolicy",
|
||||
pod: st.MakePod().Name("p").Label("foo", "").
|
||||
SpreadConstraint(1, "node", v1.DoNotSchedule, barSelector, nil, nil, nil).
|
||||
SpreadConstraint(1, "node", v1.DoNotSchedule, barSelector, nil, nil, nil, nil).
|
||||
Obj(),
|
||||
nodes: []*v1.Node{
|
||||
st.MakeNode().Name("node-a").Label("node", "node-a").Obj(),
|
||||
@ -867,7 +869,7 @@ func TestPreFilterState(t *testing.T) {
|
||||
{
|
||||
name: "NodeTaintsPolicy ignored",
|
||||
pod: st.MakePod().Name("p").Label("foo", "").
|
||||
SpreadConstraint(1, "node", v1.DoNotSchedule, barSelector, nil, nil, nil).
|
||||
SpreadConstraint(1, "node", v1.DoNotSchedule, barSelector, nil, nil, nil, nil).
|
||||
Obj(),
|
||||
nodes: []*v1.Node{
|
||||
st.MakeNode().Name("node-a").Label("node", "node-a").Obj(),
|
||||
@ -905,7 +907,7 @@ func TestPreFilterState(t *testing.T) {
|
||||
{
|
||||
name: "NodeTaintsPolicy honored",
|
||||
pod: st.MakePod().Name("p").Label("foo", "").
|
||||
SpreadConstraint(1, "node", v1.DoNotSchedule, barSelector, nil, nil, &honorPolicy).
|
||||
SpreadConstraint(1, "node", v1.DoNotSchedule, barSelector, nil, nil, &honorPolicy, nil).
|
||||
Obj(),
|
||||
nodes: []*v1.Node{
|
||||
st.MakeNode().Name("node-a").Label("node", "node-a").Obj(),
|
||||
@ -943,7 +945,7 @@ func TestPreFilterState(t *testing.T) {
|
||||
name: "NodeTaintsPolicy honored with tolerated taints",
|
||||
pod: st.MakePod().Name("p").Label("foo", "").
|
||||
Toleration(v1.TaintNodeUnschedulable).
|
||||
SpreadConstraint(1, "node", v1.DoNotSchedule, barSelector, nil, nil, &honorPolicy).
|
||||
SpreadConstraint(1, "node", v1.DoNotSchedule, barSelector, nil, nil, &honorPolicy, nil).
|
||||
Obj(),
|
||||
nodes: []*v1.Node{
|
||||
st.MakeNode().Name("node-a").Label("node", "node-a").Obj(),
|
||||
@ -982,8 +984,8 @@ func TestPreFilterState(t *testing.T) {
|
||||
name: "two node inclusion Constraints, zone: honor/ignore, node: ignore/ignore",
|
||||
pod: st.MakePod().Name("p").Label("foo", "").
|
||||
NodeSelector(map[string]string{"foo": ""}).
|
||||
SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil).
|
||||
SpreadConstraint(1, "node", v1.DoNotSchedule, fooSelector, nil, &ignorePolicy, nil).
|
||||
SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil).
|
||||
SpreadConstraint(1, "node", v1.DoNotSchedule, fooSelector, nil, &ignorePolicy, nil, nil).
|
||||
Obj(),
|
||||
nodes: []*v1.Node{
|
||||
st.MakeNode().Name("node-a").Label("zone", "zone1").Label("node", "node-a").Label("foo", "").Obj(),
|
||||
@ -1031,8 +1033,8 @@ func TestPreFilterState(t *testing.T) {
|
||||
{
|
||||
name: "two node inclusion Constraints, zone: honor/honor, node: honor/ignore",
|
||||
pod: st.MakePod().Name("p").Label("foo", "").
|
||||
SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, &honorPolicy).
|
||||
SpreadConstraint(1, "node", v1.DoNotSchedule, fooSelector, nil, nil, nil).
|
||||
SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, &honorPolicy, nil).
|
||||
SpreadConstraint(1, "node", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil).
|
||||
Obj(),
|
||||
nodes: []*v1.Node{
|
||||
st.MakeNode().Name("node-a").Label("zone", "zone1").Label("node", "node-a").Label("foo", "").Obj(),
|
||||
@ -1081,8 +1083,8 @@ func TestPreFilterState(t *testing.T) {
|
||||
name: "two node inclusion Constraints, zone: honor/ignore, node: honor/ignore",
|
||||
pod: st.MakePod().Name("p").Label("foo", "").
|
||||
NodeSelector(map[string]string{"foo": ""}).
|
||||
SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil).
|
||||
SpreadConstraint(1, "node", v1.DoNotSchedule, fooSelector, nil, nil, nil).
|
||||
SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil).
|
||||
SpreadConstraint(1, "node", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil).
|
||||
Obj(),
|
||||
nodes: []*v1.Node{
|
||||
st.MakeNode().Name("node-a").Label("zone", "zone1").Label("node", "node-a").Obj(),
|
||||
@ -1132,8 +1134,8 @@ func TestPreFilterState(t *testing.T) {
|
||||
name: "two node inclusion Constraints, zone: ignore/ignore, node: honor/honor",
|
||||
pod: st.MakePod().Name("p").Label("foo", "").
|
||||
NodeSelector(map[string]string{"foo": ""}).
|
||||
SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, &ignorePolicy, nil).
|
||||
SpreadConstraint(1, "node", v1.DoNotSchedule, fooSelector, nil, nil, &honorPolicy).
|
||||
SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, &ignorePolicy, nil, nil).
|
||||
SpreadConstraint(1, "node", v1.DoNotSchedule, fooSelector, nil, nil, &honorPolicy, nil).
|
||||
Obj(),
|
||||
nodes: []*v1.Node{
|
||||
st.MakeNode().Name("node-a").Label("zone", "zone1").Label("node", "node-a").Obj(),
|
||||
@ -1180,6 +1182,162 @@ func TestPreFilterState(t *testing.T) {
|
||||
},
|
||||
enableNodeInclustionPolicy: true,
|
||||
},
|
||||
{
|
||||
name: "matchLabelKeys ignored when feature gate disabled",
|
||||
pod: st.MakePod().Name("p").Label("foo", "").Label("bar", "").
|
||||
SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil, []string{"bar"}).
|
||||
Obj(),
|
||||
nodes: []*v1.Node{
|
||||
st.MakeNode().Name("node-a").Label("zone", "zone1").Label("node", "node-a").Obj(),
|
||||
st.MakeNode().Name("node-b").Label("zone", "zone1").Label("node", "node-b").Obj(),
|
||||
st.MakeNode().Name("node-x").Label("zone", "zone2").Label("node", "node-x").Obj(),
|
||||
st.MakeNode().Name("node-y").Label("zone", "zone2").Label("node", "node-y").Obj(),
|
||||
},
|
||||
existingPods: []*v1.Pod{
|
||||
st.MakePod().Name("p-a1").Node("node-a").Label("foo", "").Obj(),
|
||||
st.MakePod().Name("p-a2").Node("node-a").Label("foo", "").Obj(),
|
||||
st.MakePod().Name("p-b1").Node("node-b").Label("foo", "").Obj(),
|
||||
st.MakePod().Name("p-y1").Node("node-y").Label("foo", "").Obj(),
|
||||
st.MakePod().Name("p-y2").Node("node-y").Label("foo", "").Obj(),
|
||||
},
|
||||
want: &preFilterState{
|
||||
Constraints: []topologySpreadConstraint{
|
||||
{
|
||||
MaxSkew: 1,
|
||||
TopologyKey: "zone",
|
||||
Selector: mustConvertLabelSelectorAsSelector(t, fooSelector),
|
||||
MinDomains: 1,
|
||||
NodeAffinityPolicy: v1.NodeInclusionPolicyHonor,
|
||||
NodeTaintsPolicy: v1.NodeInclusionPolicyIgnore,
|
||||
},
|
||||
},
|
||||
TpKeyToCriticalPaths: map[string]*criticalPaths{
|
||||
"zone": {{"zone2", 2}, {"zone1", 3}},
|
||||
},
|
||||
TpPairToMatchNum: map[topologyPair]int{
|
||||
{key: "zone", value: "zone1"}: 3,
|
||||
{key: "zone", value: "zone2"}: 2,
|
||||
},
|
||||
},
|
||||
enableMatchLabelKeys: false,
|
||||
},
|
||||
{
|
||||
name: "matchLabelKeys ANDed with LabelSelector when LabelSelector isn't empty",
|
||||
pod: st.MakePod().Name("p").Label("foo", "").Label("bar", "a").
|
||||
SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil, []string{"bar"}).
|
||||
Obj(),
|
||||
nodes: []*v1.Node{
|
||||
st.MakeNode().Name("node-a").Label("zone", "zone1").Label("node", "node-a").Obj(),
|
||||
st.MakeNode().Name("node-b").Label("zone", "zone1").Label("node", "node-b").Obj(),
|
||||
st.MakeNode().Name("node-x").Label("zone", "zone2").Label("node", "node-x").Obj(),
|
||||
st.MakeNode().Name("node-y").Label("zone", "zone2").Label("node", "node-y").Obj(),
|
||||
},
|
||||
existingPods: []*v1.Pod{
|
||||
st.MakePod().Name("p-a1").Node("node-a").Label("foo", "").Obj(),
|
||||
st.MakePod().Name("p-a2").Node("node-a").Label("foo", "").Label("bar", "a").Obj(),
|
||||
st.MakePod().Name("p-b1").Node("node-b").Label("foo", "").Obj(),
|
||||
st.MakePod().Name("p-y1").Node("node-y").Label("foo", "").Label("bar", "a").Obj(),
|
||||
st.MakePod().Name("p-y2").Node("node-y").Label("bar", "").Obj(),
|
||||
},
|
||||
want: &preFilterState{
|
||||
Constraints: []topologySpreadConstraint{
|
||||
{
|
||||
MaxSkew: 1,
|
||||
TopologyKey: "zone",
|
||||
Selector: mustConvertLabelSelectorAsSelector(t, st.MakeLabelSelector().Exists("foo").Label("bar", "a").Obj()),
|
||||
MinDomains: 1,
|
||||
NodeAffinityPolicy: v1.NodeInclusionPolicyHonor,
|
||||
NodeTaintsPolicy: v1.NodeInclusionPolicyIgnore,
|
||||
},
|
||||
},
|
||||
TpKeyToCriticalPaths: map[string]*criticalPaths{
|
||||
"zone": {{"zone2", 1}, {"zone1", 1}},
|
||||
},
|
||||
TpPairToMatchNum: map[topologyPair]int{
|
||||
{key: "zone", value: "zone1"}: 1,
|
||||
{key: "zone", value: "zone2"}: 1,
|
||||
},
|
||||
},
|
||||
enableMatchLabelKeys: true,
|
||||
},
|
||||
{
|
||||
name: "matchLabelKeys ANDed with LabelSelector when LabelSelector is empty",
|
||||
pod: st.MakePod().Name("p").Label("foo", "a").
|
||||
SpreadConstraint(1, "zone", v1.DoNotSchedule, st.MakeLabelSelector().Obj(), nil, nil, nil, []string{"foo"}).
|
||||
Obj(),
|
||||
nodes: []*v1.Node{
|
||||
st.MakeNode().Name("node-a").Label("zone", "zone1").Label("node", "node-a").Obj(),
|
||||
st.MakeNode().Name("node-b").Label("zone", "zone1").Label("node", "node-b").Obj(),
|
||||
st.MakeNode().Name("node-x").Label("zone", "zone2").Label("node", "node-x").Obj(),
|
||||
st.MakeNode().Name("node-y").Label("zone", "zone2").Label("node", "node-y").Obj(),
|
||||
},
|
||||
existingPods: []*v1.Pod{
|
||||
st.MakePod().Name("p-a1").Node("node-a").Label("foo", "a").Obj(),
|
||||
st.MakePod().Name("p-a2").Node("node-a").Label("foo", "a").Obj(),
|
||||
st.MakePod().Name("p-b1").Node("node-b").Label("foo", "a").Obj(),
|
||||
st.MakePod().Name("p-y1").Node("node-y").Label("foo", "a").Obj(),
|
||||
st.MakePod().Name("p-y2").Node("node-y").Label("foo", "a").Obj(),
|
||||
},
|
||||
want: &preFilterState{
|
||||
Constraints: []topologySpreadConstraint{
|
||||
{
|
||||
MaxSkew: 1,
|
||||
TopologyKey: "zone",
|
||||
Selector: mustConvertLabelSelectorAsSelector(t, st.MakeLabelSelector().Label("foo", "a").Obj()),
|
||||
MinDomains: 1,
|
||||
NodeAffinityPolicy: v1.NodeInclusionPolicyHonor,
|
||||
NodeTaintsPolicy: v1.NodeInclusionPolicyIgnore,
|
||||
},
|
||||
},
|
||||
TpKeyToCriticalPaths: map[string]*criticalPaths{
|
||||
"zone": {{"zone2", 2}, {"zone1", 3}},
|
||||
},
|
||||
TpPairToMatchNum: map[topologyPair]int{
|
||||
{key: "zone", value: "zone1"}: 3,
|
||||
{key: "zone", value: "zone2"}: 2,
|
||||
},
|
||||
},
|
||||
enableMatchLabelKeys: true,
|
||||
},
|
||||
{
|
||||
name: "key in matchLabelKeys is ignored when it isn't exist in pod.labels",
|
||||
pod: st.MakePod().Name("p").Label("foo", "").
|
||||
SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil, []string{"bar"}).
|
||||
Obj(),
|
||||
nodes: []*v1.Node{
|
||||
st.MakeNode().Name("node-a").Label("zone", "zone1").Label("node", "node-a").Obj(),
|
||||
st.MakeNode().Name("node-b").Label("zone", "zone1").Label("node", "node-b").Obj(),
|
||||
st.MakeNode().Name("node-x").Label("zone", "zone2").Label("node", "node-x").Obj(),
|
||||
st.MakeNode().Name("node-y").Label("zone", "zone2").Label("node", "node-y").Obj(),
|
||||
},
|
||||
existingPods: []*v1.Pod{
|
||||
st.MakePod().Name("p-a1").Node("node-a").Label("foo", "a").Obj(),
|
||||
st.MakePod().Name("p-a2").Node("node-a").Label("foo", "a").Obj(),
|
||||
st.MakePod().Name("p-b1").Node("node-b").Label("foo", "a").Obj(),
|
||||
st.MakePod().Name("p-y1").Node("node-y").Label("foo", "a").Obj(),
|
||||
st.MakePod().Name("p-y2").Node("node-y").Label("foo", "a").Obj(),
|
||||
},
|
||||
want: &preFilterState{
|
||||
Constraints: []topologySpreadConstraint{
|
||||
{
|
||||
MaxSkew: 1,
|
||||
TopologyKey: "zone",
|
||||
Selector: mustConvertLabelSelectorAsSelector(t, fooSelector),
|
||||
MinDomains: 1,
|
||||
NodeAffinityPolicy: v1.NodeInclusionPolicyHonor,
|
||||
NodeTaintsPolicy: v1.NodeInclusionPolicyIgnore,
|
||||
},
|
||||
},
|
||||
TpKeyToCriticalPaths: map[string]*criticalPaths{
|
||||
"zone": {{"zone2", 2}, {"zone1", 3}},
|
||||
},
|
||||
TpPairToMatchNum: map[topologyPair]int{
|
||||
{key: "zone", value: "zone1"}: 3,
|
||||
{key: "zone", value: "zone2"}: 2,
|
||||
},
|
||||
},
|
||||
enableMatchLabelKeys: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
@ -1193,6 +1351,7 @@ func TestPreFilterState(t *testing.T) {
|
||||
p := plugintesting.SetupPluginWithInformers(ctx, t, topologySpreadFunc, args, cache.NewSnapshot(tt.existingPods, tt.nodes), tt.objs)
|
||||
p.(*PodTopologySpread).enableMinDomainsInPodTopologySpread = tt.enableMinDomains
|
||||
p.(*PodTopologySpread).enableNodeInclusionPolicyInPodTopologySpread = tt.enableNodeInclustionPolicy
|
||||
p.(*PodTopologySpread).enableMatchLabelKeysInPodTopologySpread = tt.enableMatchLabelKeys
|
||||
|
||||
cs := framework.NewCycleState()
|
||||
if _, s := p.(*PodTopologySpread).PreFilter(ctx, cs, tt.pod); !s.IsSuccess() {
|
||||
@ -1233,7 +1392,7 @@ func TestPreFilterStateAddPod(t *testing.T) {
|
||||
{
|
||||
name: "node a and b both impact current min match",
|
||||
preemptor: st.MakePod().Name("p").Label("foo", "").
|
||||
SpreadConstraint(1, "node", v1.DoNotSchedule, fooSelector, nil, nil, nil).
|
||||
SpreadConstraint(1, "node", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil).
|
||||
Obj(),
|
||||
addedPod: st.MakePod().Name("p-a1").Node("node-a").Label("foo", "").Obj(),
|
||||
existingPods: nil, // it's an empty cluster
|
||||
@ -1256,7 +1415,7 @@ func TestPreFilterStateAddPod(t *testing.T) {
|
||||
{
|
||||
name: "only node a impacts current min match",
|
||||
preemptor: st.MakePod().Name("p").Label("foo", "").
|
||||
SpreadConstraint(1, "node", v1.DoNotSchedule, fooSelector, nil, nil, nil).
|
||||
SpreadConstraint(1, "node", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil).
|
||||
Obj(),
|
||||
addedPod: st.MakePod().Name("p-a1").Node("node-a").Label("foo", "").Obj(),
|
||||
existingPods: []*v1.Pod{
|
||||
@ -1281,7 +1440,7 @@ func TestPreFilterStateAddPod(t *testing.T) {
|
||||
{
|
||||
name: "add a pod in a different namespace doesn't change topologyKeyToMinPodsMap",
|
||||
preemptor: st.MakePod().Name("p").Label("foo", "").
|
||||
SpreadConstraint(1, "node", v1.DoNotSchedule, fooSelector, nil, nil, nil).
|
||||
SpreadConstraint(1, "node", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil).
|
||||
Obj(),
|
||||
addedPod: st.MakePod().Name("p-a1").Namespace("ns1").Node("node-a").Label("foo", "").Obj(),
|
||||
existingPods: []*v1.Pod{
|
||||
@ -1306,7 +1465,7 @@ func TestPreFilterStateAddPod(t *testing.T) {
|
||||
{
|
||||
name: "add pod on non-critical node won't trigger re-calculation",
|
||||
preemptor: st.MakePod().Name("p").Label("foo", "").
|
||||
SpreadConstraint(1, "node", v1.DoNotSchedule, fooSelector, nil, nil, nil).
|
||||
SpreadConstraint(1, "node", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil).
|
||||
Obj(),
|
||||
addedPod: st.MakePod().Name("p-b2").Node("node-b").Label("foo", "").Obj(),
|
||||
existingPods: []*v1.Pod{
|
||||
@ -1331,8 +1490,8 @@ func TestPreFilterStateAddPod(t *testing.T) {
|
||||
{
|
||||
name: "node a and x both impact topologyKeyToMinPodsMap on zone and node",
|
||||
preemptor: st.MakePod().Name("p").Label("foo", "").
|
||||
SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil).
|
||||
SpreadConstraint(1, "node", v1.DoNotSchedule, fooSelector, nil, nil, nil).
|
||||
SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil).
|
||||
SpreadConstraint(1, "node", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil).
|
||||
Obj(),
|
||||
addedPod: st.MakePod().Name("p-a1").Node("node-a").Label("foo", "").Obj(),
|
||||
existingPods: nil, // it's an empty cluster
|
||||
@ -1358,8 +1517,8 @@ func TestPreFilterStateAddPod(t *testing.T) {
|
||||
{
|
||||
name: "only node a impacts topologyKeyToMinPodsMap on zone and node",
|
||||
preemptor: st.MakePod().Name("p").Label("foo", "").
|
||||
SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil).
|
||||
SpreadConstraint(1, "node", v1.DoNotSchedule, fooSelector, nil, nil, nil).
|
||||
SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil).
|
||||
SpreadConstraint(1, "node", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil).
|
||||
Obj(),
|
||||
addedPod: st.MakePod().Name("p-a1").Node("node-a").Label("foo", "").Obj(),
|
||||
existingPods: []*v1.Pod{
|
||||
@ -1387,8 +1546,8 @@ func TestPreFilterStateAddPod(t *testing.T) {
|
||||
{
|
||||
name: "node a impacts topologyKeyToMinPodsMap on node, node x impacts topologyKeyToMinPodsMap on zone",
|
||||
preemptor: st.MakePod().Name("p").Label("foo", "").
|
||||
SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil).
|
||||
SpreadConstraint(1, "node", v1.DoNotSchedule, fooSelector, nil, nil, nil).
|
||||
SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil).
|
||||
SpreadConstraint(1, "node", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil).
|
||||
Obj(),
|
||||
addedPod: st.MakePod().Name("p-a1").Node("node-a").Label("foo", "").Obj(),
|
||||
existingPods: []*v1.Pod{
|
||||
@ -1420,8 +1579,8 @@ func TestPreFilterStateAddPod(t *testing.T) {
|
||||
{
|
||||
name: "Constraints hold different labelSelectors, node a impacts topologyKeyToMinPodsMap on zone",
|
||||
preemptor: st.MakePod().Name("p").Label("foo", "").Label("bar", "").
|
||||
SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil).
|
||||
SpreadConstraint(1, "node", v1.DoNotSchedule, barSelector, nil, nil, nil).
|
||||
SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil).
|
||||
SpreadConstraint(1, "node", v1.DoNotSchedule, barSelector, nil, nil, nil, nil).
|
||||
Obj(),
|
||||
addedPod: st.MakePod().Name("p-a1").Node("node-a").Label("foo", "").Obj(),
|
||||
existingPods: []*v1.Pod{
|
||||
@ -1463,8 +1622,8 @@ func TestPreFilterStateAddPod(t *testing.T) {
|
||||
{
|
||||
name: "Constraints hold different labelSelectors, node a impacts topologyKeyToMinPodsMap on both zone and node",
|
||||
preemptor: st.MakePod().Name("p").Label("foo", "").Label("bar", "").
|
||||
SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil).
|
||||
SpreadConstraint(1, "node", v1.DoNotSchedule, barSelector, nil, nil, nil).
|
||||
SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil).
|
||||
SpreadConstraint(1, "node", v1.DoNotSchedule, barSelector, nil, nil, nil, nil).
|
||||
Obj(),
|
||||
addedPod: st.MakePod().Name("p-a1").Node("node-a").Label("foo", "").Label("bar", "").Obj(),
|
||||
existingPods: []*v1.Pod{
|
||||
@ -1506,7 +1665,7 @@ func TestPreFilterStateAddPod(t *testing.T) {
|
||||
{
|
||||
name: "add a pod that doesn't match node affinity when NodeInclustionPolicy disabled",
|
||||
preemptor: st.MakePod().Name("p").Label("foo", "").NodeAffinityNotIn("foo", []string{"bar"}).
|
||||
SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil).
|
||||
SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil).
|
||||
Obj(),
|
||||
nodeIdx: 0,
|
||||
addedPod: st.MakePod().Name("p-a1").Node("node-b").Label("foo", "").Label("zone", "zone2").Obj(),
|
||||
@ -1532,7 +1691,7 @@ func TestPreFilterStateAddPod(t *testing.T) {
|
||||
{
|
||||
name: "add a pod that doesn't match node affinity when NodeInclustionPolicy enabled",
|
||||
preemptor: st.MakePod().Name("p").Label("foo", "").NodeAffinityNotIn("foo", []string{"bar"}).
|
||||
SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil).
|
||||
SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil).
|
||||
Obj(),
|
||||
nodeIdx: 0,
|
||||
addedPod: st.MakePod().Name("p-a1").Node("node-b").Label("foo", "").Label("zone", "zone2").Obj(),
|
||||
@ -1612,7 +1771,7 @@ func TestPreFilterStateRemovePod(t *testing.T) {
|
||||
// So preemption is triggered.
|
||||
name: "one spreadConstraint on zone, topologyKeyToMinPodsMap unchanged",
|
||||
preemptor: st.MakePod().Name("p").Label("foo", "").
|
||||
SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil).
|
||||
SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil).
|
||||
Obj(),
|
||||
nodes: []*v1.Node{
|
||||
st.MakeNode().Name("node-a").Label("zone", "zone1").Label("node", "node-a").Obj(),
|
||||
@ -1640,7 +1799,7 @@ func TestPreFilterStateRemovePod(t *testing.T) {
|
||||
{
|
||||
name: "one spreadConstraint on node, topologyKeyToMinPodsMap changed",
|
||||
preemptor: st.MakePod().Name("p").Label("foo", "").
|
||||
SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil).
|
||||
SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil).
|
||||
Obj(),
|
||||
nodes: []*v1.Node{
|
||||
st.MakeNode().Name("node-a").Label("zone", "zone1").Label("node", "node-a").Obj(),
|
||||
@ -1670,7 +1829,7 @@ func TestPreFilterStateRemovePod(t *testing.T) {
|
||||
{
|
||||
name: "delete an irrelevant pod won't help",
|
||||
preemptor: st.MakePod().Name("p").Label("foo", "").
|
||||
SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil).
|
||||
SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil).
|
||||
Obj(),
|
||||
nodes: []*v1.Node{
|
||||
st.MakeNode().Name("node-a").Label("zone", "zone1").Label("node", "node-a").Obj(),
|
||||
@ -1701,7 +1860,7 @@ func TestPreFilterStateRemovePod(t *testing.T) {
|
||||
{
|
||||
name: "delete a non-existing pod won't help",
|
||||
preemptor: st.MakePod().Name("p").Label("foo", "").
|
||||
SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil).
|
||||
SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil).
|
||||
Obj(),
|
||||
nodes: []*v1.Node{
|
||||
st.MakeNode().Name("node-a").Label("zone", "zone1").Label("node", "node-a").Obj(),
|
||||
@ -1732,8 +1891,8 @@ func TestPreFilterStateRemovePod(t *testing.T) {
|
||||
{
|
||||
name: "two spreadConstraints",
|
||||
preemptor: st.MakePod().Name("p").Label("foo", "").
|
||||
SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil).
|
||||
SpreadConstraint(1, "node", v1.DoNotSchedule, fooSelector, nil, nil, nil).
|
||||
SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil).
|
||||
SpreadConstraint(1, "node", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil).
|
||||
Obj(),
|
||||
nodes: []*v1.Node{
|
||||
st.MakeNode().Name("node-a").Label("zone", "zone1").Label("node", "node-a").Obj(),
|
||||
@ -1811,7 +1970,7 @@ func BenchmarkFilter(b *testing.B) {
|
||||
{
|
||||
name: "1000nodes/single-constraint-zone",
|
||||
pod: st.MakePod().Name("p").Label("foo", "").
|
||||
SpreadConstraint(1, v1.LabelTopologyZone, v1.DoNotSchedule, fooSelector, nil, nil, nil).
|
||||
SpreadConstraint(1, v1.LabelTopologyZone, v1.DoNotSchedule, fooSelector, nil, nil, nil, nil).
|
||||
Obj(),
|
||||
existingPodsNum: 10000,
|
||||
allNodesNum: 1000,
|
||||
@ -1820,7 +1979,7 @@ func BenchmarkFilter(b *testing.B) {
|
||||
{
|
||||
name: "1000nodes/single-constraint-node",
|
||||
pod: st.MakePod().Name("p").Label("foo", "").
|
||||
SpreadConstraint(1, v1.LabelHostname, v1.DoNotSchedule, fooSelector, nil, nil, nil).
|
||||
SpreadConstraint(1, v1.LabelHostname, v1.DoNotSchedule, fooSelector, nil, nil, nil, nil).
|
||||
Obj(),
|
||||
existingPodsNum: 10000,
|
||||
allNodesNum: 1000,
|
||||
@ -1829,8 +1988,8 @@ func BenchmarkFilter(b *testing.B) {
|
||||
{
|
||||
name: "1000nodes/two-Constraints-zone-node",
|
||||
pod: st.MakePod().Name("p").Label("foo", "").Label("bar", "").
|
||||
SpreadConstraint(1, v1.LabelTopologyZone, v1.DoNotSchedule, fooSelector, nil, nil, nil).
|
||||
SpreadConstraint(1, v1.LabelHostname, v1.DoNotSchedule, barSelector, nil, nil, nil).
|
||||
SpreadConstraint(1, v1.LabelTopologyZone, v1.DoNotSchedule, fooSelector, nil, nil, nil, nil).
|
||||
SpreadConstraint(1, v1.LabelHostname, v1.DoNotSchedule, barSelector, nil, nil, nil, nil).
|
||||
Obj(),
|
||||
existingPodsNum: 10000,
|
||||
allNodesNum: 1000,
|
||||
@ -1887,7 +2046,7 @@ func TestSingleConstraint(t *testing.T) {
|
||||
{
|
||||
name: "no existing pods",
|
||||
pod: st.MakePod().Name("p").Label("foo", "").
|
||||
SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil).
|
||||
SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil).
|
||||
Obj(),
|
||||
nodes: []*v1.Node{
|
||||
st.MakeNode().Name("node-a").Label("zone", "zone1").Label("node", "node-a").Obj(),
|
||||
@ -1905,7 +2064,7 @@ func TestSingleConstraint(t *testing.T) {
|
||||
{
|
||||
name: "no existing pods, incoming pod doesn't match itself",
|
||||
pod: st.MakePod().Name("p").Label("foo", "").
|
||||
SpreadConstraint(1, "zone", v1.DoNotSchedule, barSelector, nil, nil, nil).
|
||||
SpreadConstraint(1, "zone", v1.DoNotSchedule, barSelector, nil, nil, nil, nil).
|
||||
Obj(),
|
||||
nodes: []*v1.Node{
|
||||
st.MakeNode().Name("node-a").Label("zone", "zone1").Label("node", "node-a").Obj(),
|
||||
@ -1923,7 +2082,7 @@ func TestSingleConstraint(t *testing.T) {
|
||||
{
|
||||
name: "existing pods in a different namespace do not count",
|
||||
pod: st.MakePod().Name("p").Label("foo", "").
|
||||
SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil).
|
||||
SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil).
|
||||
Obj(),
|
||||
nodes: []*v1.Node{
|
||||
st.MakeNode().Name("node-a").Label("zone", "zone1").Label("node", "node-a").Obj(),
|
||||
@ -1947,7 +2106,7 @@ func TestSingleConstraint(t *testing.T) {
|
||||
{
|
||||
name: "pods spread across zones as 3/3, all nodes fit",
|
||||
pod: st.MakePod().Name("p").Label("foo", "").
|
||||
SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil).
|
||||
SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil).
|
||||
Obj(),
|
||||
nodes: []*v1.Node{
|
||||
st.MakeNode().Name("node-a").Label("zone", "zone1").Label("node", "node-a").Obj(),
|
||||
@ -1975,7 +2134,7 @@ func TestSingleConstraint(t *testing.T) {
|
||||
// can cause unexpected behavior
|
||||
name: "pods spread across zones as 1/2 due to absence of label 'zone' on node-b",
|
||||
pod: st.MakePod().Name("p").Label("foo", "").
|
||||
SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil).
|
||||
SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil).
|
||||
Obj(),
|
||||
nodes: []*v1.Node{
|
||||
st.MakeNode().Name("node-a").Label("zone", "zone1").Label("node", "node-a").Obj(),
|
||||
@ -1999,7 +2158,7 @@ func TestSingleConstraint(t *testing.T) {
|
||||
{
|
||||
name: "pod cannot be scheduled as all nodes don't have label 'rack'",
|
||||
pod: st.MakePod().Name("p").Label("foo", "").
|
||||
SpreadConstraint(1, "rack", v1.DoNotSchedule, fooSelector, nil, nil, nil).
|
||||
SpreadConstraint(1, "rack", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil).
|
||||
Obj(),
|
||||
nodes: []*v1.Node{
|
||||
st.MakeNode().Name("node-a").Label("zone", "zone1").Label("node", "node-a").Obj(),
|
||||
@ -2013,7 +2172,7 @@ func TestSingleConstraint(t *testing.T) {
|
||||
{
|
||||
name: "pods spread across nodes as 2/1/0/3, only node-x fits",
|
||||
pod: st.MakePod().Name("p").Label("foo", "").
|
||||
SpreadConstraint(1, "node", v1.DoNotSchedule, fooSelector, nil, nil, nil).
|
||||
SpreadConstraint(1, "node", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil).
|
||||
Obj(),
|
||||
nodes: []*v1.Node{
|
||||
st.MakeNode().Name("node-a").Label("zone", "zone1").Label("node", "node-a").Obj(),
|
||||
@ -2039,7 +2198,7 @@ func TestSingleConstraint(t *testing.T) {
|
||||
{
|
||||
name: "pods spread across nodes as 2/1/0/3, maxSkew is 2, node-b and node-x fit",
|
||||
pod: st.MakePod().Name("p").Label("foo", "").
|
||||
SpreadConstraint(2, "node", v1.DoNotSchedule, fooSelector, nil, nil, nil).
|
||||
SpreadConstraint(2, "node", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil).
|
||||
Obj(),
|
||||
nodes: []*v1.Node{
|
||||
st.MakeNode().Name("node-a").Label("zone", "zone1").Label("node", "node-a").Obj(),
|
||||
@ -2069,7 +2228,7 @@ func TestSingleConstraint(t *testing.T) {
|
||||
// as the incoming pod doesn't have label "foo"
|
||||
name: "pods spread across nodes as 2/1/0/3, but pod doesn't match itself",
|
||||
pod: st.MakePod().Name("p").Label("bar", "").
|
||||
SpreadConstraint(1, "node", v1.DoNotSchedule, fooSelector, nil, nil, nil).
|
||||
SpreadConstraint(1, "node", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil).
|
||||
Obj(),
|
||||
nodes: []*v1.Node{
|
||||
st.MakeNode().Name("node-a").Label("zone", "zone1").Label("node", "node-a").Obj(),
|
||||
@ -2101,7 +2260,7 @@ func TestSingleConstraint(t *testing.T) {
|
||||
name: "incoming pod has nodeAffinity, pods spread as 2/~1~/~0~/3, hence node-a fits",
|
||||
pod: st.MakePod().Name("p").Label("foo", "").
|
||||
NodeAffinityIn("node", []string{"node-a", "node-y"}).
|
||||
SpreadConstraint(1, "node", v1.DoNotSchedule, fooSelector, nil, nil, nil).
|
||||
SpreadConstraint(1, "node", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil).
|
||||
Obj(),
|
||||
nodes: []*v1.Node{
|
||||
st.MakeNode().Name("node-a").Label("zone", "zone1").Label("node", "node-a").Obj(),
|
||||
@ -2127,7 +2286,7 @@ func TestSingleConstraint(t *testing.T) {
|
||||
{
|
||||
name: "terminating Pods should be excluded",
|
||||
pod: st.MakePod().Name("p").Label("foo", "").
|
||||
SpreadConstraint(1, "node", v1.DoNotSchedule, fooSelector, nil, nil, nil).
|
||||
SpreadConstraint(1, "node", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil).
|
||||
Obj(),
|
||||
nodes: []*v1.Node{
|
||||
st.MakeNode().Name("node-a").Label("node", "node-a").Obj(),
|
||||
@ -2147,7 +2306,7 @@ func TestSingleConstraint(t *testing.T) {
|
||||
name: "incoming pod has nodeAffinity, pods spread as 0/~2~/0/1, hence node-a fits",
|
||||
pod: st.MakePod().Name("p").Label("foo", "").
|
||||
NodeAffinityNotIn("node", []string{"node-b"}).
|
||||
SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil).
|
||||
SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil).
|
||||
Obj(),
|
||||
nodes: []*v1.Node{
|
||||
st.MakeNode().Name("node-a").Label("zone", "zone1").Label("node", "node-a").Obj(),
|
||||
@ -2177,6 +2336,7 @@ func TestSingleConstraint(t *testing.T) {
|
||||
pointer.Int32(4), // larger than the number of domains(3)
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
).Obj(),
|
||||
nodes: []*v1.Node{
|
||||
st.MakeNode().Name("node-a").Label("node", "node-a").Obj(),
|
||||
@ -2207,6 +2367,7 @@ func TestSingleConstraint(t *testing.T) {
|
||||
pointer.Int32(2), // smaller than the number of domains(3)
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
).Obj(),
|
||||
nodes: []*v1.Node{
|
||||
st.MakeNode().Name("node-a").Label("node", "node-a").Obj(),
|
||||
@ -2237,6 +2398,7 @@ func TestSingleConstraint(t *testing.T) {
|
||||
pointer.Int32(3), // larger than the number of domains(2)
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
).Obj(),
|
||||
nodes: []*v1.Node{
|
||||
st.MakeNode().Name("node-a").Label("zone", "zone1").Label("node", "node-a").Obj(),
|
||||
@ -2267,6 +2429,7 @@ func TestSingleConstraint(t *testing.T) {
|
||||
pointer.Int32(1), // smaller than the number of domains(2)
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
).Obj(),
|
||||
nodes: []*v1.Node{
|
||||
st.MakeNode().Name("node-a").Label("zone", "zone1").Label("node", "node-a").Obj(),
|
||||
@ -2292,7 +2455,7 @@ func TestSingleConstraint(t *testing.T) {
|
||||
name: "NodeAffinityPolicy honored with labelSelectors",
|
||||
pod: st.MakePod().Name("p").Label("foo", "").
|
||||
NodeSelector(map[string]string{"foo": ""}).
|
||||
SpreadConstraint(1, "node", v1.DoNotSchedule, fooSelector, nil, nil, nil).
|
||||
SpreadConstraint(1, "node", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil).
|
||||
Obj(),
|
||||
nodes: []*v1.Node{
|
||||
st.MakeNode().Name("node-a").Label("node", "node-a").Label("foo", "").Obj(),
|
||||
@ -2318,7 +2481,7 @@ func TestSingleConstraint(t *testing.T) {
|
||||
name: "NodeAffinityPolicy ignored with labelSelectors",
|
||||
pod: st.MakePod().Name("p").Label("foo", "").
|
||||
NodeSelector(map[string]string{"foo": ""}).
|
||||
SpreadConstraint(1, "node", v1.DoNotSchedule, fooSelector, nil, &ignorePolicy, nil).
|
||||
SpreadConstraint(1, "node", v1.DoNotSchedule, fooSelector, nil, &ignorePolicy, nil, nil).
|
||||
Obj(),
|
||||
nodes: []*v1.Node{
|
||||
st.MakeNode().Name("node-a").Label("node", "node-a").Label("foo", "").Obj(),
|
||||
@ -2344,7 +2507,7 @@ func TestSingleConstraint(t *testing.T) {
|
||||
name: "NodeAffinityPolicy honored with nodeAffinity",
|
||||
pod: st.MakePod().Name("p").Label("foo", "").
|
||||
NodeAffinityIn("foo", []string{""}).
|
||||
SpreadConstraint(1, "node", v1.DoNotSchedule, fooSelector, nil, nil, nil).
|
||||
SpreadConstraint(1, "node", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil).
|
||||
Obj(),
|
||||
nodes: []*v1.Node{
|
||||
st.MakeNode().Name("node-a").Label("node", "node-a").Label("foo", "").Obj(),
|
||||
@ -2370,7 +2533,7 @@ func TestSingleConstraint(t *testing.T) {
|
||||
name: "NodeAffinityPolicy ignored with labelSelectors",
|
||||
pod: st.MakePod().Name("p").Label("foo", "").
|
||||
NodeAffinityIn("foo", []string{""}).
|
||||
SpreadConstraint(1, "node", v1.DoNotSchedule, fooSelector, nil, &ignorePolicy, nil).
|
||||
SpreadConstraint(1, "node", v1.DoNotSchedule, fooSelector, nil, &ignorePolicy, nil, nil).
|
||||
Obj(),
|
||||
nodes: []*v1.Node{
|
||||
st.MakeNode().Name("node-a").Label("node", "node-a").Label("foo", "").Obj(),
|
||||
@ -2395,7 +2558,7 @@ func TestSingleConstraint(t *testing.T) {
|
||||
// pods spread across node as 1/1/0/~0~
|
||||
name: "NodeTaintsPolicy honored",
|
||||
pod: st.MakePod().Name("p").Label("foo", "").
|
||||
SpreadConstraint(1, "node", v1.DoNotSchedule, fooSelector, nil, nil, &honorPolicy).
|
||||
SpreadConstraint(1, "node", v1.DoNotSchedule, fooSelector, nil, nil, &honorPolicy, nil).
|
||||
Obj(),
|
||||
nodes: []*v1.Node{
|
||||
st.MakeNode().Name("node-a").Label("node", "node-a").Label("foo", "").Obj(),
|
||||
@ -2420,7 +2583,7 @@ func TestSingleConstraint(t *testing.T) {
|
||||
// pods spread across node as 1/1/0/~1~
|
||||
name: "NodeTaintsPolicy ignored",
|
||||
pod: st.MakePod().Name("p").Label("foo", "").
|
||||
SpreadConstraint(1, "node", v1.DoNotSchedule, fooSelector, nil, nil, nil).
|
||||
SpreadConstraint(1, "node", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil).
|
||||
Obj(),
|
||||
nodes: []*v1.Node{
|
||||
st.MakeNode().Name("node-a").Label("node", "node-a").Label("foo", "").Obj(),
|
||||
@ -2480,8 +2643,8 @@ func TestMultipleConstraints(t *testing.T) {
|
||||
// intersection of (1) and (2) returns node-x
|
||||
name: "two Constraints on zone and node, spreads = [3/3, 2/1/0/3]",
|
||||
pod: st.MakePod().Name("p").Label("foo", "").
|
||||
SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil).
|
||||
SpreadConstraint(1, "node", v1.DoNotSchedule, fooSelector, nil, nil, nil).
|
||||
SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil).
|
||||
SpreadConstraint(1, "node", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil).
|
||||
Obj(),
|
||||
nodes: []*v1.Node{
|
||||
st.MakeNode().Name("node-a").Label("zone", "zone1").Label("node", "node-a").Obj(),
|
||||
@ -2510,8 +2673,8 @@ func TestMultipleConstraints(t *testing.T) {
|
||||
// intersection of (1) and (2) returns no node
|
||||
name: "two Constraints on zone and node, spreads = [3/4, 2/1/0/4]",
|
||||
pod: st.MakePod().Name("p").Label("foo", "").
|
||||
SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil).
|
||||
SpreadConstraint(1, "node", v1.DoNotSchedule, fooSelector, nil, nil, nil).
|
||||
SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil).
|
||||
SpreadConstraint(1, "node", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil).
|
||||
Obj(),
|
||||
nodes: []*v1.Node{
|
||||
st.MakeNode().Name("node-a").Label("zone", "zone1").Label("node", "node-a").Obj(),
|
||||
@ -2541,8 +2704,8 @@ func TestMultipleConstraints(t *testing.T) {
|
||||
// intersection of (1) and (2) returns node-x
|
||||
name: "Constraints hold different labelSelectors, spreads = [1/0, 1/0/0/1]",
|
||||
pod: st.MakePod().Name("p").Label("foo", "").Label("bar", "").
|
||||
SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil).
|
||||
SpreadConstraint(1, "node", v1.DoNotSchedule, barSelector, nil, nil, nil).
|
||||
SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil).
|
||||
SpreadConstraint(1, "node", v1.DoNotSchedule, barSelector, nil, nil, nil, nil).
|
||||
Obj(),
|
||||
nodes: []*v1.Node{
|
||||
st.MakeNode().Name("node-a").Label("zone", "zone1").Label("node", "node-a").Obj(),
|
||||
@ -2567,8 +2730,8 @@ func TestMultipleConstraints(t *testing.T) {
|
||||
// intersection of (1) and (2) returns no node
|
||||
name: "Constraints hold different labelSelectors, spreads = [1/0, 0/0/1/1]",
|
||||
pod: st.MakePod().Name("p").Label("foo", "").Label("bar", "").
|
||||
SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil).
|
||||
SpreadConstraint(1, "node", v1.DoNotSchedule, barSelector, nil, nil, nil).
|
||||
SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil).
|
||||
SpreadConstraint(1, "node", v1.DoNotSchedule, barSelector, nil, nil, nil, nil).
|
||||
Obj(),
|
||||
nodes: []*v1.Node{
|
||||
st.MakeNode().Name("node-a").Label("zone", "zone1").Label("node", "node-a").Obj(),
|
||||
@ -2594,8 +2757,8 @@ func TestMultipleConstraints(t *testing.T) {
|
||||
// intersection of (1) and (2) returns node-b
|
||||
name: "Constraints hold different labelSelectors, spreads = [2/3, 1/0/0/1]",
|
||||
pod: st.MakePod().Name("p").Label("foo", "").Label("bar", "").
|
||||
SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil).
|
||||
SpreadConstraint(1, "node", v1.DoNotSchedule, barSelector, nil, nil, nil).
|
||||
SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil).
|
||||
SpreadConstraint(1, "node", v1.DoNotSchedule, barSelector, nil, nil, nil, nil).
|
||||
Obj(),
|
||||
nodes: []*v1.Node{
|
||||
st.MakeNode().Name("node-a").Label("zone", "zone1").Label("node", "node-a").Obj(),
|
||||
@ -2623,8 +2786,8 @@ func TestMultipleConstraints(t *testing.T) {
|
||||
// intersection of (1) and (2) returns node-a and node-b
|
||||
name: "Constraints hold different labelSelectors but pod doesn't match itself on 'zone' constraint",
|
||||
pod: st.MakePod().Name("p").Label("bar", "").
|
||||
SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil).
|
||||
SpreadConstraint(1, "node", v1.DoNotSchedule, barSelector, nil, nil, nil).
|
||||
SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil).
|
||||
SpreadConstraint(1, "node", v1.DoNotSchedule, barSelector, nil, nil, nil, nil).
|
||||
Obj(),
|
||||
nodes: []*v1.Node{
|
||||
st.MakeNode().Name("node-a").Label("zone", "zone1").Label("node", "node-a").Obj(),
|
||||
@ -2650,8 +2813,8 @@ func TestMultipleConstraints(t *testing.T) {
|
||||
// intersection of (1) and (2) returns node-b
|
||||
name: "two Constraints on zone and node, absence of label 'node' on node-x, spreads = [1/1, 1/0/0/1]",
|
||||
pod: st.MakePod().Name("p").Label("foo", "").
|
||||
SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil).
|
||||
SpreadConstraint(1, "node", v1.DoNotSchedule, fooSelector, nil, nil, nil).
|
||||
SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil).
|
||||
SpreadConstraint(1, "node", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil).
|
||||
Obj(),
|
||||
nodes: []*v1.Node{
|
||||
st.MakeNode().Name("node-a").Label("zone", "zone1").Label("node", "node-a").Obj(),
|
||||
@ -2677,8 +2840,8 @@ func TestMultipleConstraints(t *testing.T) {
|
||||
name: "two node inclusion Constraints, zone: honor/ignore, node: ignore/ignore",
|
||||
pod: st.MakePod().Name("p").Label("foo", "").
|
||||
NodeSelector(map[string]string{"foo": ""}).
|
||||
SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil).
|
||||
SpreadConstraint(1, "node", v1.DoNotSchedule, fooSelector, nil, &ignorePolicy, nil).
|
||||
SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil).
|
||||
SpreadConstraint(1, "node", v1.DoNotSchedule, fooSelector, nil, &ignorePolicy, nil, nil).
|
||||
Obj(),
|
||||
nodes: []*v1.Node{
|
||||
st.MakeNode().Name("node-a").Label("zone", "zone1").Label("node", "node-a").Label("foo", "").Obj(),
|
||||
@ -2705,8 +2868,8 @@ func TestMultipleConstraints(t *testing.T) {
|
||||
// intersection of (1) and (2) returns node-x
|
||||
name: "two node inclusion Constraints, zone: honor/honor, node: honor/ignore",
|
||||
pod: st.MakePod().Name("p").Label("foo", "").
|
||||
SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, &honorPolicy).
|
||||
SpreadConstraint(1, "node", v1.DoNotSchedule, fooSelector, nil, nil, nil).
|
||||
SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, &honorPolicy, nil).
|
||||
SpreadConstraint(1, "node", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil).
|
||||
Obj(),
|
||||
nodes: []*v1.Node{
|
||||
st.MakeNode().Name("node-a").Label("zone", "zone1").Label("node", "node-a").Label("foo", "").Obj(),
|
||||
@ -2734,8 +2897,8 @@ func TestMultipleConstraints(t *testing.T) {
|
||||
name: "two node inclusion Constraints, zone: honor/ignore, node: honor/ignore",
|
||||
pod: st.MakePod().Name("p").Label("foo", "").
|
||||
NodeSelector(map[string]string{"foo": ""}).
|
||||
SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil).
|
||||
SpreadConstraint(1, "node", v1.DoNotSchedule, fooSelector, nil, nil, nil).
|
||||
SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil).
|
||||
SpreadConstraint(1, "node", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil).
|
||||
Obj(),
|
||||
nodes: []*v1.Node{
|
||||
st.MakeNode().Name("node-a").Label("zone", "zone1").Label("node", "node-a").Label("foo", "").Obj(),
|
||||
@ -2763,8 +2926,8 @@ func TestMultipleConstraints(t *testing.T) {
|
||||
name: "two node inclusion Constraints, zone: honor/honor, node: ignore/ignore",
|
||||
pod: st.MakePod().Name("p").Label("foo", "").
|
||||
NodeSelector(map[string]string{"foo": ""}).
|
||||
SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, &honorPolicy).
|
||||
SpreadConstraint(1, "node", v1.DoNotSchedule, fooSelector, nil, &ignorePolicy, nil).
|
||||
SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, &honorPolicy, nil).
|
||||
SpreadConstraint(1, "node", v1.DoNotSchedule, fooSelector, nil, &ignorePolicy, nil, nil).
|
||||
Obj(),
|
||||
nodes: []*v1.Node{
|
||||
st.MakeNode().Name("node-a").Label("zone", "zone1").Label("node", "node-a").Label("foo", "").Obj(),
|
||||
|
@ -64,6 +64,7 @@ type PodTopologySpread struct {
|
||||
statefulSets appslisters.StatefulSetLister
|
||||
enableMinDomainsInPodTopologySpread bool
|
||||
enableNodeInclusionPolicyInPodTopologySpread bool
|
||||
enableMatchLabelKeysInPodTopologySpread bool
|
||||
}
|
||||
|
||||
var _ framework.PreFilterPlugin = &PodTopologySpread{}
|
||||
@ -98,6 +99,7 @@ func New(plArgs runtime.Object, h framework.Handle, fts feature.Features) (frame
|
||||
defaultConstraints: args.DefaultConstraints,
|
||||
enableMinDomainsInPodTopologySpread: fts.EnableMinDomainsInPodTopologySpread,
|
||||
enableNodeInclusionPolicyInPodTopologySpread: fts.EnableNodeInclusionPolicyInPodTopologySpread,
|
||||
enableMatchLabelKeysInPodTopologySpread: fts.EnableMatchLabelKeysInPodTopologySpread,
|
||||
}
|
||||
if args.DefaultingType == config.SystemDefaulting {
|
||||
pl.defaultConstraints = systemDefaultConstraints
|
||||
|
@ -59,11 +59,10 @@ func (s *preScoreState) Clone() framework.StateData {
|
||||
func (pl *PodTopologySpread) initPreScoreState(s *preScoreState, pod *v1.Pod, filteredNodes []*v1.Node, requireAllTopologies bool) error {
|
||||
var err error
|
||||
if len(pod.Spec.TopologySpreadConstraints) > 0 {
|
||||
s.Constraints, err = filterTopologySpreadConstraints(
|
||||
s.Constraints, err = pl.filterTopologySpreadConstraints(
|
||||
pod.Spec.TopologySpreadConstraints,
|
||||
pod.Labels,
|
||||
v1.ScheduleAnyway,
|
||||
pl.enableMinDomainsInPodTopologySpread,
|
||||
pl.enableNodeInclusionPolicyInPodTopologySpread,
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("obtaining pod's soft topology spread constraints: %w", err)
|
||||
|
@ -55,8 +55,8 @@ func TestPreScoreStateEmptyNodes(t *testing.T) {
|
||||
{
|
||||
name: "normal case",
|
||||
pod: st.MakePod().Name("p").Label("foo", "").
|
||||
SpreadConstraint(1, "zone", v1.ScheduleAnyway, fooSelector, nil, nil, nil).
|
||||
SpreadConstraint(1, v1.LabelHostname, v1.ScheduleAnyway, fooSelector, nil, nil, nil).
|
||||
SpreadConstraint(1, "zone", v1.ScheduleAnyway, fooSelector, nil, nil, nil, nil).
|
||||
SpreadConstraint(1, v1.LabelHostname, v1.ScheduleAnyway, fooSelector, nil, nil, nil, nil).
|
||||
Obj(),
|
||||
nodes: []*v1.Node{
|
||||
st.MakeNode().Name("node-a").Label("zone", "zone1").Label(v1.LabelHostname, "node-a").Obj(),
|
||||
@ -96,8 +96,8 @@ func TestPreScoreStateEmptyNodes(t *testing.T) {
|
||||
{
|
||||
name: "node-x doesn't have label zone",
|
||||
pod: st.MakePod().Name("p").Label("foo", "").
|
||||
SpreadConstraint(1, "zone", v1.ScheduleAnyway, fooSelector, nil, nil, nil).
|
||||
SpreadConstraint(1, v1.LabelHostname, v1.ScheduleAnyway, barSelector, nil, nil, nil).
|
||||
SpreadConstraint(1, "zone", v1.ScheduleAnyway, fooSelector, nil, nil, nil, nil).
|
||||
SpreadConstraint(1, v1.LabelHostname, v1.ScheduleAnyway, barSelector, nil, nil, nil, nil).
|
||||
Obj(),
|
||||
nodes: []*v1.Node{
|
||||
st.MakeNode().Name("node-a").Label("zone", "zone1").Label(v1.LabelHostname, "node-a").Obj(),
|
||||
@ -253,8 +253,8 @@ func TestPreScoreStateEmptyNodes(t *testing.T) {
|
||||
name: "default constraints and a replicaset, but pod has constraints",
|
||||
pod: st.MakePod().Name("p").Namespace("default").Label("foo", "bar").Label("baz", "sup").
|
||||
OwnerReference("rs1", appsv1.SchemeGroupVersion.WithKind("ReplicaSet")).
|
||||
SpreadConstraint(1, "zone", v1.DoNotSchedule, barSelector, nil, nil, nil).
|
||||
SpreadConstraint(2, "planet", v1.ScheduleAnyway, st.MakeLabelSelector().Label("baz", "sup").Obj(), nil, nil, nil).
|
||||
SpreadConstraint(1, "zone", v1.DoNotSchedule, barSelector, nil, nil, nil, nil).
|
||||
SpreadConstraint(2, "planet", v1.ScheduleAnyway, st.MakeLabelSelector().Label("baz", "sup").Obj(), nil, nil, nil, nil).
|
||||
Obj(),
|
||||
config: config.PodTopologySpreadArgs{
|
||||
DefaultConstraints: []v1.TopologySpreadConstraint{
|
||||
@ -294,7 +294,7 @@ func TestPreScoreStateEmptyNodes(t *testing.T) {
|
||||
name: "NodeAffinityPolicy honored with labelSelectors",
|
||||
pod: st.MakePod().Name("p").Label("foo", "").
|
||||
NodeSelector(map[string]string{"foo": ""}).
|
||||
SpreadConstraint(1, "zone", v1.ScheduleAnyway, barSelector, nil, nil, nil).
|
||||
SpreadConstraint(1, "zone", v1.ScheduleAnyway, barSelector, nil, nil, nil, nil).
|
||||
Obj(),
|
||||
nodes: []*v1.Node{
|
||||
st.MakeNode().Name("node-a").Label("zone", "zone1").Label("foo", "").Label(v1.LabelHostname, "node-a").Obj(),
|
||||
@ -328,7 +328,7 @@ func TestPreScoreStateEmptyNodes(t *testing.T) {
|
||||
name: "NodeAffinityPolicy ignored with labelSelectors",
|
||||
pod: st.MakePod().Name("p").Label("foo", "").
|
||||
NodeSelector(map[string]string{"foo": ""}).
|
||||
SpreadConstraint(1, "zone", v1.ScheduleAnyway, barSelector, nil, &ignorePolicy, nil).
|
||||
SpreadConstraint(1, "zone", v1.ScheduleAnyway, barSelector, nil, &ignorePolicy, nil, nil).
|
||||
Obj(),
|
||||
nodes: []*v1.Node{
|
||||
st.MakeNode().Name("node-a").Label("zone", "zone1").Label("foo", "").Label(v1.LabelHostname, "node-a").Obj(),
|
||||
@ -362,7 +362,7 @@ func TestPreScoreStateEmptyNodes(t *testing.T) {
|
||||
name: "NodeAffinityPolicy honored with nodeAffinity",
|
||||
pod: st.MakePod().Name("p").Label("foo", "").
|
||||
NodeAffinityIn("foo", []string{""}).
|
||||
SpreadConstraint(1, "zone", v1.ScheduleAnyway, barSelector, nil, nil, nil).
|
||||
SpreadConstraint(1, "zone", v1.ScheduleAnyway, barSelector, nil, nil, nil, nil).
|
||||
Obj(),
|
||||
nodes: []*v1.Node{
|
||||
st.MakeNode().Name("node-a").Label("zone", "zone1").Label("foo", "").Label(v1.LabelHostname, "node-a").Obj(),
|
||||
@ -396,7 +396,7 @@ func TestPreScoreStateEmptyNodes(t *testing.T) {
|
||||
name: "NodeAffinityPolicy ignored with nodeAffinity",
|
||||
pod: st.MakePod().Name("p").Label("foo", "").
|
||||
NodeAffinityIn("foo", []string{""}).
|
||||
SpreadConstraint(1, "zone", v1.ScheduleAnyway, barSelector, nil, &ignorePolicy, nil).
|
||||
SpreadConstraint(1, "zone", v1.ScheduleAnyway, barSelector, nil, &ignorePolicy, nil, nil).
|
||||
Obj(),
|
||||
nodes: []*v1.Node{
|
||||
st.MakeNode().Name("node-a").Label("zone", "zone1").Label("foo", "").Label(v1.LabelHostname, "node-a").Obj(),
|
||||
@ -429,7 +429,7 @@ func TestPreScoreStateEmptyNodes(t *testing.T) {
|
||||
{
|
||||
name: "NodeTaintsPolicy honored",
|
||||
pod: st.MakePod().Name("p").Label("foo", "").
|
||||
SpreadConstraint(1, "zone", v1.ScheduleAnyway, barSelector, nil, nil, &honorPolicy).
|
||||
SpreadConstraint(1, "zone", v1.ScheduleAnyway, barSelector, nil, nil, &honorPolicy, nil).
|
||||
Obj(),
|
||||
nodes: []*v1.Node{
|
||||
st.MakeNode().Name("node-a").Label("zone", "zone1").Label("foo", "").Label(v1.LabelHostname, "node-a").Obj(),
|
||||
@ -462,7 +462,7 @@ func TestPreScoreStateEmptyNodes(t *testing.T) {
|
||||
{
|
||||
name: "NodeTaintsPolicy ignored",
|
||||
pod: st.MakePod().Name("p").Label("foo", "").
|
||||
SpreadConstraint(1, "zone", v1.ScheduleAnyway, barSelector, nil, nil, nil).
|
||||
SpreadConstraint(1, "zone", v1.ScheduleAnyway, barSelector, nil, nil, nil, nil).
|
||||
Obj(),
|
||||
nodes: []*v1.Node{
|
||||
st.MakeNode().Name("node-a").Label("zone", "zone1").Label("foo", "").Label(v1.LabelHostname, "node-a").Obj(),
|
||||
@ -537,6 +537,7 @@ func TestPodTopologySpreadScore(t *testing.T) {
|
||||
objs []runtime.Object
|
||||
want framework.NodeScoreList
|
||||
enableNodeInclustionPolicy bool
|
||||
enableMatchLabelKeys bool
|
||||
}{
|
||||
// Explanation on the Legend:
|
||||
// a) X/Y means there are X matching pods on node1 and Y on node2, both nodes are candidates
|
||||
@ -547,7 +548,7 @@ func TestPodTopologySpreadScore(t *testing.T) {
|
||||
{
|
||||
name: "one constraint on node, no existing pods",
|
||||
pod: st.MakePod().Name("p").Label("foo", "").
|
||||
SpreadConstraint(1, v1.LabelHostname, v1.ScheduleAnyway, fooSelector, nil, nil, nil).
|
||||
SpreadConstraint(1, v1.LabelHostname, v1.ScheduleAnyway, fooSelector, nil, nil, nil, nil).
|
||||
Obj(),
|
||||
nodes: []*v1.Node{
|
||||
st.MakeNode().Name("node-a").Label(v1.LabelHostname, "node-a").Obj(),
|
||||
@ -562,7 +563,7 @@ func TestPodTopologySpreadScore(t *testing.T) {
|
||||
// if there is only one candidate node, it should be scored to 100
|
||||
name: "one constraint on node, only one node is candidate",
|
||||
pod: st.MakePod().Name("p").Label("foo", "").
|
||||
SpreadConstraint(1, v1.LabelHostname, v1.ScheduleAnyway, fooSelector, nil, nil, nil).
|
||||
SpreadConstraint(1, v1.LabelHostname, v1.ScheduleAnyway, fooSelector, nil, nil, nil, nil).
|
||||
Obj(),
|
||||
existingPods: []*v1.Pod{
|
||||
st.MakePod().Name("p-a1").Node("node-a").Label("foo", "").Obj(),
|
||||
@ -582,7 +583,7 @@ func TestPodTopologySpreadScore(t *testing.T) {
|
||||
{
|
||||
name: "one constraint on node, all nodes have the same number of matching pods",
|
||||
pod: st.MakePod().Name("p").Label("foo", "").
|
||||
SpreadConstraint(1, v1.LabelHostname, v1.ScheduleAnyway, fooSelector, nil, nil, nil).
|
||||
SpreadConstraint(1, v1.LabelHostname, v1.ScheduleAnyway, fooSelector, nil, nil, nil, nil).
|
||||
Obj(),
|
||||
existingPods: []*v1.Pod{
|
||||
st.MakePod().Name("p-a1").Node("node-a").Label("foo", "").Obj(),
|
||||
@ -601,7 +602,7 @@ func TestPodTopologySpreadScore(t *testing.T) {
|
||||
// matching pods spread as 2/1/0/3.
|
||||
name: "one constraint on node, all 4 nodes are candidates",
|
||||
pod: st.MakePod().Name("p").Label("foo", "").
|
||||
SpreadConstraint(1, v1.LabelHostname, v1.ScheduleAnyway, fooSelector, nil, nil, nil).
|
||||
SpreadConstraint(1, v1.LabelHostname, v1.ScheduleAnyway, fooSelector, nil, nil, nil, nil).
|
||||
Obj(),
|
||||
existingPods: []*v1.Pod{
|
||||
st.MakePod().Name("p-a1").Node("node-a").Label("foo", "").Obj(),
|
||||
@ -628,7 +629,7 @@ func TestPodTopologySpreadScore(t *testing.T) {
|
||||
{
|
||||
name: "one constraint on node, all 4 nodes are candidates, maxSkew=2",
|
||||
pod: st.MakePod().Name("p").Label("foo", "").
|
||||
SpreadConstraint(2, v1.LabelHostname, v1.ScheduleAnyway, fooSelector, nil, nil, nil).
|
||||
SpreadConstraint(2, v1.LabelHostname, v1.ScheduleAnyway, fooSelector, nil, nil, nil, nil).
|
||||
Obj(),
|
||||
// matching pods spread as 2/1/0/3.
|
||||
existingPods: []*v1.Pod{
|
||||
@ -656,7 +657,7 @@ func TestPodTopologySpreadScore(t *testing.T) {
|
||||
{
|
||||
name: "one constraint on node, all 4 nodes are candidates, maxSkew=3",
|
||||
pod: st.MakePod().Name("p").Label("foo", "").
|
||||
SpreadConstraint(3, v1.LabelHostname, v1.ScheduleAnyway, fooSelector, nil, nil, nil).
|
||||
SpreadConstraint(3, v1.LabelHostname, v1.ScheduleAnyway, fooSelector, nil, nil, nil, nil).
|
||||
Obj(),
|
||||
existingPods: []*v1.Pod{
|
||||
// matching pods spread as 4/3/2/1.
|
||||
@ -723,7 +724,7 @@ func TestPodTopologySpreadScore(t *testing.T) {
|
||||
// matching pods spread as 4/2/1/~3~ (node4 is not a candidate)
|
||||
name: "one constraint on node, 3 out of 4 nodes are candidates",
|
||||
pod: st.MakePod().Name("p").Label("foo", "").
|
||||
SpreadConstraint(1, v1.LabelHostname, v1.ScheduleAnyway, fooSelector, nil, nil, nil).
|
||||
SpreadConstraint(1, v1.LabelHostname, v1.ScheduleAnyway, fooSelector, nil, nil, nil, nil).
|
||||
Obj(),
|
||||
existingPods: []*v1.Pod{
|
||||
st.MakePod().Name("p-a1").Node("node-a").Label("foo", "").Obj(),
|
||||
@ -755,7 +756,7 @@ func TestPodTopologySpreadScore(t *testing.T) {
|
||||
// matching pods spread as 4/?2?/1/~3~, total = 4+?+1 = 5 (as node2 is problematic)
|
||||
name: "one constraint on node, 3 out of 4 nodes are candidates, one node doesn't match topology key",
|
||||
pod: st.MakePod().Name("p").Label("foo", "").
|
||||
SpreadConstraint(1, v1.LabelHostname, v1.ScheduleAnyway, fooSelector, nil, nil, nil).
|
||||
SpreadConstraint(1, v1.LabelHostname, v1.ScheduleAnyway, fooSelector, nil, nil, nil, nil).
|
||||
Obj(),
|
||||
existingPods: []*v1.Pod{
|
||||
st.MakePod().Name("p-a1").Node("node-a").Label("foo", "").Obj(),
|
||||
@ -787,7 +788,7 @@ func TestPodTopologySpreadScore(t *testing.T) {
|
||||
// matching pods spread as 4/2/1/~3~
|
||||
name: "one constraint on zone, 3 out of 4 nodes are candidates",
|
||||
pod: st.MakePod().Name("p").Label("foo", "").
|
||||
SpreadConstraint(1, "zone", v1.ScheduleAnyway, fooSelector, nil, nil, nil).
|
||||
SpreadConstraint(1, "zone", v1.ScheduleAnyway, fooSelector, nil, nil, nil, nil).
|
||||
Obj(),
|
||||
existingPods: []*v1.Pod{
|
||||
st.MakePod().Name("p-a1").Node("node-a").Label("foo", "").Obj(),
|
||||
@ -819,8 +820,8 @@ func TestPodTopologySpreadScore(t *testing.T) {
|
||||
// matching pods spread as 2/~1~/2/~4~.
|
||||
name: "two Constraints on zone and node, 2 out of 4 nodes are candidates",
|
||||
pod: st.MakePod().Name("p").Label("foo", "").
|
||||
SpreadConstraint(1, "zone", v1.ScheduleAnyway, fooSelector, nil, nil, nil).
|
||||
SpreadConstraint(1, v1.LabelHostname, v1.ScheduleAnyway, fooSelector, nil, nil, nil).
|
||||
SpreadConstraint(1, "zone", v1.ScheduleAnyway, fooSelector, nil, nil, nil, nil).
|
||||
SpreadConstraint(1, v1.LabelHostname, v1.ScheduleAnyway, fooSelector, nil, nil, nil, nil).
|
||||
Obj(),
|
||||
existingPods: []*v1.Pod{
|
||||
st.MakePod().Name("p-a1").Node("node-a").Label("foo", "").Obj(),
|
||||
@ -859,8 +860,8 @@ func TestPodTopologySpreadScore(t *testing.T) {
|
||||
// For the second constraint (node): the matching pods spread as 0/1/0/1
|
||||
name: "two Constraints on zone and node, with different labelSelectors",
|
||||
pod: st.MakePod().Name("p").Label("foo", "").Label("bar", "").
|
||||
SpreadConstraint(1, "zone", v1.ScheduleAnyway, fooSelector, nil, nil, nil).
|
||||
SpreadConstraint(1, v1.LabelHostname, v1.ScheduleAnyway, barSelector, nil, nil, nil).
|
||||
SpreadConstraint(1, "zone", v1.ScheduleAnyway, fooSelector, nil, nil, nil, nil).
|
||||
SpreadConstraint(1, v1.LabelHostname, v1.ScheduleAnyway, barSelector, nil, nil, nil, nil).
|
||||
Obj(),
|
||||
existingPods: []*v1.Pod{
|
||||
st.MakePod().Name("p-a1").Node("node-a").Label("foo", "").Obj(),
|
||||
@ -887,8 +888,8 @@ func TestPodTopologySpreadScore(t *testing.T) {
|
||||
// For the second constraint (node): the matching pods spread as 0/1/0/1
|
||||
name: "two Constraints on zone and node, with different labelSelectors, some nodes have 0 pods",
|
||||
pod: st.MakePod().Name("p").Label("foo", "").Label("bar", "").
|
||||
SpreadConstraint(1, "zone", v1.ScheduleAnyway, fooSelector, nil, nil, nil).
|
||||
SpreadConstraint(1, v1.LabelHostname, v1.ScheduleAnyway, barSelector, nil, nil, nil).
|
||||
SpreadConstraint(1, "zone", v1.ScheduleAnyway, fooSelector, nil, nil, nil, nil).
|
||||
SpreadConstraint(1, v1.LabelHostname, v1.ScheduleAnyway, barSelector, nil, nil, nil, nil).
|
||||
Obj(),
|
||||
existingPods: []*v1.Pod{
|
||||
st.MakePod().Name("p-b1").Node("node-b").Label("bar", "").Obj(),
|
||||
@ -914,8 +915,8 @@ func TestPodTopologySpreadScore(t *testing.T) {
|
||||
// For the second constraint (node): the matching pods spread as 0/1/0/~1~
|
||||
name: "two Constraints on zone and node, with different labelSelectors, 3 out of 4 nodes are candidates",
|
||||
pod: st.MakePod().Name("p").Label("foo", "").Label("bar", "").
|
||||
SpreadConstraint(1, "zone", v1.ScheduleAnyway, fooSelector, nil, nil, nil).
|
||||
SpreadConstraint(1, v1.LabelHostname, v1.ScheduleAnyway, barSelector, nil, nil, nil).
|
||||
SpreadConstraint(1, "zone", v1.ScheduleAnyway, fooSelector, nil, nil, nil, nil).
|
||||
SpreadConstraint(1, v1.LabelHostname, v1.ScheduleAnyway, barSelector, nil, nil, nil, nil).
|
||||
Obj(),
|
||||
existingPods: []*v1.Pod{
|
||||
st.MakePod().Name("p-a1").Node("node-a").Label("foo", "").Obj(),
|
||||
@ -940,7 +941,7 @@ func TestPodTopologySpreadScore(t *testing.T) {
|
||||
{
|
||||
name: "existing pods in a different namespace do not count",
|
||||
pod: st.MakePod().Name("p").Label("foo", "").
|
||||
SpreadConstraint(1, v1.LabelHostname, v1.ScheduleAnyway, fooSelector, nil, nil, nil).
|
||||
SpreadConstraint(1, v1.LabelHostname, v1.ScheduleAnyway, fooSelector, nil, nil, nil, nil).
|
||||
Obj(),
|
||||
existingPods: []*v1.Pod{
|
||||
st.MakePod().Name("p-a1").Namespace("ns1").Node("node-a").Label("foo", "").Obj(),
|
||||
@ -960,7 +961,7 @@ func TestPodTopologySpreadScore(t *testing.T) {
|
||||
{
|
||||
name: "terminating Pods should be excluded",
|
||||
pod: st.MakePod().Name("p").Label("foo", "").
|
||||
SpreadConstraint(1, v1.LabelHostname, v1.ScheduleAnyway, fooSelector, nil, nil, nil).
|
||||
SpreadConstraint(1, v1.LabelHostname, v1.ScheduleAnyway, fooSelector, nil, nil, nil, nil).
|
||||
Obj(),
|
||||
nodes: []*v1.Node{
|
||||
st.MakeNode().Name("node-a").Label(v1.LabelHostname, "node-a").Obj(),
|
||||
@ -987,6 +988,7 @@ func TestPodTopologySpreadScore(t *testing.T) {
|
||||
pointer.Int32(10), // larger than the number of domains(3)
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
).Obj(),
|
||||
nodes: []*v1.Node{
|
||||
st.MakeNode().Name("node-a").Label("node", "node-a").Obj(),
|
||||
@ -1010,7 +1012,7 @@ func TestPodTopologySpreadScore(t *testing.T) {
|
||||
name: "NodeAffinityPolicy honoed with labelSelectors",
|
||||
pod: st.MakePod().Name("p").Label("foo", "").
|
||||
NodeSelector(map[string]string{"foo": ""}).
|
||||
SpreadConstraint(1, "node", v1.ScheduleAnyway, fooSelector, nil, nil, nil).
|
||||
SpreadConstraint(1, "node", v1.ScheduleAnyway, fooSelector, nil, nil, nil, nil).
|
||||
Obj(),
|
||||
nodes: []*v1.Node{
|
||||
st.MakeNode().Name("node-a").Label("node", "node-a").Label("foo", "").Obj(),
|
||||
@ -1034,7 +1036,7 @@ func TestPodTopologySpreadScore(t *testing.T) {
|
||||
name: "NodeAffinityPolicy ignored with labelSelectors",
|
||||
pod: st.MakePod().Name("p").Label("foo", "").
|
||||
NodeSelector(map[string]string{"foo": ""}).
|
||||
SpreadConstraint(1, "node", v1.ScheduleAnyway, fooSelector, nil, &ignorePolicy, nil).
|
||||
SpreadConstraint(1, "node", v1.ScheduleAnyway, fooSelector, nil, &ignorePolicy, nil, nil).
|
||||
Obj(),
|
||||
nodes: []*v1.Node{
|
||||
st.MakeNode().Name("node-a").Label("node", "node-a").Label("foo", "").Obj(),
|
||||
@ -1058,7 +1060,7 @@ func TestPodTopologySpreadScore(t *testing.T) {
|
||||
name: "NodeAffinityPolicy honoed with nodeAffinity",
|
||||
pod: st.MakePod().Name("p").Label("foo", "").
|
||||
NodeAffinityIn("foo", []string{""}).
|
||||
SpreadConstraint(1, "node", v1.ScheduleAnyway, fooSelector, nil, nil, nil).
|
||||
SpreadConstraint(1, "node", v1.ScheduleAnyway, fooSelector, nil, nil, nil, nil).
|
||||
Obj(),
|
||||
nodes: []*v1.Node{
|
||||
st.MakeNode().Name("node-a").Label("node", "node-a").Label("foo", "").Obj(),
|
||||
@ -1082,7 +1084,7 @@ func TestPodTopologySpreadScore(t *testing.T) {
|
||||
name: "NodeAffinityPolicy ignored with nodeAffinity",
|
||||
pod: st.MakePod().Name("p").Label("foo", "").
|
||||
NodeAffinityIn("foo", []string{""}).
|
||||
SpreadConstraint(1, "node", v1.ScheduleAnyway, fooSelector, nil, &ignorePolicy, nil).
|
||||
SpreadConstraint(1, "node", v1.ScheduleAnyway, fooSelector, nil, &ignorePolicy, nil, nil).
|
||||
Obj(),
|
||||
nodes: []*v1.Node{
|
||||
st.MakeNode().Name("node-a").Label("node", "node-a").Label("foo", "").Obj(),
|
||||
@ -1105,7 +1107,7 @@ func TestPodTopologySpreadScore(t *testing.T) {
|
||||
{
|
||||
name: "NodeTaintsPolicy honored",
|
||||
pod: st.MakePod().Name("p").Label("foo", "").
|
||||
SpreadConstraint(1, "node", v1.ScheduleAnyway, fooSelector, nil, nil, &honorPolicy).
|
||||
SpreadConstraint(1, "node", v1.ScheduleAnyway, fooSelector, nil, nil, &honorPolicy, nil).
|
||||
Obj(),
|
||||
nodes: []*v1.Node{
|
||||
st.MakeNode().Name("node-a").Label("node", "node-a").Obj(),
|
||||
@ -1128,7 +1130,7 @@ func TestPodTopologySpreadScore(t *testing.T) {
|
||||
{
|
||||
name: "NodeTaintsPolicy ignored",
|
||||
pod: st.MakePod().Name("p").Label("foo", "").
|
||||
SpreadConstraint(1, "node", v1.ScheduleAnyway, fooSelector, nil, nil, nil).
|
||||
SpreadConstraint(1, "node", v1.ScheduleAnyway, fooSelector, nil, nil, nil, nil).
|
||||
Obj(),
|
||||
nodes: []*v1.Node{
|
||||
st.MakeNode().Name("node-a").Label("node", "node-a").Obj(),
|
||||
@ -1148,6 +1150,85 @@ func TestPodTopologySpreadScore(t *testing.T) {
|
||||
},
|
||||
enableNodeInclustionPolicy: true,
|
||||
},
|
||||
{
|
||||
name: "matchLabelKeys ignored when feature gate disabled",
|
||||
pod: st.MakePod().Name("p").Label("foo", "").Label("bar", "").Label("baz", "").
|
||||
SpreadConstraint(1, "zone", v1.ScheduleAnyway, fooSelector, nil, nil, nil, []string{"baz"}).
|
||||
SpreadConstraint(1, v1.LabelHostname, v1.ScheduleAnyway, barSelector, nil, nil, nil, []string{"baz"}).
|
||||
Obj(),
|
||||
existingPods: []*v1.Pod{
|
||||
st.MakePod().Name("p-a1").Node("node-a").Label("foo", "").Obj(),
|
||||
st.MakePod().Name("p-b1").Node("node-b").Label("foo", "").Label("bar", "").Obj(),
|
||||
st.MakePod().Name("p-y1").Node("node-c").Label("foo", "").Obj(),
|
||||
st.MakePod().Name("p-y2").Node("node-c").Label("bar", "").Obj(),
|
||||
},
|
||||
nodes: []*v1.Node{
|
||||
st.MakeNode().Name("node-a").Label("zone", "zone1").Label(v1.LabelHostname, "node-a").Obj(),
|
||||
st.MakeNode().Name("node-b").Label("zone", "zone1").Label(v1.LabelHostname, "node-b").Obj(),
|
||||
st.MakeNode().Name("node-c").Label("zone", "zone2").Label(v1.LabelHostname, "node-c").Obj(),
|
||||
st.MakeNode().Name("node-d").Label("zone", "zone2").Label(v1.LabelHostname, "node-d").Obj(),
|
||||
},
|
||||
want: []framework.NodeScore{
|
||||
{Name: "node-a", Score: 60},
|
||||
{Name: "node-b", Score: 20},
|
||||
{Name: "node-c", Score: 60},
|
||||
{Name: "node-d", Score: 100},
|
||||
},
|
||||
enableMatchLabelKeys: false,
|
||||
},
|
||||
{
|
||||
name: "matchLabelKeys ANDed with LabelSelector when LabelSelector is empty",
|
||||
pod: st.MakePod().Name("p").Label("foo", "").Label("bar", "").
|
||||
SpreadConstraint(1, "zone", v1.ScheduleAnyway, st.MakeLabelSelector().Obj(), nil, nil, nil, []string{"foo"}).
|
||||
SpreadConstraint(1, v1.LabelHostname, v1.ScheduleAnyway, st.MakeLabelSelector().Obj(), nil, nil, nil, []string{"bar"}).
|
||||
Obj(),
|
||||
existingPods: []*v1.Pod{
|
||||
st.MakePod().Name("p-a1").Node("node-a").Label("foo", "").Obj(),
|
||||
st.MakePod().Name("p-b1").Node("node-b").Label("foo", "").Label("bar", "").Obj(),
|
||||
st.MakePod().Name("p-y1").Node("node-c").Label("foo", "").Obj(),
|
||||
st.MakePod().Name("p-y2").Node("node-c").Label("bar", "").Obj(),
|
||||
},
|
||||
nodes: []*v1.Node{
|
||||
st.MakeNode().Name("node-a").Label("zone", "zone1").Label(v1.LabelHostname, "node-a").Obj(),
|
||||
st.MakeNode().Name("node-b").Label("zone", "zone1").Label(v1.LabelHostname, "node-b").Obj(),
|
||||
st.MakeNode().Name("node-c").Label("zone", "zone2").Label(v1.LabelHostname, "node-c").Obj(),
|
||||
st.MakeNode().Name("node-d").Label("zone", "zone2").Label(v1.LabelHostname, "node-d").Obj(),
|
||||
},
|
||||
want: []framework.NodeScore{
|
||||
{Name: "node-a", Score: 60},
|
||||
{Name: "node-b", Score: 20},
|
||||
{Name: "node-c", Score: 60},
|
||||
{Name: "node-d", Score: 100},
|
||||
},
|
||||
enableMatchLabelKeys: true,
|
||||
},
|
||||
{
|
||||
name: "matchLabelKeys ANDed with LabelSelector when LabelSelector isn't empty",
|
||||
pod: st.MakePod().Name("p").Label("foo", "").Label("bar", "").Label("baz", "").
|
||||
SpreadConstraint(1, "zone", v1.ScheduleAnyway, fooSelector, nil, nil, nil, nil).
|
||||
SpreadConstraint(1, v1.LabelHostname, v1.ScheduleAnyway, barSelector, nil, nil, nil, []string{"baz"}).
|
||||
Obj(),
|
||||
existingPods: []*v1.Pod{
|
||||
st.MakePod().Name("p-a1").Node("node-a").Label("foo", "").Obj(),
|
||||
st.MakePod().Name("p-b1").Node("node-b").Label("foo", "").Label("bar", "").Label("baz", "").Obj(),
|
||||
st.MakePod().Name("p-c1").Node("node-c").Label("foo", "").Obj(),
|
||||
st.MakePod().Name("p-c2").Node("node-c").Label("bar", "").Obj(),
|
||||
st.MakePod().Name("p-d3").Node("node-c").Label("bar", "").Label("baz", "").Obj(),
|
||||
},
|
||||
nodes: []*v1.Node{
|
||||
st.MakeNode().Name("node-a").Label("zone", "zone1").Label(v1.LabelHostname, "node-a").Obj(),
|
||||
st.MakeNode().Name("node-b").Label("zone", "zone1").Label(v1.LabelHostname, "node-b").Obj(),
|
||||
st.MakeNode().Name("node-c").Label("zone", "zone2").Label(v1.LabelHostname, "node-c").Obj(),
|
||||
st.MakeNode().Name("node-d").Label("zone", "zone2").Label(v1.LabelHostname, "node-d").Obj(),
|
||||
},
|
||||
want: []framework.NodeScore{
|
||||
{Name: "node-a", Score: 60},
|
||||
{Name: "node-b", Score: 20},
|
||||
{Name: "node-c", Score: 60},
|
||||
{Name: "node-d", Score: 100},
|
||||
},
|
||||
enableMatchLabelKeys: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
@ -1159,6 +1240,7 @@ func TestPodTopologySpreadScore(t *testing.T) {
|
||||
pl := plugintesting.SetupPluginWithInformers(ctx, t, podTopologySpreadFunc, &config.PodTopologySpreadArgs{DefaultingType: config.SystemDefaulting}, cache.NewSnapshot(tt.existingPods, allNodes), tt.objs)
|
||||
p := pl.(*PodTopologySpread)
|
||||
p.enableNodeInclusionPolicyInPodTopologySpread = tt.enableNodeInclustionPolicy
|
||||
p.enableMatchLabelKeysInPodTopologySpread = tt.enableMatchLabelKeys
|
||||
|
||||
status := p.PreScore(context.Background(), state, tt.pod, tt.nodes)
|
||||
if !status.IsSuccess() {
|
||||
@ -1199,7 +1281,7 @@ func BenchmarkTestPodTopologySpreadScore(b *testing.B) {
|
||||
{
|
||||
name: "1000nodes/single-constraint-zone",
|
||||
pod: st.MakePod().Name("p").Label("foo", "").
|
||||
SpreadConstraint(1, v1.LabelTopologyZone, v1.ScheduleAnyway, fooSelector, nil, nil, nil).
|
||||
SpreadConstraint(1, v1.LabelTopologyZone, v1.ScheduleAnyway, fooSelector, nil, nil, nil, nil).
|
||||
Obj(),
|
||||
existingPodsNum: 10000,
|
||||
allNodesNum: 1000,
|
||||
@ -1208,7 +1290,7 @@ func BenchmarkTestPodTopologySpreadScore(b *testing.B) {
|
||||
{
|
||||
name: "1000nodes/single-constraint-node",
|
||||
pod: st.MakePod().Name("p").Label("foo", "").
|
||||
SpreadConstraint(1, v1.LabelHostname, v1.ScheduleAnyway, fooSelector, nil, nil, nil).
|
||||
SpreadConstraint(1, v1.LabelHostname, v1.ScheduleAnyway, fooSelector, nil, nil, nil, nil).
|
||||
Obj(),
|
||||
existingPodsNum: 10000,
|
||||
allNodesNum: 1000,
|
||||
@ -1217,8 +1299,8 @@ func BenchmarkTestPodTopologySpreadScore(b *testing.B) {
|
||||
{
|
||||
name: "1000nodes/two-Constraints-zone-node",
|
||||
pod: st.MakePod().Name("p").Label("foo", "").Label("bar", "").
|
||||
SpreadConstraint(1, v1.LabelTopologyZone, v1.ScheduleAnyway, fooSelector, nil, nil, nil).
|
||||
SpreadConstraint(1, v1.LabelHostname, v1.ScheduleAnyway, barSelector, nil, nil, nil).
|
||||
SpreadConstraint(1, v1.LabelTopologyZone, v1.ScheduleAnyway, fooSelector, nil, nil, nil, nil).
|
||||
SpreadConstraint(1, v1.LabelHostname, v1.ScheduleAnyway, barSelector, nil, nil, nil, nil).
|
||||
Obj(),
|
||||
existingPodsNum: 10000,
|
||||
allNodesNum: 1000,
|
||||
|
@ -49,6 +49,7 @@ func NewInTreeRegistry() runtime.Registry {
|
||||
EnableVolumeCapacityPriority: feature.DefaultFeatureGate.Enabled(features.VolumeCapacityPriority),
|
||||
EnableMinDomainsInPodTopologySpread: feature.DefaultFeatureGate.Enabled(features.MinDomainsInPodTopologySpread),
|
||||
EnableNodeInclusionPolicyInPodTopologySpread: feature.DefaultFeatureGate.Enabled(features.NodeInclusionPolicyInPodTopologySpread),
|
||||
EnableMatchLabelKeysInPodTopologySpread: feature.DefaultFeatureGate.Enabled(features.MatchLabelKeysInPodTopologySpread),
|
||||
}
|
||||
|
||||
return runtime.Registry{
|
||||
|
@ -1759,7 +1759,7 @@ func TestSchedulerSchedulePod(t *testing.T) {
|
||||
Operator: metav1.LabelSelectorOpExists,
|
||||
},
|
||||
},
|
||||
}, nil, nil, nil).Obj(),
|
||||
}, nil, nil, nil, nil).Obj(),
|
||||
pods: []*v1.Pod{
|
||||
st.MakePod().Name("pod1").UID("pod1").Label("foo", "").Node("node1").Phase(v1.PodRunning).Obj(),
|
||||
},
|
||||
@ -1786,7 +1786,7 @@ func TestSchedulerSchedulePod(t *testing.T) {
|
||||
Operator: metav1.LabelSelectorOpExists,
|
||||
},
|
||||
},
|
||||
}, nil, nil, nil).Obj(),
|
||||
}, nil, nil, nil, nil).Obj(),
|
||||
pods: []*v1.Pod{
|
||||
st.MakePod().Name("pod1a").UID("pod1a").Label("foo", "").Node("node1").Phase(v1.PodRunning).Obj(),
|
||||
st.MakePod().Name("pod1b").UID("pod1b").Label("foo", "").Node("node1").Phase(v1.PodRunning).Obj(),
|
||||
|
@ -547,7 +547,7 @@ func (p *PodWrapper) PodAntiAffinityNotIn(labelKey, topologyKey string, vals []s
|
||||
|
||||
// SpreadConstraint constructs a TopologySpreadConstraint object and injects
|
||||
// into the inner pod.
|
||||
func (p *PodWrapper) SpreadConstraint(maxSkew int, tpKey string, mode v1.UnsatisfiableConstraintAction, selector *metav1.LabelSelector, minDomains *int32, nodeAffinityPolicy, nodeTaintsPolicy *v1.NodeInclusionPolicy) *PodWrapper {
|
||||
func (p *PodWrapper) SpreadConstraint(maxSkew int, tpKey string, mode v1.UnsatisfiableConstraintAction, selector *metav1.LabelSelector, minDomains *int32, nodeAffinityPolicy, nodeTaintsPolicy *v1.NodeInclusionPolicy, matchLabelKeys []string) *PodWrapper {
|
||||
c := v1.TopologySpreadConstraint{
|
||||
MaxSkew: int32(maxSkew),
|
||||
TopologyKey: tpKey,
|
||||
@ -556,6 +556,7 @@ func (p *PodWrapper) SpreadConstraint(maxSkew int, tpKey string, mode v1.Unsatis
|
||||
MinDomains: minDomains,
|
||||
NodeAffinityPolicy: nodeAffinityPolicy,
|
||||
NodeTaintsPolicy: nodeTaintsPolicy,
|
||||
MatchLabelKeys: matchLabelKeys,
|
||||
}
|
||||
p.Spec.TopologySpreadConstraints = append(p.Spec.TopologySpreadConstraints, c)
|
||||
return p
|
||||
|
1837
staging/src/k8s.io/api/core/v1/generated.pb.go
generated
1837
staging/src/k8s.io/api/core/v1/generated.pb.go
generated
File diff suppressed because it is too large
Load Diff
@ -5499,6 +5499,16 @@ message TopologySpreadConstraint {
|
||||
// This is a alpha-level feature enabled by the NodeInclusionPolicyInPodTopologySpread feature flag.
|
||||
// +optional
|
||||
optional string nodeTaintsPolicy = 7;
|
||||
|
||||
// MatchLabelKeys is a set of pod label keys to select the pods over which
|
||||
// spreading will be calculated. The keys are used to lookup values from the
|
||||
// incoming pod labels, those key-value labels are ANDed with labelSelector
|
||||
// to select the group of existing pods over which spreading will be calculated
|
||||
// for the incoming pod. Keys that don't exist in the incoming pod labels will
|
||||
// be ignored. A null or empty list means only match against labelSelector.
|
||||
// +listType=atomic
|
||||
// +optional
|
||||
repeated string matchLabelKeys = 8;
|
||||
}
|
||||
|
||||
// TypedLocalObjectReference contains enough information to let you locate the
|
||||
|
@ -3454,6 +3454,15 @@ type TopologySpreadConstraint struct {
|
||||
// This is a alpha-level feature enabled by the NodeInclusionPolicyInPodTopologySpread feature flag.
|
||||
// +optional
|
||||
NodeTaintsPolicy *NodeInclusionPolicy `json:"nodeTaintsPolicy,omitempty" protobuf:"bytes,7,opt,name=nodeTaintsPolicy"`
|
||||
// MatchLabelKeys is a set of pod label keys to select the pods over which
|
||||
// spreading will be calculated. The keys are used to lookup values from the
|
||||
// incoming pod labels, those key-value labels are ANDed with labelSelector
|
||||
// to select the group of existing pods over which spreading will be calculated
|
||||
// for the incoming pod. Keys that don't exist in the incoming pod labels will
|
||||
// be ignored. A null or empty list means only match against labelSelector.
|
||||
// +listType=atomic
|
||||
// +optional
|
||||
MatchLabelKeys []string `json:"matchLabelKeys,omitempty" protobuf:"bytes,8,opt,name=matchLabelKeys"`
|
||||
}
|
||||
|
||||
const (
|
||||
|
@ -2408,6 +2408,7 @@ var map_TopologySpreadConstraint = map[string]string{
|
||||
"minDomains": "MinDomains indicates a minimum number of eligible domains. When the number of eligible domains with matching topology keys is less than minDomains, Pod Topology Spread treats \"global minimum\" as 0, and then the calculation of Skew is performed. And when the number of eligible domains with matching topology keys equals or greater than minDomains, this value has no effect on scheduling. As a result, when the number of eligible domains is less than minDomains, scheduler won't schedule more than maxSkew Pods to those domains. If value is nil, the constraint behaves as if MinDomains is equal to 1. Valid values are integers greater than 0. When value is not nil, WhenUnsatisfiable must be DoNotSchedule.\n\nFor example, in a 3-zone cluster, MaxSkew is set to 2, MinDomains is set to 5 and pods with the same labelSelector spread as 2/2/2: ",
|
||||
"nodeAffinityPolicy": "NodeAffinityPolicy indicates how we will treat Pod's nodeAffinity/nodeSelector when calculating pod topology spread skew. Options are: - Honor: only nodes matching nodeAffinity/nodeSelector are included in the calculations. - Ignore: nodeAffinity/nodeSelector are ignored. All nodes are included in the calculations.\n\nIf this value is nil, the behavior is equivalent to the Honor policy. This is a alpha-level feature enabled by the NodeInclusionPolicyInPodTopologySpread feature flag.",
|
||||
"nodeTaintsPolicy": "NodeTaintsPolicy indicates how we will treat node taints when calculating pod topology spread skew. Options are: - Honor: nodes without taints, along with tainted nodes for which the incoming pod has a toleration, are included. - Ignore: node taints are ignored. All nodes are included.\n\nIf this value is nil, the behavior is equivalent to the Ignore policy. This is a alpha-level feature enabled by the NodeInclusionPolicyInPodTopologySpread feature flag.",
|
||||
"matchLabelKeys": "MatchLabelKeys is a set of pod label keys to select the pods over which spreading will be calculated. The keys are used to lookup values from the incoming pod labels, those key-value labels are ANDed with labelSelector to select the group of existing pods over which spreading will be calculated for the incoming pod. Keys that don't exist in the incoming pod labels will be ignored. A null or empty list means only match against labelSelector.",
|
||||
}
|
||||
|
||||
func (TopologySpreadConstraint) SwaggerDoc() map[string]string {
|
||||
|
@ -5664,6 +5664,11 @@ func (in *TopologySpreadConstraint) DeepCopyInto(out *TopologySpreadConstraint)
|
||||
*out = new(NodeInclusionPolicy)
|
||||
**out = **in
|
||||
}
|
||||
if in.MatchLabelKeys != nil {
|
||||
in, out := &in.MatchLabelKeys, &out.MatchLabelKeys
|
||||
*out = make([]string, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -1616,7 +1616,10 @@
|
||||
},
|
||||
"minDomains": 5,
|
||||
"nodeAffinityPolicy": "nodeAffinityPolicyValue",
|
||||
"nodeTaintsPolicy": "nodeTaintsPolicyValue"
|
||||
"nodeTaintsPolicy": "nodeTaintsPolicyValue",
|
||||
"matchLabelKeys": [
|
||||
"matchLabelKeysValue"
|
||||
]
|
||||
}
|
||||
],
|
||||
"setHostnameAsFQDN": true,
|
||||
|
Binary file not shown.
@ -846,6 +846,8 @@ spec:
|
||||
- valuesValue
|
||||
matchLabels:
|
||||
matchLabelsKey: matchLabelsValue
|
||||
matchLabelKeys:
|
||||
- matchLabelKeysValue
|
||||
maxSkew: 1
|
||||
minDomains: 5
|
||||
nodeAffinityPolicy: nodeAffinityPolicyValue
|
||||
|
@ -1617,7 +1617,10 @@
|
||||
},
|
||||
"minDomains": 5,
|
||||
"nodeAffinityPolicy": "nodeAffinityPolicyValue",
|
||||
"nodeTaintsPolicy": "nodeTaintsPolicyValue"
|
||||
"nodeTaintsPolicy": "nodeTaintsPolicyValue",
|
||||
"matchLabelKeys": [
|
||||
"matchLabelKeysValue"
|
||||
]
|
||||
}
|
||||
],
|
||||
"setHostnameAsFQDN": true,
|
||||
|
Binary file not shown.
@ -854,6 +854,8 @@ spec:
|
||||
- valuesValue
|
||||
matchLabels:
|
||||
matchLabelsKey: matchLabelsValue
|
||||
matchLabelKeys:
|
||||
- matchLabelKeysValue
|
||||
maxSkew: 1
|
||||
minDomains: 5
|
||||
nodeAffinityPolicy: nodeAffinityPolicyValue
|
||||
|
@ -1618,7 +1618,10 @@
|
||||
},
|
||||
"minDomains": 5,
|
||||
"nodeAffinityPolicy": "nodeAffinityPolicyValue",
|
||||
"nodeTaintsPolicy": "nodeTaintsPolicyValue"
|
||||
"nodeTaintsPolicy": "nodeTaintsPolicyValue",
|
||||
"matchLabelKeys": [
|
||||
"matchLabelKeysValue"
|
||||
]
|
||||
}
|
||||
],
|
||||
"setHostnameAsFQDN": true,
|
||||
|
Binary file not shown.
@ -846,6 +846,8 @@ spec:
|
||||
- valuesValue
|
||||
matchLabels:
|
||||
matchLabelsKey: matchLabelsValue
|
||||
matchLabelKeys:
|
||||
- matchLabelKeysValue
|
||||
maxSkew: 1
|
||||
minDomains: 5
|
||||
nodeAffinityPolicy: nodeAffinityPolicyValue
|
||||
|
@ -1617,7 +1617,10 @@
|
||||
},
|
||||
"minDomains": 5,
|
||||
"nodeAffinityPolicy": "nodeAffinityPolicyValue",
|
||||
"nodeTaintsPolicy": "nodeTaintsPolicyValue"
|
||||
"nodeTaintsPolicy": "nodeTaintsPolicyValue",
|
||||
"matchLabelKeys": [
|
||||
"matchLabelKeysValue"
|
||||
]
|
||||
}
|
||||
],
|
||||
"setHostnameAsFQDN": true,
|
||||
|
Binary file not shown.
@ -852,6 +852,8 @@ spec:
|
||||
- valuesValue
|
||||
matchLabels:
|
||||
matchLabelsKey: matchLabelsValue
|
||||
matchLabelKeys:
|
||||
- matchLabelKeysValue
|
||||
maxSkew: 1
|
||||
minDomains: 5
|
||||
nodeAffinityPolicy: nodeAffinityPolicyValue
|
||||
|
@ -1617,7 +1617,10 @@
|
||||
},
|
||||
"minDomains": 5,
|
||||
"nodeAffinityPolicy": "nodeAffinityPolicyValue",
|
||||
"nodeTaintsPolicy": "nodeTaintsPolicyValue"
|
||||
"nodeTaintsPolicy": "nodeTaintsPolicyValue",
|
||||
"matchLabelKeys": [
|
||||
"matchLabelKeysValue"
|
||||
]
|
||||
}
|
||||
],
|
||||
"setHostnameAsFQDN": true,
|
||||
|
Binary file not shown.
@ -856,6 +856,8 @@ spec:
|
||||
- valuesValue
|
||||
matchLabels:
|
||||
matchLabelsKey: matchLabelsValue
|
||||
matchLabelKeys:
|
||||
- matchLabelKeysValue
|
||||
maxSkew: 1
|
||||
minDomains: 5
|
||||
nodeAffinityPolicy: nodeAffinityPolicyValue
|
||||
|
@ -1617,7 +1617,10 @@
|
||||
},
|
||||
"minDomains": 5,
|
||||
"nodeAffinityPolicy": "nodeAffinityPolicyValue",
|
||||
"nodeTaintsPolicy": "nodeTaintsPolicyValue"
|
||||
"nodeTaintsPolicy": "nodeTaintsPolicyValue",
|
||||
"matchLabelKeys": [
|
||||
"matchLabelKeysValue"
|
||||
]
|
||||
}
|
||||
],
|
||||
"setHostnameAsFQDN": true,
|
||||
|
Binary file not shown.
@ -852,6 +852,8 @@ spec:
|
||||
- valuesValue
|
||||
matchLabels:
|
||||
matchLabelsKey: matchLabelsValue
|
||||
matchLabelKeys:
|
||||
- matchLabelKeysValue
|
||||
maxSkew: 1
|
||||
minDomains: 5
|
||||
nodeAffinityPolicy: nodeAffinityPolicyValue
|
||||
|
@ -1616,7 +1616,10 @@
|
||||
},
|
||||
"minDomains": 5,
|
||||
"nodeAffinityPolicy": "nodeAffinityPolicyValue",
|
||||
"nodeTaintsPolicy": "nodeTaintsPolicyValue"
|
||||
"nodeTaintsPolicy": "nodeTaintsPolicyValue",
|
||||
"matchLabelKeys": [
|
||||
"matchLabelKeysValue"
|
||||
]
|
||||
}
|
||||
],
|
||||
"setHostnameAsFQDN": true,
|
||||
|
Binary file not shown.
@ -846,6 +846,8 @@ spec:
|
||||
- valuesValue
|
||||
matchLabels:
|
||||
matchLabelsKey: matchLabelsValue
|
||||
matchLabelKeys:
|
||||
- matchLabelKeysValue
|
||||
maxSkew: 1
|
||||
minDomains: 5
|
||||
nodeAffinityPolicy: nodeAffinityPolicyValue
|
||||
|
@ -1617,7 +1617,10 @@
|
||||
},
|
||||
"minDomains": 5,
|
||||
"nodeAffinityPolicy": "nodeAffinityPolicyValue",
|
||||
"nodeTaintsPolicy": "nodeTaintsPolicyValue"
|
||||
"nodeTaintsPolicy": "nodeTaintsPolicyValue",
|
||||
"matchLabelKeys": [
|
||||
"matchLabelKeysValue"
|
||||
]
|
||||
}
|
||||
],
|
||||
"setHostnameAsFQDN": true,
|
||||
|
Binary file not shown.
@ -854,6 +854,8 @@ spec:
|
||||
- valuesValue
|
||||
matchLabels:
|
||||
matchLabelsKey: matchLabelsValue
|
||||
matchLabelKeys:
|
||||
- matchLabelKeysValue
|
||||
maxSkew: 1
|
||||
minDomains: 5
|
||||
nodeAffinityPolicy: nodeAffinityPolicyValue
|
||||
|
@ -1618,7 +1618,10 @@
|
||||
},
|
||||
"minDomains": 5,
|
||||
"nodeAffinityPolicy": "nodeAffinityPolicyValue",
|
||||
"nodeTaintsPolicy": "nodeTaintsPolicyValue"
|
||||
"nodeTaintsPolicy": "nodeTaintsPolicyValue",
|
||||
"matchLabelKeys": [
|
||||
"matchLabelKeysValue"
|
||||
]
|
||||
}
|
||||
],
|
||||
"setHostnameAsFQDN": true,
|
||||
|
Binary file not shown.
@ -846,6 +846,8 @@ spec:
|
||||
- valuesValue
|
||||
matchLabels:
|
||||
matchLabelsKey: matchLabelsValue
|
||||
matchLabelKeys:
|
||||
- matchLabelKeysValue
|
||||
maxSkew: 1
|
||||
minDomains: 5
|
||||
nodeAffinityPolicy: nodeAffinityPolicyValue
|
||||
|
@ -1617,7 +1617,10 @@
|
||||
},
|
||||
"minDomains": 5,
|
||||
"nodeAffinityPolicy": "nodeAffinityPolicyValue",
|
||||
"nodeTaintsPolicy": "nodeTaintsPolicyValue"
|
||||
"nodeTaintsPolicy": "nodeTaintsPolicyValue",
|
||||
"matchLabelKeys": [
|
||||
"matchLabelKeysValue"
|
||||
]
|
||||
}
|
||||
],
|
||||
"setHostnameAsFQDN": true,
|
||||
|
Binary file not shown.
@ -852,6 +852,8 @@ spec:
|
||||
- valuesValue
|
||||
matchLabels:
|
||||
matchLabelsKey: matchLabelsValue
|
||||
matchLabelKeys:
|
||||
- matchLabelKeysValue
|
||||
maxSkew: 1
|
||||
minDomains: 5
|
||||
nodeAffinityPolicy: nodeAffinityPolicyValue
|
||||
|
@ -1670,7 +1670,10 @@
|
||||
},
|
||||
"minDomains": 5,
|
||||
"nodeAffinityPolicy": "nodeAffinityPolicyValue",
|
||||
"nodeTaintsPolicy": "nodeTaintsPolicyValue"
|
||||
"nodeTaintsPolicy": "nodeTaintsPolicyValue",
|
||||
"matchLabelKeys": [
|
||||
"matchLabelKeysValue"
|
||||
]
|
||||
}
|
||||
],
|
||||
"setHostnameAsFQDN": true,
|
||||
|
Binary file not shown.
@ -887,6 +887,8 @@ spec:
|
||||
- valuesValue
|
||||
matchLabels:
|
||||
matchLabelsKey: matchLabelsValue
|
||||
matchLabelKeys:
|
||||
- matchLabelKeysValue
|
||||
maxSkew: 1
|
||||
minDomains: 5
|
||||
nodeAffinityPolicy: nodeAffinityPolicyValue
|
||||
|
@ -1621,7 +1621,10 @@
|
||||
},
|
||||
"minDomains": 5,
|
||||
"nodeAffinityPolicy": "nodeAffinityPolicyValue",
|
||||
"nodeTaintsPolicy": "nodeTaintsPolicyValue"
|
||||
"nodeTaintsPolicy": "nodeTaintsPolicyValue",
|
||||
"matchLabelKeys": [
|
||||
"matchLabelKeysValue"
|
||||
]
|
||||
}
|
||||
],
|
||||
"setHostnameAsFQDN": true,
|
||||
|
BIN
staging/src/k8s.io/api/testdata/HEAD/batch.v1.Job.pb
vendored
BIN
staging/src/k8s.io/api/testdata/HEAD/batch.v1.Job.pb
vendored
Binary file not shown.
@ -851,6 +851,8 @@ spec:
|
||||
- valuesValue
|
||||
matchLabels:
|
||||
matchLabelsKey: matchLabelsValue
|
||||
matchLabelKeys:
|
||||
- matchLabelKeysValue
|
||||
maxSkew: 1
|
||||
minDomains: 5
|
||||
nodeAffinityPolicy: nodeAffinityPolicyValue
|
||||
|
@ -1670,7 +1670,10 @@
|
||||
},
|
||||
"minDomains": 5,
|
||||
"nodeAffinityPolicy": "nodeAffinityPolicyValue",
|
||||
"nodeTaintsPolicy": "nodeTaintsPolicyValue"
|
||||
"nodeTaintsPolicy": "nodeTaintsPolicyValue",
|
||||
"matchLabelKeys": [
|
||||
"matchLabelKeysValue"
|
||||
]
|
||||
}
|
||||
],
|
||||
"setHostnameAsFQDN": true,
|
||||
|
Binary file not shown.
@ -887,6 +887,8 @@ spec:
|
||||
- valuesValue
|
||||
matchLabels:
|
||||
matchLabelsKey: matchLabelsValue
|
||||
matchLabelKeys:
|
||||
- matchLabelKeysValue
|
||||
maxSkew: 1
|
||||
minDomains: 5
|
||||
nodeAffinityPolicy: nodeAffinityPolicyValue
|
||||
|
@ -1664,7 +1664,10 @@
|
||||
},
|
||||
"minDomains": 5,
|
||||
"nodeAffinityPolicy": "nodeAffinityPolicyValue",
|
||||
"nodeTaintsPolicy": "nodeTaintsPolicyValue"
|
||||
"nodeTaintsPolicy": "nodeTaintsPolicyValue",
|
||||
"matchLabelKeys": [
|
||||
"matchLabelKeysValue"
|
||||
]
|
||||
}
|
||||
],
|
||||
"setHostnameAsFQDN": true,
|
||||
|
Binary file not shown.
@ -884,6 +884,8 @@ template:
|
||||
- valuesValue
|
||||
matchLabels:
|
||||
matchLabelsKey: matchLabelsValue
|
||||
matchLabelKeys:
|
||||
- matchLabelKeysValue
|
||||
maxSkew: 1
|
||||
minDomains: 5
|
||||
nodeAffinityPolicy: nodeAffinityPolicyValue
|
||||
|
@ -1558,7 +1558,10 @@
|
||||
},
|
||||
"minDomains": 5,
|
||||
"nodeAffinityPolicy": "nodeAffinityPolicyValue",
|
||||
"nodeTaintsPolicy": "nodeTaintsPolicyValue"
|
||||
"nodeTaintsPolicy": "nodeTaintsPolicyValue",
|
||||
"matchLabelKeys": [
|
||||
"matchLabelKeysValue"
|
||||
]
|
||||
}
|
||||
],
|
||||
"setHostnameAsFQDN": true,
|
||||
|
BIN
staging/src/k8s.io/api/testdata/HEAD/core.v1.Pod.pb
vendored
BIN
staging/src/k8s.io/api/testdata/HEAD/core.v1.Pod.pb
vendored
Binary file not shown.
@ -802,6 +802,8 @@ spec:
|
||||
- valuesValue
|
||||
matchLabels:
|
||||
matchLabelsKey: matchLabelsValue
|
||||
matchLabelKeys:
|
||||
- matchLabelKeysValue
|
||||
maxSkew: 1
|
||||
minDomains: 5
|
||||
nodeAffinityPolicy: nodeAffinityPolicyValue
|
||||
|
@ -1601,7 +1601,10 @@
|
||||
},
|
||||
"minDomains": 5,
|
||||
"nodeAffinityPolicy": "nodeAffinityPolicyValue",
|
||||
"nodeTaintsPolicy": "nodeTaintsPolicyValue"
|
||||
"nodeTaintsPolicy": "nodeTaintsPolicyValue",
|
||||
"matchLabelKeys": [
|
||||
"matchLabelKeysValue"
|
||||
]
|
||||
}
|
||||
],
|
||||
"setHostnameAsFQDN": true,
|
||||
|
Binary file not shown.
@ -835,6 +835,8 @@ template:
|
||||
- valuesValue
|
||||
matchLabels:
|
||||
matchLabelsKey: matchLabelsValue
|
||||
matchLabelKeys:
|
||||
- matchLabelKeysValue
|
||||
maxSkew: 1
|
||||
minDomains: 5
|
||||
nodeAffinityPolicy: nodeAffinityPolicyValue
|
||||
|
@ -1607,7 +1607,10 @@
|
||||
},
|
||||
"minDomains": 5,
|
||||
"nodeAffinityPolicy": "nodeAffinityPolicyValue",
|
||||
"nodeTaintsPolicy": "nodeTaintsPolicyValue"
|
||||
"nodeTaintsPolicy": "nodeTaintsPolicyValue",
|
||||
"matchLabelKeys": [
|
||||
"matchLabelKeysValue"
|
||||
]
|
||||
}
|
||||
],
|
||||
"setHostnameAsFQDN": true,
|
||||
|
Binary file not shown.
@ -840,6 +840,8 @@ spec:
|
||||
- valuesValue
|
||||
matchLabels:
|
||||
matchLabelsKey: matchLabelsValue
|
||||
matchLabelKeys:
|
||||
- matchLabelKeysValue
|
||||
maxSkew: 1
|
||||
minDomains: 5
|
||||
nodeAffinityPolicy: nodeAffinityPolicyValue
|
||||
|
@ -1616,7 +1616,10 @@
|
||||
},
|
||||
"minDomains": 5,
|
||||
"nodeAffinityPolicy": "nodeAffinityPolicyValue",
|
||||
"nodeTaintsPolicy": "nodeTaintsPolicyValue"
|
||||
"nodeTaintsPolicy": "nodeTaintsPolicyValue",
|
||||
"matchLabelKeys": [
|
||||
"matchLabelKeysValue"
|
||||
]
|
||||
}
|
||||
],
|
||||
"setHostnameAsFQDN": true,
|
||||
|
Binary file not shown.
@ -846,6 +846,8 @@ spec:
|
||||
- valuesValue
|
||||
matchLabels:
|
||||
matchLabelsKey: matchLabelsValue
|
||||
matchLabelKeys:
|
||||
- matchLabelKeysValue
|
||||
maxSkew: 1
|
||||
minDomains: 5
|
||||
nodeAffinityPolicy: nodeAffinityPolicyValue
|
||||
|
@ -1617,7 +1617,10 @@
|
||||
},
|
||||
"minDomains": 5,
|
||||
"nodeAffinityPolicy": "nodeAffinityPolicyValue",
|
||||
"nodeTaintsPolicy": "nodeTaintsPolicyValue"
|
||||
"nodeTaintsPolicy": "nodeTaintsPolicyValue",
|
||||
"matchLabelKeys": [
|
||||
"matchLabelKeysValue"
|
||||
]
|
||||
}
|
||||
],
|
||||
"setHostnameAsFQDN": true,
|
||||
|
Binary file not shown.
@ -856,6 +856,8 @@ spec:
|
||||
- valuesValue
|
||||
matchLabels:
|
||||
matchLabelsKey: matchLabelsValue
|
||||
matchLabelKeys:
|
||||
- matchLabelKeysValue
|
||||
maxSkew: 1
|
||||
minDomains: 5
|
||||
nodeAffinityPolicy: nodeAffinityPolicyValue
|
||||
|
@ -1618,7 +1618,10 @@
|
||||
},
|
||||
"minDomains": 5,
|
||||
"nodeAffinityPolicy": "nodeAffinityPolicyValue",
|
||||
"nodeTaintsPolicy": "nodeTaintsPolicyValue"
|
||||
"nodeTaintsPolicy": "nodeTaintsPolicyValue",
|
||||
"matchLabelKeys": [
|
||||
"matchLabelKeysValue"
|
||||
]
|
||||
}
|
||||
],
|
||||
"setHostnameAsFQDN": true,
|
||||
|
Binary file not shown.
@ -846,6 +846,8 @@ spec:
|
||||
- valuesValue
|
||||
matchLabels:
|
||||
matchLabelsKey: matchLabelsValue
|
||||
matchLabelKeys:
|
||||
- matchLabelKeysValue
|
||||
maxSkew: 1
|
||||
minDomains: 5
|
||||
nodeAffinityPolicy: nodeAffinityPolicyValue
|
||||
|
@ -33,6 +33,7 @@ type TopologySpreadConstraintApplyConfiguration struct {
|
||||
MinDomains *int32 `json:"minDomains,omitempty"`
|
||||
NodeAffinityPolicy *v1.NodeInclusionPolicy `json:"nodeAffinityPolicy,omitempty"`
|
||||
NodeTaintsPolicy *v1.NodeInclusionPolicy `json:"nodeTaintsPolicy,omitempty"`
|
||||
MatchLabelKeys []string `json:"matchLabelKeys,omitempty"`
|
||||
}
|
||||
|
||||
// TopologySpreadConstraintApplyConfiguration constructs an declarative configuration of the TopologySpreadConstraint type for use with
|
||||
@ -96,3 +97,13 @@ func (b *TopologySpreadConstraintApplyConfiguration) WithNodeTaintsPolicy(value
|
||||
b.NodeTaintsPolicy = &value
|
||||
return b
|
||||
}
|
||||
|
||||
// WithMatchLabelKeys adds the given value to the MatchLabelKeys field in the declarative configuration
|
||||
// and returns the receiver, so that objects can be build by chaining "With" function invocations.
|
||||
// If called multiple times, values provided by each call will be appended to the MatchLabelKeys field.
|
||||
func (b *TopologySpreadConstraintApplyConfiguration) WithMatchLabelKeys(values ...string) *TopologySpreadConstraintApplyConfiguration {
|
||||
for i := range values {
|
||||
b.MatchLabelKeys = append(b.MatchLabelKeys, values[i])
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
@ -6858,6 +6858,12 @@ var schemaYAML = typed.YAMLObject(`types:
|
||||
- name: labelSelector
|
||||
type:
|
||||
namedType: io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector
|
||||
- name: matchLabelKeys
|
||||
type:
|
||||
list:
|
||||
elementType:
|
||||
scalar: string
|
||||
elementRelationship: atomic
|
||||
- name: maxSkew
|
||||
type:
|
||||
scalar: numeric
|
||||
|
@ -1076,12 +1076,13 @@ func TestPodTopologySpreadFilter(t *testing.T) {
|
||||
candidateNodes []string // nodes expected to schedule onto
|
||||
enableMinDomains bool
|
||||
enableNodeInclustionPolicy bool
|
||||
enableMatchLabelKeys bool
|
||||
}{
|
||||
// note: naming starts at index 0
|
||||
{
|
||||
name: "place pod on a 1/1/0/1 cluster with MaxSkew=1, node-2 is the only fit",
|
||||
incomingPod: st.MakePod().Name("p").Label("foo", "").Container(pause).
|
||||
SpreadConstraint(1, "node", hardSpread, st.MakeLabelSelector().Exists("foo").Obj(), nil, nil, nil).
|
||||
SpreadConstraint(1, "node", hardSpread, st.MakeLabelSelector().Exists("foo").Obj(), nil, nil, nil, nil).
|
||||
Obj(),
|
||||
existingPods: []*v1.Pod{
|
||||
st.MakePod().Name("p0").Node("node-0").Label("foo", "").Container(pause).Obj(),
|
||||
@ -1095,7 +1096,7 @@ func TestPodTopologySpreadFilter(t *testing.T) {
|
||||
{
|
||||
name: "place pod on a 2/0/0/1 cluster with MaxSkew=2, node-{1,2,3} are good fits",
|
||||
incomingPod: st.MakePod().Name("p").Label("foo", "").Container(pause).
|
||||
SpreadConstraint(2, "node", hardSpread, st.MakeLabelSelector().Exists("foo").Obj(), nil, nil, nil).
|
||||
SpreadConstraint(2, "node", hardSpread, st.MakeLabelSelector().Exists("foo").Obj(), nil, nil, nil, nil).
|
||||
Obj(),
|
||||
existingPods: []*v1.Pod{
|
||||
st.MakePod().Name("p0a").Node("node-0").Label("foo", "").Container(pause).Obj(),
|
||||
@ -1110,7 +1111,7 @@ func TestPodTopologySpreadFilter(t *testing.T) {
|
||||
name: "pod is required to be placed on zone0, so only node-1 fits",
|
||||
incomingPod: st.MakePod().Name("p").Label("foo", "").Container(pause).
|
||||
NodeAffinityIn("zone", []string{"zone-0"}).
|
||||
SpreadConstraint(1, "node", hardSpread, st.MakeLabelSelector().Exists("foo").Obj(), nil, nil, nil).
|
||||
SpreadConstraint(1, "node", hardSpread, st.MakeLabelSelector().Exists("foo").Obj(), nil, nil, nil, nil).
|
||||
Obj(),
|
||||
existingPods: []*v1.Pod{
|
||||
st.MakePod().Name("p0").Node("node-0").Label("foo", "").Container(pause).Obj(),
|
||||
@ -1123,8 +1124,8 @@ func TestPodTopologySpreadFilter(t *testing.T) {
|
||||
{
|
||||
name: "two constraints: pod can only be placed to zone-1/node-2",
|
||||
incomingPod: st.MakePod().Name("p").Label("foo", "").Container(pause).
|
||||
SpreadConstraint(1, "zone", hardSpread, st.MakeLabelSelector().Exists("foo").Obj(), nil, nil, nil).
|
||||
SpreadConstraint(1, "node", hardSpread, st.MakeLabelSelector().Exists("foo").Obj(), nil, nil, nil).
|
||||
SpreadConstraint(1, "zone", hardSpread, st.MakeLabelSelector().Exists("foo").Obj(), nil, nil, nil, nil).
|
||||
SpreadConstraint(1, "node", hardSpread, st.MakeLabelSelector().Exists("foo").Obj(), nil, nil, nil, nil).
|
||||
Obj(),
|
||||
existingPods: []*v1.Pod{
|
||||
st.MakePod().Name("p0").Node("node-0").Label("foo", "").Container(pause).Obj(),
|
||||
@ -1140,8 +1141,8 @@ func TestPodTopologySpreadFilter(t *testing.T) {
|
||||
name: "pod cannot be placed onto any node",
|
||||
incomingPod: st.MakePod().Name("p").Label("foo", "").Container(pause).
|
||||
NodeAffinityNotIn("node", []string{"node-0"}). // mock a 3-node cluster
|
||||
SpreadConstraint(1, "zone", hardSpread, st.MakeLabelSelector().Exists("foo").Obj(), nil, nil, nil).
|
||||
SpreadConstraint(1, "node", hardSpread, st.MakeLabelSelector().Exists("foo").Obj(), nil, nil, nil).
|
||||
SpreadConstraint(1, "zone", hardSpread, st.MakeLabelSelector().Exists("foo").Obj(), nil, nil, nil, nil).
|
||||
SpreadConstraint(1, "node", hardSpread, st.MakeLabelSelector().Exists("foo").Obj(), nil, nil, nil, nil).
|
||||
Obj(),
|
||||
existingPods: []*v1.Pod{
|
||||
st.MakePod().Name("p1a").Node("node-1").Label("foo", "").Container(pause).Obj(),
|
||||
@ -1157,8 +1158,8 @@ func TestPodTopologySpreadFilter(t *testing.T) {
|
||||
name: "high priority pod can preempt others",
|
||||
incomingPod: st.MakePod().Name("p").Label("foo", "").Container(pause).Priority(100).
|
||||
NodeAffinityNotIn("node", []string{"node-0"}). // mock a 3-node cluster
|
||||
SpreadConstraint(1, "zone", hardSpread, st.MakeLabelSelector().Exists("foo").Obj(), nil, nil, nil).
|
||||
SpreadConstraint(1, "node", hardSpread, st.MakeLabelSelector().Exists("foo").Obj(), nil, nil, nil).
|
||||
SpreadConstraint(1, "zone", hardSpread, st.MakeLabelSelector().Exists("foo").Obj(), nil, nil, nil, nil).
|
||||
SpreadConstraint(1, "node", hardSpread, st.MakeLabelSelector().Exists("foo").Obj(), nil, nil, nil, nil).
|
||||
Obj(),
|
||||
existingPods: []*v1.Pod{
|
||||
st.MakePod().ZeroTerminationGracePeriod().Name("p1a").Node("node-1").Label("foo", "").Container(pause).Obj(),
|
||||
@ -1183,6 +1184,7 @@ func TestPodTopologySpreadFilter(t *testing.T) {
|
||||
pointer.Int32(4), // larger than the number of domains (= 3)
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
).
|
||||
Obj(),
|
||||
existingPods: []*v1.Pod{
|
||||
@ -1209,6 +1211,7 @@ func TestPodTopologySpreadFilter(t *testing.T) {
|
||||
pointer.Int32(2), // smaller than the number of domains (= 3)
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
).
|
||||
Obj(),
|
||||
existingPods: []*v1.Pod{
|
||||
@ -1234,6 +1237,7 @@ func TestPodTopologySpreadFilter(t *testing.T) {
|
||||
pointer.Int32(3), // larger than the number of domains(2)
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
).Obj(),
|
||||
existingPods: []*v1.Pod{
|
||||
st.MakePod().Name("p1a").Node("node-0").Label("foo", "").Container(pause).Obj(),
|
||||
@ -1256,6 +1260,7 @@ func TestPodTopologySpreadFilter(t *testing.T) {
|
||||
pointer.Int32(1), // smaller than the number of domains(2)
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
).Obj(),
|
||||
existingPods: []*v1.Pod{
|
||||
st.MakePod().Name("p1a").Node("node-1").Label("foo", "").Container(pause).Obj(),
|
||||
@ -1271,7 +1276,7 @@ func TestPodTopologySpreadFilter(t *testing.T) {
|
||||
name: "NodeAffinityPolicy honored with labelSelectors, pods spread across zone as 2/1",
|
||||
incomingPod: st.MakePod().Name("p").Label("foo", "").Container(pause).
|
||||
NodeSelector(map[string]string{"foo": ""}).
|
||||
SpreadConstraint(1, "zone", v1.DoNotSchedule, st.MakeLabelSelector().Exists("foo").Obj(), nil, nil, nil).
|
||||
SpreadConstraint(1, "zone", v1.DoNotSchedule, st.MakeLabelSelector().Exists("foo").Obj(), nil, nil, nil, nil).
|
||||
Obj(),
|
||||
existingPods: []*v1.Pod{
|
||||
st.MakePod().Name("p1a").Node("node-1").Label("foo", "").Container(pause).Obj(),
|
||||
@ -1293,7 +1298,7 @@ func TestPodTopologySpreadFilter(t *testing.T) {
|
||||
name: "NodeAffinityPolicy ignored with nodeAffinity, pods spread across zone as 1/~2~",
|
||||
incomingPod: st.MakePod().Name("p").Label("foo", "").Container(pause).
|
||||
NodeAffinityIn("foo", []string{""}).
|
||||
SpreadConstraint(1, "zone", v1.DoNotSchedule, st.MakeLabelSelector().Exists("foo").Obj(), nil, &ignorePolicy, nil).
|
||||
SpreadConstraint(1, "zone", v1.DoNotSchedule, st.MakeLabelSelector().Exists("foo").Obj(), nil, &ignorePolicy, nil, nil).
|
||||
Obj(),
|
||||
existingPods: []*v1.Pod{
|
||||
st.MakePod().Name("p1a").Node("node-1").Label("foo", "").Container(pause).Obj(),
|
||||
@ -1313,7 +1318,7 @@ func TestPodTopologySpreadFilter(t *testing.T) {
|
||||
{
|
||||
name: "NodeTaintsPolicy honored, pods spread across zone as 2/1",
|
||||
incomingPod: st.MakePod().Name("p").Label("foo", "").Container(pause).
|
||||
SpreadConstraint(1, "zone", v1.DoNotSchedule, st.MakeLabelSelector().Exists("foo").Obj(), nil, nil, &honorPolicy).
|
||||
SpreadConstraint(1, "zone", v1.DoNotSchedule, st.MakeLabelSelector().Exists("foo").Obj(), nil, nil, &honorPolicy, nil).
|
||||
Obj(),
|
||||
existingPods: []*v1.Pod{
|
||||
st.MakePod().Name("p1a").Node("node-1").Label("foo", "").Container(pause).Obj(),
|
||||
@ -1334,7 +1339,7 @@ func TestPodTopologySpreadFilter(t *testing.T) {
|
||||
{
|
||||
name: "NodeTaintsPolicy ignored, pods spread across zone as 2/2",
|
||||
incomingPod: st.MakePod().Name("p").Label("foo", "").Container(pause).
|
||||
SpreadConstraint(1, "zone", v1.DoNotSchedule, st.MakeLabelSelector().Exists("foo").Obj(), nil, nil, nil).
|
||||
SpreadConstraint(1, "zone", v1.DoNotSchedule, st.MakeLabelSelector().Exists("foo").Obj(), nil, nil, nil, nil).
|
||||
Obj(),
|
||||
existingPods: []*v1.Pod{
|
||||
st.MakePod().Name("p1a").Node("node-1").Label("foo", "").Container(pause).Obj(),
|
||||
@ -1359,8 +1364,8 @@ func TestPodTopologySpreadFilter(t *testing.T) {
|
||||
name: "two node inclusion Constraints, zone: honor/ignore, node: honor/ignore",
|
||||
incomingPod: st.MakePod().Name("p").Label("foo", "").Container(pause).
|
||||
NodeSelector(map[string]string{"foo": ""}).
|
||||
SpreadConstraint(1, "zone", v1.DoNotSchedule, st.MakeLabelSelector().Exists("foo").Obj(), nil, nil, nil).
|
||||
SpreadConstraint(1, "node", v1.DoNotSchedule, st.MakeLabelSelector().Exists("foo").Obj(), nil, nil, nil).
|
||||
SpreadConstraint(1, "zone", v1.DoNotSchedule, st.MakeLabelSelector().Exists("foo").Obj(), nil, nil, nil, nil).
|
||||
SpreadConstraint(1, "node", v1.DoNotSchedule, st.MakeLabelSelector().Exists("foo").Obj(), nil, nil, nil, nil).
|
||||
Obj(),
|
||||
existingPods: []*v1.Pod{
|
||||
st.MakePod().Name("p1a").Node("node-1").Label("foo", "").Container(pause).Obj(),
|
||||
@ -1385,8 +1390,8 @@ func TestPodTopologySpreadFilter(t *testing.T) {
|
||||
name: "feature gate disabled, two node inclusion Constraints, zone: honor/ignore, node: honor/ignore",
|
||||
incomingPod: st.MakePod().Name("p").Label("foo", "").Container(pause).
|
||||
NodeSelector(map[string]string{"foo": ""}).
|
||||
SpreadConstraint(1, "zone", v1.DoNotSchedule, st.MakeLabelSelector().Exists("foo").Obj(), nil, nil, nil).
|
||||
SpreadConstraint(1, "node", v1.DoNotSchedule, st.MakeLabelSelector().Exists("foo").Obj(), nil, nil, nil).
|
||||
SpreadConstraint(1, "zone", v1.DoNotSchedule, st.MakeLabelSelector().Exists("foo").Obj(), nil, nil, nil, nil).
|
||||
SpreadConstraint(1, "node", v1.DoNotSchedule, st.MakeLabelSelector().Exists("foo").Obj(), nil, nil, nil, nil).
|
||||
Obj(),
|
||||
existingPods: []*v1.Pod{
|
||||
st.MakePod().Name("p1a").Node("node-1").Label("foo", "").Container(pause).Obj(),
|
||||
@ -1411,8 +1416,8 @@ func TestPodTopologySpreadFilter(t *testing.T) {
|
||||
name: "two node inclusion Constraints, zone: ignore/ignore, node: honor/honor",
|
||||
incomingPod: st.MakePod().Name("p").Label("foo", "").Container(pause).
|
||||
NodeSelector(map[string]string{"foo": ""}).
|
||||
SpreadConstraint(1, "zone", v1.DoNotSchedule, st.MakeLabelSelector().Exists("foo").Obj(), nil, &ignorePolicy, nil).
|
||||
SpreadConstraint(1, "node", v1.DoNotSchedule, st.MakeLabelSelector().Exists("foo").Obj(), nil, nil, &honorPolicy).
|
||||
SpreadConstraint(1, "zone", v1.DoNotSchedule, st.MakeLabelSelector().Exists("foo").Obj(), nil, &ignorePolicy, nil, nil).
|
||||
SpreadConstraint(1, "node", v1.DoNotSchedule, st.MakeLabelSelector().Exists("foo").Obj(), nil, nil, &honorPolicy, nil).
|
||||
Obj(),
|
||||
existingPods: []*v1.Pod{
|
||||
st.MakePod().Name("p1a").Node("node-1").Label("foo", "").Container(pause).Obj(),
|
||||
@ -1430,11 +1435,57 @@ func TestPodTopologySpreadFilter(t *testing.T) {
|
||||
candidateNodes: []string{"node-1", "node-4"},
|
||||
enableNodeInclustionPolicy: true,
|
||||
},
|
||||
{
|
||||
name: "matchLabelKeys ignored when feature gate disabled, pods spread across zone as 2/1",
|
||||
incomingPod: st.MakePod().Name("p").Label("foo", "").Label("bar", "").Container(pause).
|
||||
SpreadConstraint(1, "zone", v1.DoNotSchedule, st.MakeLabelSelector().Exists("foo").Obj(), nil, nil, nil, []string{"bar"}).
|
||||
Obj(),
|
||||
existingPods: []*v1.Pod{
|
||||
st.MakePod().Name("p1a").Node("node-0").Label("foo", "").Container(pause).Obj(),
|
||||
st.MakePod().Name("p2a").Node("node-1").Label("foo", "").Container(pause).Obj(),
|
||||
st.MakePod().Name("p3a").Node("node-2").Label("foo", "").Label("bar", "").Container(pause).Obj(),
|
||||
},
|
||||
fits: true,
|
||||
nodes: defaultNodes,
|
||||
candidateNodes: []string{"node-2", "node-3"},
|
||||
enableMatchLabelKeys: false,
|
||||
},
|
||||
{
|
||||
name: "matchLabelKeys ANDed with LabelSelector when LabelSelector isn't empty, pods spread across zone as 0/1",
|
||||
incomingPod: st.MakePod().Name("p").Label("foo", "").Label("bar", "").Container(pause).
|
||||
SpreadConstraint(1, "zone", v1.DoNotSchedule, st.MakeLabelSelector().Exists("foo").Obj(), nil, nil, nil, []string{"bar"}).
|
||||
Obj(),
|
||||
existingPods: []*v1.Pod{
|
||||
st.MakePod().Name("p1a").Node("node-0").Label("foo", "").Container(pause).Obj(),
|
||||
st.MakePod().Name("p2a").Node("node-1").Label("foo", "").Container(pause).Obj(),
|
||||
st.MakePod().Name("p3a").Node("node-2").Label("foo", "").Label("bar", "").Container(pause).Obj(),
|
||||
},
|
||||
fits: true,
|
||||
nodes: defaultNodes,
|
||||
candidateNodes: []string{"node-0", "node-1"},
|
||||
enableMatchLabelKeys: true,
|
||||
},
|
||||
{
|
||||
name: "matchLabelKeys ANDed with LabelSelector when LabelSelector is empty, pods spread across zone as 2/1",
|
||||
incomingPod: st.MakePod().Name("p").Label("foo", "").Container(pause).
|
||||
SpreadConstraint(1, "zone", v1.DoNotSchedule, st.MakeLabelSelector().Obj(), nil, nil, nil, []string{"foo"}).
|
||||
Obj(),
|
||||
existingPods: []*v1.Pod{
|
||||
st.MakePod().Name("p1a").Node("node-0").Label("foo", "").Container(pause).Obj(),
|
||||
st.MakePod().Name("p2a").Node("node-1").Label("foo", "").Container(pause).Obj(),
|
||||
st.MakePod().Name("p3a").Node("node-2").Label("foo", "").Container(pause).Obj(),
|
||||
},
|
||||
fits: true,
|
||||
nodes: defaultNodes,
|
||||
candidateNodes: []string{"node-2", "node-3"},
|
||||
enableMatchLabelKeys: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.MinDomainsInPodTopologySpread, tt.enableMinDomains)()
|
||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.NodeInclusionPolicyInPodTopologySpread, tt.enableNodeInclustionPolicy)()
|
||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.MatchLabelKeysInPodTopologySpread, tt.enableMatchLabelKeys)()
|
||||
|
||||
testCtx := initTest(t, "pts-predicate")
|
||||
cs := testCtx.ClientSet
|
||||
|
@ -449,13 +449,14 @@ func TestPodTopologySpreadScoring(t *testing.T) {
|
||||
nodes []*v1.Node
|
||||
want []string // nodes expected to schedule onto
|
||||
enableNodeInclustionPolicy bool
|
||||
enableMatchLabelKeys bool
|
||||
}{
|
||||
// note: naming starts at index 0
|
||||
// the symbol ~X~ means that node is infeasible
|
||||
{
|
||||
name: "place pod on a ~0~/1/2/3 cluster with MaxSkew=1, node-1 is the preferred fit",
|
||||
incomingPod: st.MakePod().Name("p").Label("foo", "").Container(pause).
|
||||
SpreadConstraint(1, "node", softSpread, st.MakeLabelSelector().Exists("foo").Obj(), nil, nil, nil).
|
||||
SpreadConstraint(1, "node", softSpread, st.MakeLabelSelector().Exists("foo").Obj(), nil, nil, nil, nil).
|
||||
Obj(),
|
||||
existingPods: []*v1.Pod{
|
||||
st.MakePod().Name("p1").Node("node-1").Label("foo", "").Container(pause).Obj(),
|
||||
@ -472,8 +473,8 @@ func TestPodTopologySpreadScoring(t *testing.T) {
|
||||
{
|
||||
name: "combined with hardSpread constraint on a ~4~/0/1/2 cluster",
|
||||
incomingPod: st.MakePod().Name("p").Label("foo", "").Container(pause).
|
||||
SpreadConstraint(1, "node", softSpread, st.MakeLabelSelector().Exists("foo").Obj(), nil, nil, nil).
|
||||
SpreadConstraint(1, "zone", hardSpread, st.MakeLabelSelector().Exists("foo").Obj(), nil, nil, nil).
|
||||
SpreadConstraint(1, "node", softSpread, st.MakeLabelSelector().Exists("foo").Obj(), nil, nil, nil, nil).
|
||||
SpreadConstraint(1, "zone", hardSpread, st.MakeLabelSelector().Exists("foo").Obj(), nil, nil, nil, nil).
|
||||
Obj(),
|
||||
existingPods: []*v1.Pod{
|
||||
st.MakePod().Name("p0a").Node("node-0").Label("foo", "").Container(pause).Obj(),
|
||||
@ -495,8 +496,8 @@ func TestPodTopologySpreadScoring(t *testing.T) {
|
||||
name: "soft constraint with two node inclusion Constraints, zone: honor/ignore, node: honor/ignore",
|
||||
incomingPod: st.MakePod().Name("p").Label("foo", "").Container(pause).
|
||||
NodeSelector(map[string]string{"foo": ""}).
|
||||
SpreadConstraint(1, "zone", softSpread, st.MakeLabelSelector().Exists("foo").Obj(), nil, nil, nil).
|
||||
SpreadConstraint(1, "node", softSpread, st.MakeLabelSelector().Exists("foo").Obj(), nil, nil, nil).
|
||||
SpreadConstraint(1, "zone", softSpread, st.MakeLabelSelector().Exists("foo").Obj(), nil, nil, nil, nil).
|
||||
SpreadConstraint(1, "node", softSpread, st.MakeLabelSelector().Exists("foo").Obj(), nil, nil, nil, nil).
|
||||
Obj(),
|
||||
existingPods: []*v1.Pod{
|
||||
st.MakePod().Name("p1a").Node("node-1").Label("foo", "").Container(pause).Obj(),
|
||||
@ -521,8 +522,8 @@ func TestPodTopologySpreadScoring(t *testing.T) {
|
||||
name: "soft constraint with two node inclusion Constraints, zone: ignore/ignore, node: honor/honor",
|
||||
incomingPod: st.MakePod().Name("p").Label("foo", "").Container(pause).
|
||||
NodeSelector(map[string]string{"foo": ""}).
|
||||
SpreadConstraint(1, "zone", softSpread, st.MakeLabelSelector().Exists("foo").Obj(), nil, &ignorePolicy, nil).
|
||||
SpreadConstraint(1, "node", softSpread, st.MakeLabelSelector().Exists("foo").Obj(), nil, nil, &honorPolicy).
|
||||
SpreadConstraint(1, "zone", softSpread, st.MakeLabelSelector().Exists("foo").Obj(), nil, &ignorePolicy, nil, nil).
|
||||
SpreadConstraint(1, "node", softSpread, st.MakeLabelSelector().Exists("foo").Obj(), nil, nil, &honorPolicy, nil).
|
||||
Obj(),
|
||||
existingPods: []*v1.Pod{
|
||||
st.MakePod().Name("p1a").Node("node-1").Label("foo", "").Container(pause).Obj(),
|
||||
@ -540,10 +541,65 @@ func TestPodTopologySpreadScoring(t *testing.T) {
|
||||
want: []string{"node-3"},
|
||||
enableNodeInclustionPolicy: true,
|
||||
},
|
||||
{
|
||||
name: "matchLabelKeys ignored when feature gate disabled, node-1 is the preferred fit",
|
||||
incomingPod: st.MakePod().Name("p").Label("foo", "").Label("bar", "").Container(pause).
|
||||
SpreadConstraint(1, "node", softSpread, st.MakeLabelSelector().Exists("foo").Obj(), nil, nil, nil, []string{"bar"}).
|
||||
Obj(),
|
||||
existingPods: []*v1.Pod{
|
||||
st.MakePod().Name("p1").Node("node-1").Label("foo", "").Label("bar", "").Container(pause).Obj(),
|
||||
st.MakePod().Name("p2a").Node("node-2").Label("foo", "").Container(pause).Obj(),
|
||||
st.MakePod().Name("p2b").Node("node-2").Label("foo", "").Container(pause).Obj(),
|
||||
st.MakePod().Name("p3a").Node("node-3").Label("foo", "").Label("bar", "").Container(pause).Obj(),
|
||||
st.MakePod().Name("p3b").Node("node-3").Label("foo", "").Label("bar", "").Container(pause).Obj(),
|
||||
st.MakePod().Name("p3c").Node("node-3").Label("foo", "").Container(pause).Obj(),
|
||||
},
|
||||
fits: true,
|
||||
nodes: defaultNodes,
|
||||
want: []string{"node-1"},
|
||||
enableMatchLabelKeys: false,
|
||||
},
|
||||
{
|
||||
name: "matchLabelKeys ANDed with LabelSelector when LabelSelector isn't empty, node-2 is the preferred fit",
|
||||
incomingPod: st.MakePod().Name("p").Label("foo", "").Label("bar", "").Container(pause).
|
||||
SpreadConstraint(1, "node", softSpread, st.MakeLabelSelector().Exists("foo").Obj(), nil, nil, nil, []string{"bar"}).
|
||||
Obj(),
|
||||
existingPods: []*v1.Pod{
|
||||
st.MakePod().Name("p1").Node("node-1").Label("foo", "").Label("bar", "").Container(pause).Obj(),
|
||||
st.MakePod().Name("p2a").Node("node-2").Label("foo", "").Container(pause).Obj(),
|
||||
st.MakePod().Name("p2b").Node("node-2").Label("foo", "").Container(pause).Obj(),
|
||||
st.MakePod().Name("p3a").Node("node-3").Label("foo", "").Label("bar", "").Container(pause).Obj(),
|
||||
st.MakePod().Name("p3b").Node("node-3").Label("foo", "").Label("bar", "").Container(pause).Obj(),
|
||||
st.MakePod().Name("p3c").Node("node-3").Label("foo", "").Container(pause).Obj(),
|
||||
},
|
||||
fits: true,
|
||||
nodes: defaultNodes,
|
||||
want: []string{"node-2"},
|
||||
enableMatchLabelKeys: true,
|
||||
},
|
||||
{
|
||||
name: "matchLabelKeys ANDed with LabelSelector when LabelSelector is empty, node-1 is the preferred fit",
|
||||
incomingPod: st.MakePod().Name("p").Label("foo", "").Container(pause).
|
||||
SpreadConstraint(1, "node", softSpread, st.MakeLabelSelector().Obj(), nil, nil, nil, []string{"foo"}).
|
||||
Obj(),
|
||||
existingPods: []*v1.Pod{
|
||||
st.MakePod().Name("p1").Node("node-1").Label("foo", "").Container(pause).Obj(),
|
||||
st.MakePod().Name("p2a").Node("node-2").Label("foo", "").Container(pause).Obj(),
|
||||
st.MakePod().Name("p2b").Node("node-2").Label("foo", "").Container(pause).Obj(),
|
||||
st.MakePod().Name("p3a").Node("node-3").Label("foo", "").Container(pause).Obj(),
|
||||
st.MakePod().Name("p3b").Node("node-3").Label("foo", "").Container(pause).Obj(),
|
||||
st.MakePod().Name("p3c").Node("node-3").Label("foo", "").Container(pause).Obj(),
|
||||
},
|
||||
fits: true,
|
||||
nodes: defaultNodes,
|
||||
want: []string{"node-1"},
|
||||
enableMatchLabelKeys: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.NodeInclusionPolicyInPodTopologySpread, tt.enableNodeInclustionPolicy)()
|
||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.MatchLabelKeysInPodTopologySpread, tt.enableMatchLabelKeys)()
|
||||
|
||||
testCtx := initTestSchedulerForPriorityTest(t, podtopologyspread.Name)
|
||||
defer testutils.CleanupTest(t, testCtx)
|
||||
|
Loading…
Reference in New Issue
Block a user