New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
parse pod's node affinity once in preFilter #99213
Conversation
Thanks for your pull request. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA). 📝 Please follow instructions at https://git.k8s.io/community/CLA.md#the-contributor-license-agreement to sign the CLA. It may take a couple minutes for the CLA signature to be fully registered; after that, please reply here with a new comment and we'll verify. Thanks.
Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes/test-infra repository. I understand the commands that are listed here. |
Welcome @AliceZhang2016! |
Hi @AliceZhang2016. Thanks for your PR. I'm waiting for a kubernetes member to verify that this patch is reasonable to test. If it is, they should reply with Once the patch is verified, the new status will be reflected by the I understand the commands that are listed here. Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes/test-infra repository. |
} | ||
|
||
// Clone implements the mandatory Clone interface. We don't really copy the data since | ||
// there is not need for that |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We don't deep copy the data since values never get overridden
return false | ||
} | ||
return true | ||
} | ||
|
||
// nodeMatchesNodeSelector checks if a node's labels satisfy a list of node selector terms, | ||
// terms are ORed, and an empty list of terms will match nothing. | ||
func nodeMatchesNodeSelector(node *v1.Node, nodeSelector *v1.NodeSelector) bool { | ||
func NodeMatchesNodeSelector(node *v1.Node, nodeSelector *v1.NodeSelector) bool { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
rather, we should be able to get rid of this function now. And perhaps the entire file.
} | ||
var affinity *v1.NodeAffinity | ||
if pod.Spec.Affinity != nil { | ||
affinity = pod.Spec.Affinity.NodeAffinity |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
use nodeaffinity.NewLazyErrorNodeSelector
to store the parsed affinity.
and perhaps return a preScoreState
object directly
/ok-to-test |
return s | ||
} | ||
|
||
// PreFilter builds and writes cycle state used by Filter |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// PreFilter builds and writes cycle state used by Filter | |
// PreFilter builds and writes cycle state used by Filter. |
Make comment as a sentence. Referring to Comment sentences
if s.requiredNodeSelector != nil { | ||
if !s.requiredNodeSelector.Matches(labels.Set(node.Labels)) { | ||
flagFilter = false | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
use ./hack/update-gofmt.sh
to format the files.
} | ||
} | ||
if s.requiredNodeAffinity != nil { | ||
if s.requiredNodeAffinity.RequiredDuringSchedulingIgnoredDuringExecution != nil && !pluginhelper.NodeMatchesNodeSelector(node, s.requiredNodeAffinity.RequiredDuringSchedulingIgnoredDuringExecution) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: split the line.
if err != nil { | ||
// fallback to calculate requiredNodeSelector and requiredNodeAffinity | ||
// here when PreFilter is disabled | ||
requiredNodeSelector, requiredNodeAffinity, err := getPodRequiredNodeSelectorAndAffinity(pod) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
requiredNodeSelector, requiredNodeAffinity, err := getPodRequiredNodeSelectorAndAffinity(pod)
if err != nil {
return framework.AsStatus(err)
}
state := &preFilterState{
requiredNodeSelector: requiredNodeSelector,
requiredNodeAffinity: requiredNodeAffinity,
}
abstract a func to reduce duplicated code?
@@ -68,9 +97,39 @@ 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) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are several other components depending on pluginhelper.PodMatchesNodeSelectorAndAffinityTerms
. This change duplicates similar logic with the function, is it possible that we refactor helper functions to still share codes with other components?
} | ||
if flagFilter { | ||
if s.requiredNodeAffinity != nil { | ||
flagFilter, _ = s.requiredNodeAffinity.Match(node) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add a comment for the ignored error:
// Ignoring parsing errors for backwards compatibility.
} | ||
|
||
var flagFilter bool = true | ||
if s != nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this check is unnecessary.
pod.Spec.Affinity.NodeAffinity.RequiredDuringSchedulingIgnoredDuringExecution != nil { | ||
affinity = nodeaffinity.NewLazyErrorNodeSelector(pod.Spec.Affinity.NodeAffinity.RequiredDuringSchedulingIgnoredDuringExecution) | ||
} | ||
return &preFilterState{requiredNodeSelector: selector, requiredNodeAffinity: affinity}, nil |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
remove the error returned, as it is always nil
if pod.Spec.Affinity != nil && | ||
pod.Spec.Affinity.NodeAffinity != nil && | ||
pod.Spec.Affinity.NodeAffinity.RequiredDuringSchedulingIgnoredDuringExecution != nil { | ||
affinity = nodeaffinity.NewLazyErrorNodeSelector(pod.Spec.Affinity.NodeAffinity.RequiredDuringSchedulingIgnoredDuringExecution) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
add a comment:
Using LazyErrorNodeSelector for backwards compatibility of parsing errors
if s != nil { | ||
if s.requiredNodeSelector != nil { | ||
if !s.requiredNodeSelector.Matches(labels.Set(node.Labels)) { | ||
flagFilter = false |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it's simpler if you just return UnschedulableAndUnresolvable
here.
labels: map[string]string{ | ||
"GPU": "NVIDIA-GRID-K1", | ||
}, | ||
name: "Matches node affinity correctly even if PreFilter is not called", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's move the name of the test to the beginning of the struct. Please fix the existing tests too.
"baz": "blah", | ||
}, | ||
name: "Matches node selector correctly even if PreFilter is not called", | ||
disablePreFilter: true, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it looks like this variable is not being used.
cycleState.Write(preFilterStateKey, state) | ||
return nil | ||
} | ||
|
||
// PreFilterExtensions does not exist for this plugin. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// PreFilterExtensions not necessary for this plugin as state doesn't depend on nodes
name: "no selector", | ||
pod: &v1.Pod{}, | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
remove empty line
state := framework.NewCycleState() | ||
var gotStatus *framework.Status | ||
if !test.disablePreFilter { | ||
_ = p.(framework.PreFilterPlugin).PreFilter(context.Background(), state, test.pod) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
don't discard the error. Call t.Errorf
I signed it |
8ec023c
to
4fb8e34
Compare
/retest |
/test pull-kubernetes-e2e-kind |
/test pull-kubernetes-e2e-gce-ubuntu-containerd |
/kind feature |
/retest |
/remove-area test |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/lgtm
Good job
@AliceZhang2016: The following tests failed, say
Full PR test history. Your PR dashboard. Please help us cut down on flakes by linking to an open issue when you hit one in your PR. Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes/test-infra repository. I understand the commands that are listed here. |
/hold
|
/retest |
/hold cancel |
What type of PR is this?
/kind feature
What this PR does / why we need it:
scheduler: parse Pod's Node affinity once in PreFilter phase
Which issue(s) this PR fixes:
Fixes #96164
Special notes for your reviewer:
Does this PR introduce a user-facing change?
Additional documentation e.g., KEPs (Kubernetes Enhancement Proposals), usage docs, etc.: