parse pod's node affinity once in preFilter
This commit is contained in:
@@ -21,6 +21,7 @@ import (
|
||||
"fmt"
|
||||
|
||||
v1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/component-helpers/scheduling/corev1/nodeaffinity"
|
||||
"k8s.io/kubernetes/pkg/scheduler/apis/config"
|
||||
@@ -46,6 +47,9 @@ const (
|
||||
// preScoreStateKey is the key in CycleState to NodeAffinity pre-computed data for Scoring.
|
||||
preScoreStateKey = "PreScore" + Name
|
||||
|
||||
// preFilterStateKey is the key in CycleState to NodeAffinity pre-compute data for Filtering.
|
||||
preFilterStateKey = "PreFilter" + Name
|
||||
|
||||
// ErrReasonPod is the reason for Pod's node affinity/selector not matching.
|
||||
ErrReasonPod = "node(s) didn't match Pod's node affinity/selector"
|
||||
|
||||
@@ -58,6 +62,28 @@ func (pl *NodeAffinity) Name() string {
|
||||
return Name
|
||||
}
|
||||
|
||||
type preFilterState struct {
|
||||
requiredNodeSelector labels.Selector
|
||||
requiredNodeAffinity *nodeaffinity.LazyErrorNodeSelector
|
||||
}
|
||||
|
||||
// Clone just returns the same state because it is not affected by pod additions or deletions.
|
||||
func (s *preFilterState) Clone() framework.StateData {
|
||||
return s
|
||||
}
|
||||
|
||||
// PreFilter builds and writes cycle state used by Filter.
|
||||
func (pl *NodeAffinity) PreFilter(ctx context.Context, cycleState *framework.CycleState, pod *v1.Pod) *framework.Status {
|
||||
state := getPodRequiredNodeSelectorAndAffinity(pod)
|
||||
cycleState.Write(preFilterStateKey, state)
|
||||
return nil
|
||||
}
|
||||
|
||||
// PreFilterExtensions not necessary for this plugin as state doesn't depend on pod additions or deletions.
|
||||
func (pl *NodeAffinity) PreFilterExtensions() framework.PreFilterExtensions {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Filter checks if the Node matches the Pod .spec.affinity.nodeAffinity and
|
||||
// the plugin's added affinity.
|
||||
func (pl *NodeAffinity) Filter(ctx context.Context, state *framework.CycleState, pod *v1.Pod, nodeInfo *framework.NodeInfo) *framework.Status {
|
||||
@@ -68,8 +94,25 @@ func (pl *NodeAffinity) Filter(ctx context.Context, state *framework.CycleState,
|
||||
if pl.addedNodeSelector != nil && !pl.addedNodeSelector.Match(node) {
|
||||
return framework.NewStatus(framework.UnschedulableAndUnresolvable, errReasonEnforced)
|
||||
}
|
||||
if !pluginhelper.PodMatchesNodeSelectorAndAffinityTerms(pod, node) {
|
||||
return framework.NewStatus(framework.UnschedulableAndUnresolvable, ErrReasonPod)
|
||||
|
||||
s, err := getPreFilterState(state)
|
||||
if err != nil {
|
||||
// Fallback to calculate requiredNodeSelector and requiredNodeAffinity
|
||||
// here when PreFilter is disabled.
|
||||
s = getPodRequiredNodeSelectorAndAffinity(pod)
|
||||
}
|
||||
|
||||
if s.requiredNodeSelector != nil {
|
||||
if !s.requiredNodeSelector.Matches(labels.Set(node.Labels)) {
|
||||
return framework.NewStatus(framework.UnschedulableAndUnresolvable, ErrReasonPod)
|
||||
}
|
||||
}
|
||||
if s.requiredNodeAffinity != nil {
|
||||
// Ignore parsing errors for backwards compatibility.
|
||||
matches, _ := s.requiredNodeAffinity.Match(node)
|
||||
if !matches {
|
||||
return framework.NewStatus(framework.UnschedulableAndUnresolvable, ErrReasonPod)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -122,7 +165,7 @@ func (pl *NodeAffinity) Score(ctx context.Context, state *framework.CycleState,
|
||||
|
||||
s, err := getPreScoreState(state)
|
||||
if err != nil {
|
||||
// fallback to calculate preferredNodeAffinity here when PreScore is disabled
|
||||
// Fallback to calculate preferredNodeAffinity here when PreScore is disabled.
|
||||
preferredNodeAffinity, err := getPodPreferredNodeAffinity(pod)
|
||||
if err != nil {
|
||||
return 0, framework.AsStatus(err)
|
||||
@@ -204,3 +247,31 @@ func getPreScoreState(cycleState *framework.CycleState) (*preScoreState, error)
|
||||
}
|
||||
return s, nil
|
||||
}
|
||||
|
||||
func getPodRequiredNodeSelectorAndAffinity(pod *v1.Pod) *preFilterState {
|
||||
var selector labels.Selector
|
||||
if len(pod.Spec.NodeSelector) > 0 {
|
||||
selector = labels.SelectorFromSet(pod.Spec.NodeSelector)
|
||||
}
|
||||
// Use LazyErrorNodeSelector for backwards compatibility of parsing errors.
|
||||
var affinity *nodeaffinity.LazyErrorNodeSelector
|
||||
if pod.Spec.Affinity != nil &&
|
||||
pod.Spec.Affinity.NodeAffinity != nil &&
|
||||
pod.Spec.Affinity.NodeAffinity.RequiredDuringSchedulingIgnoredDuringExecution != nil {
|
||||
affinity = nodeaffinity.NewLazyErrorNodeSelector(pod.Spec.Affinity.NodeAffinity.RequiredDuringSchedulingIgnoredDuringExecution)
|
||||
}
|
||||
return &preFilterState{requiredNodeSelector: selector, requiredNodeAffinity: affinity}
|
||||
}
|
||||
|
||||
func getPreFilterState(cycleState *framework.CycleState) (*preFilterState, error) {
|
||||
c, err := cycleState.Read(preFilterStateKey)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("reading %q from cycleState: %v", preFilterStateKey, err)
|
||||
}
|
||||
|
||||
s, ok := c.(*preFilterState)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("invalid PreFilter state, got type %T", c)
|
||||
}
|
||||
return s, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user