|
|
|
@ -22,9 +22,6 @@ import (
|
|
|
|
|
"sync"
|
|
|
|
|
|
|
|
|
|
v1 "k8s.io/api/core/v1"
|
|
|
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
|
|
|
"k8s.io/apimachinery/pkg/labels"
|
|
|
|
|
"k8s.io/apimachinery/pkg/util/sets"
|
|
|
|
|
"k8s.io/klog"
|
|
|
|
|
framework "k8s.io/kubernetes/pkg/scheduler/framework/v1alpha1"
|
|
|
|
|
"k8s.io/kubernetes/pkg/scheduler/internal/parallelize"
|
|
|
|
@ -54,6 +51,8 @@ type preFilterState struct {
|
|
|
|
|
topologyToMatchedAffinityTerms topologyToMatchedTermCount
|
|
|
|
|
// A map of topology pairs to the number of existing pods that match the anti-affinity terms of the "pod".
|
|
|
|
|
topologyToMatchedAntiAffinityTerms topologyToMatchedTermCount
|
|
|
|
|
// podInfo of the incoming pod.
|
|
|
|
|
podInfo *framework.PodInfo
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Clone the prefilter state.
|
|
|
|
@ -66,45 +65,27 @@ func (s *preFilterState) Clone() framework.StateData {
|
|
|
|
|
copy.topologyToMatchedAffinityTerms = s.topologyToMatchedAffinityTerms.clone()
|
|
|
|
|
copy.topologyToMatchedAntiAffinityTerms = s.topologyToMatchedAntiAffinityTerms.clone()
|
|
|
|
|
copy.topologyToMatchedExistingAntiAffinityTerms = s.topologyToMatchedExistingAntiAffinityTerms.clone()
|
|
|
|
|
// No need to deep copy the podInfo because it shouldn't change.
|
|
|
|
|
copy.podInfo = s.podInfo
|
|
|
|
|
|
|
|
|
|
return ©
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// updateWithPod updates the preFilterState counters with the (anti)affinity matches for the given pod.
|
|
|
|
|
func (s *preFilterState) updateWithPod(updatedPod, pod *v1.Pod, node *v1.Node, multiplier int64) error {
|
|
|
|
|
func (s *preFilterState) updateWithPod(updatedPod *v1.Pod, node *v1.Node, multiplier int64) error {
|
|
|
|
|
if s == nil {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Update matching existing anti-affinity terms.
|
|
|
|
|
updatedPodAffinity := updatedPod.Spec.Affinity
|
|
|
|
|
if updatedPodAffinity != nil && updatedPodAffinity.PodAntiAffinity != nil {
|
|
|
|
|
antiAffinityTerms, err := getAffinityTerms(pod, schedutil.GetPodAntiAffinityTerms(updatedPodAffinity.PodAntiAffinity))
|
|
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("error in getting anti-affinity terms of Pod %v: %v", updatedPod.Name, err)
|
|
|
|
|
}
|
|
|
|
|
s.topologyToMatchedExistingAntiAffinityTerms.updateWithAntiAffinityTerms(pod, node, antiAffinityTerms, multiplier)
|
|
|
|
|
}
|
|
|
|
|
// TODO(#91058): AddPod/RemovePod should pass a *framework.PodInfo type instead of *v1.Pod.
|
|
|
|
|
updatedPodInfo := framework.NewPodInfo(updatedPod)
|
|
|
|
|
s.topologyToMatchedExistingAntiAffinityTerms.updateWithAntiAffinityTerms(s.podInfo.Pod, node, updatedPodInfo.RequiredAntiAffinityTerms, multiplier)
|
|
|
|
|
|
|
|
|
|
// Update matching incoming pod (anti)affinity terms.
|
|
|
|
|
affinity := pod.Spec.Affinity
|
|
|
|
|
podNodeName := updatedPod.Spec.NodeName
|
|
|
|
|
if affinity != nil && len(podNodeName) > 0 {
|
|
|
|
|
if affinity.PodAffinity != nil {
|
|
|
|
|
affinityTerms, err := getAffinityTerms(pod, schedutil.GetPodAffinityTerms(affinity.PodAffinity))
|
|
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("error in getting affinity terms of Pod %v: %v", pod.Name, err)
|
|
|
|
|
}
|
|
|
|
|
s.topologyToMatchedAffinityTerms.updateWithAffinityTerms(updatedPod, node, affinityTerms, multiplier)
|
|
|
|
|
}
|
|
|
|
|
if affinity.PodAntiAffinity != nil {
|
|
|
|
|
antiAffinityTerms, err := getAffinityTerms(pod, schedutil.GetPodAntiAffinityTerms(affinity.PodAntiAffinity))
|
|
|
|
|
if err != nil {
|
|
|
|
|
klog.Errorf("error in getting anti-affinity terms of Pod %v: %v", pod.Name, err)
|
|
|
|
|
}
|
|
|
|
|
s.topologyToMatchedAntiAffinityTerms.updateWithAntiAffinityTerms(updatedPod, node, antiAffinityTerms, multiplier)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
s.topologyToMatchedAffinityTerms.updateWithAffinityTerms(updatedPod, node, s.podInfo.RequiredAffinityTerms, multiplier)
|
|
|
|
|
s.topologyToMatchedAntiAffinityTerms.updateWithAntiAffinityTerms(updatedPod, node, s.podInfo.RequiredAntiAffinityTerms, multiplier)
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -131,11 +112,11 @@ func (m topologyToMatchedTermCount) clone() topologyToMatchedTermCount {
|
|
|
|
|
|
|
|
|
|
// updateWithAffinityTerms updates the topologyToMatchedTermCount map with the specified value
|
|
|
|
|
// for each affinity term if "targetPod" matches ALL terms.
|
|
|
|
|
func (m topologyToMatchedTermCount) updateWithAffinityTerms(targetPod *v1.Pod, targetPodNode *v1.Node, affinityTerms []*affinityTerm, value int64) {
|
|
|
|
|
func (m topologyToMatchedTermCount) updateWithAffinityTerms(targetPod *v1.Pod, targetPodNode *v1.Node, affinityTerms []framework.AffinityTerm, value int64) {
|
|
|
|
|
if podMatchesAllAffinityTerms(targetPod, affinityTerms) {
|
|
|
|
|
for _, t := range affinityTerms {
|
|
|
|
|
if topologyValue, ok := targetPodNode.Labels[t.topologyKey]; ok {
|
|
|
|
|
pair := topologyPair{key: t.topologyKey, value: topologyValue}
|
|
|
|
|
if topologyValue, ok := targetPodNode.Labels[t.TopologyKey]; ok {
|
|
|
|
|
pair := topologyPair{key: t.TopologyKey, value: topologyValue}
|
|
|
|
|
m[pair] += value
|
|
|
|
|
// value could be a negative value, hence we delete the entry if
|
|
|
|
|
// the entry is down to zero.
|
|
|
|
@ -149,12 +130,12 @@ func (m topologyToMatchedTermCount) updateWithAffinityTerms(targetPod *v1.Pod, t
|
|
|
|
|
|
|
|
|
|
// updateAntiAffinityTerms updates the topologyToMatchedTermCount map with the specified value
|
|
|
|
|
// for each anti-affinity term matched the target pod.
|
|
|
|
|
func (m topologyToMatchedTermCount) updateWithAntiAffinityTerms(targetPod *v1.Pod, targetPodNode *v1.Node, antiAffinityTerms []*affinityTerm, value int64) {
|
|
|
|
|
func (m topologyToMatchedTermCount) updateWithAntiAffinityTerms(targetPod *v1.Pod, targetPodNode *v1.Node, antiAffinityTerms []framework.AffinityTerm, value int64) {
|
|
|
|
|
// Check anti-affinity terms.
|
|
|
|
|
for _, a := range antiAffinityTerms {
|
|
|
|
|
if schedutil.PodMatchesTermsNamespaceAndSelector(targetPod, a.namespaces, a.selector) {
|
|
|
|
|
if topologyValue, ok := targetPodNode.Labels[a.topologyKey]; ok {
|
|
|
|
|
pair := topologyPair{key: a.topologyKey, value: topologyValue}
|
|
|
|
|
if schedutil.PodMatchesTermsNamespaceAndSelector(targetPod, a.Namespaces, a.Selector) {
|
|
|
|
|
if topologyValue, ok := targetPodNode.Labels[a.TopologyKey]; ok {
|
|
|
|
|
pair := topologyPair{key: a.TopologyKey, value: topologyValue}
|
|
|
|
|
m[pair] += value
|
|
|
|
|
// value could be a negative value, hence we delete the entry if
|
|
|
|
|
// the entry is down to zero.
|
|
|
|
@ -166,39 +147,13 @@ func (m topologyToMatchedTermCount) updateWithAntiAffinityTerms(targetPod *v1.Po
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// A processed version of v1.PodAffinityTerm.
|
|
|
|
|
type affinityTerm struct {
|
|
|
|
|
namespaces sets.String
|
|
|
|
|
selector labels.Selector
|
|
|
|
|
topologyKey string
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// getAffinityTerms receives a Pod and affinity terms and returns the namespaces and
|
|
|
|
|
// selectors of the terms.
|
|
|
|
|
func getAffinityTerms(pod *v1.Pod, v1Terms []v1.PodAffinityTerm) ([]*affinityTerm, error) {
|
|
|
|
|
if v1Terms == nil {
|
|
|
|
|
return nil, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var terms []*affinityTerm
|
|
|
|
|
for _, term := range v1Terms {
|
|
|
|
|
namespaces := schedutil.GetNamespacesFromPodAffinityTerm(pod, &term)
|
|
|
|
|
selector, err := metav1.LabelSelectorAsSelector(term.LabelSelector)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
terms = append(terms, &affinityTerm{namespaces: namespaces, selector: selector, topologyKey: term.TopologyKey})
|
|
|
|
|
}
|
|
|
|
|
return terms, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// podMatchesAllAffinityTerms returns true IFF the given pod matches all the given terms.
|
|
|
|
|
func podMatchesAllAffinityTerms(pod *v1.Pod, terms []*affinityTerm) bool {
|
|
|
|
|
func podMatchesAllAffinityTerms(pod *v1.Pod, terms []framework.AffinityTerm) bool {
|
|
|
|
|
if len(terms) == 0 {
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
for _, term := range terms {
|
|
|
|
|
if !schedutil.PodMatchesTermsNamespaceAndSelector(pod, term.namespaces, term.selector) {
|
|
|
|
|
if !schedutil.PodMatchesTermsNamespaceAndSelector(pod, term.Namespaces, term.Selector) {
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
@ -208,8 +163,7 @@ func podMatchesAllAffinityTerms(pod *v1.Pod, terms []*affinityTerm) bool {
|
|
|
|
|
// getTPMapMatchingExistingAntiAffinity calculates the following for each existing pod on each node:
|
|
|
|
|
// (1) Whether it has PodAntiAffinity
|
|
|
|
|
// (2) Whether any AffinityTerm matches the incoming pod
|
|
|
|
|
func getTPMapMatchingExistingAntiAffinity(pod *v1.Pod, allNodes []*framework.NodeInfo) (topologyToMatchedTermCount, error) {
|
|
|
|
|
errCh := parallelize.NewErrorChannel()
|
|
|
|
|
func getTPMapMatchingExistingAntiAffinity(pod *v1.Pod, allNodes []*framework.NodeInfo) topologyToMatchedTermCount {
|
|
|
|
|
var lock sync.Mutex
|
|
|
|
|
topologyMap := make(topologyToMatchedTermCount)
|
|
|
|
|
|
|
|
|
@ -219,8 +173,6 @@ func getTPMapMatchingExistingAntiAffinity(pod *v1.Pod, allNodes []*framework.Nod
|
|
|
|
|
topologyMap.append(toAppend)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
|
|
|
|
|
|
|
|
processNode := func(i int) {
|
|
|
|
|
nodeInfo := allNodes[i]
|
|
|
|
|
node := nodeInfo.Node()
|
|
|
|
@ -229,35 +181,26 @@ func getTPMapMatchingExistingAntiAffinity(pod *v1.Pod, allNodes []*framework.Nod
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
for _, existingPod := range nodeInfo.PodsWithAffinity {
|
|
|
|
|
existingPodTopologyMaps, err := getMatchingAntiAffinityTopologyPairsOfPod(pod, existingPod.Pod, node)
|
|
|
|
|
if err != nil {
|
|
|
|
|
errCh.SendErrorWithCancel(err, cancel)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
if existingPodTopologyMaps != nil {
|
|
|
|
|
existingPodTopologyMaps := getMatchingAntiAffinityTopologyPairsOfPod(pod, existingPod, node)
|
|
|
|
|
if len(existingPodTopologyMaps) != 0 {
|
|
|
|
|
appendResult(existingPodTopologyMaps)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
parallelize.Until(ctx, len(allNodes), processNode)
|
|
|
|
|
parallelize.Until(context.Background(), len(allNodes), processNode)
|
|
|
|
|
|
|
|
|
|
if err := errCh.ReceiveError(); err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return topologyMap, nil
|
|
|
|
|
return topologyMap
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// getTPMapMatchingIncomingAffinityAntiAffinity finds existing Pods that match affinity terms of the given "pod".
|
|
|
|
|
// It returns a topologyToMatchedTermCount that are checked later by the affinity
|
|
|
|
|
// predicate. With this topologyToMatchedTermCount available, the affinity predicate does not
|
|
|
|
|
// need to check all the pods in the cluster.
|
|
|
|
|
func getTPMapMatchingIncomingAffinityAntiAffinity(pod *v1.Pod, allNodes []*framework.NodeInfo) (topologyToMatchedTermCount, topologyToMatchedTermCount, error) {
|
|
|
|
|
func getTPMapMatchingIncomingAffinityAntiAffinity(podInfo *framework.PodInfo, allNodes []*framework.NodeInfo) (topologyToMatchedTermCount, topologyToMatchedTermCount) {
|
|
|
|
|
topologyPairsAffinityPodsMap := make(topologyToMatchedTermCount)
|
|
|
|
|
topologyToMatchedExistingAntiAffinityTerms := make(topologyToMatchedTermCount)
|
|
|
|
|
affinity := pod.Spec.Affinity
|
|
|
|
|
if affinity == nil || (affinity.PodAffinity == nil && affinity.PodAntiAffinity == nil) {
|
|
|
|
|
return topologyPairsAffinityPodsMap, topologyToMatchedExistingAntiAffinityTerms, nil
|
|
|
|
|
if len(podInfo.RequiredAffinityTerms) == 0 && len(podInfo.RequiredAntiAffinityTerms) == 0 {
|
|
|
|
|
return topologyPairsAffinityPodsMap, topologyToMatchedExistingAntiAffinityTerms
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var lock sync.Mutex
|
|
|
|
@ -272,16 +215,6 @@ func getTPMapMatchingIncomingAffinityAntiAffinity(pod *v1.Pod, allNodes []*frame
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
affinityTerms, err := getAffinityTerms(pod, schedutil.GetPodAffinityTerms(affinity.PodAffinity))
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, nil, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
antiAffinityTerms, err := getAffinityTerms(pod, schedutil.GetPodAntiAffinityTerms(affinity.PodAntiAffinity))
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, nil, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
processNode := func(i int) {
|
|
|
|
|
nodeInfo := allNodes[i]
|
|
|
|
|
node := nodeInfo.Node()
|
|
|
|
@ -293,10 +226,10 @@ func getTPMapMatchingIncomingAffinityAntiAffinity(pod *v1.Pod, allNodes []*frame
|
|
|
|
|
nodeTopologyPairsAntiAffinityPodsMap := make(topologyToMatchedTermCount)
|
|
|
|
|
for _, existingPod := range nodeInfo.Pods {
|
|
|
|
|
// Check affinity terms.
|
|
|
|
|
nodeTopologyPairsAffinityPodsMap.updateWithAffinityTerms(existingPod.Pod, node, affinityTerms, 1)
|
|
|
|
|
nodeTopologyPairsAffinityPodsMap.updateWithAffinityTerms(existingPod.Pod, node, podInfo.RequiredAffinityTerms, 1)
|
|
|
|
|
|
|
|
|
|
// Check anti-affinity terms.
|
|
|
|
|
nodeTopologyPairsAntiAffinityPodsMap.updateWithAntiAffinityTerms(existingPod.Pod, node, antiAffinityTerms, 1)
|
|
|
|
|
nodeTopologyPairsAntiAffinityPodsMap.updateWithAntiAffinityTerms(existingPod.Pod, node, podInfo.RequiredAntiAffinityTerms, 1)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if len(nodeTopologyPairsAffinityPodsMap) > 0 || len(nodeTopologyPairsAntiAffinityPodsMap) > 0 {
|
|
|
|
@ -305,24 +238,7 @@ func getTPMapMatchingIncomingAffinityAntiAffinity(pod *v1.Pod, allNodes []*frame
|
|
|
|
|
}
|
|
|
|
|
parallelize.Until(context.Background(), len(allNodes), processNode)
|
|
|
|
|
|
|
|
|
|
return topologyPairsAffinityPodsMap, topologyToMatchedExistingAntiAffinityTerms, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// targetPodMatchesAffinityOfPod returns true if "targetPod" matches ALL affinity terms of
|
|
|
|
|
// "pod". This function does not check topology.
|
|
|
|
|
// So, whether the targetPod actually matches or not needs further checks for a specific
|
|
|
|
|
// node.
|
|
|
|
|
func targetPodMatchesAffinityOfPod(pod, targetPod *v1.Pod) bool {
|
|
|
|
|
affinity := pod.Spec.Affinity
|
|
|
|
|
if affinity == nil || affinity.PodAffinity == nil {
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
affinityTerms, err := getAffinityTerms(pod, schedutil.GetPodAffinityTerms(affinity.PodAffinity))
|
|
|
|
|
if err != nil {
|
|
|
|
|
klog.Errorf("error in getting affinity terms of Pod %v", pod.Name)
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
return podMatchesAllAffinityTerms(targetPod, affinityTerms)
|
|
|
|
|
return topologyPairsAffinityPodsMap, topologyToMatchedExistingAntiAffinityTerms
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// PreFilter invoked at the prefilter extension point.
|
|
|
|
@ -337,22 +253,20 @@ func (pl *InterPodAffinity) PreFilter(ctx context.Context, cycleState *framework
|
|
|
|
|
return framework.NewStatus(framework.Error, fmt.Sprintf("failed to list NodeInfos with pods with affinity: %v", err))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
podInfo := framework.NewPodInfo(pod)
|
|
|
|
|
|
|
|
|
|
// existingPodAntiAffinityMap will be used later for efficient check on existing pods' anti-affinity
|
|
|
|
|
existingPodAntiAffinityMap, err := getTPMapMatchingExistingAntiAffinity(pod, havePodsWithAffinityNodes)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return framework.NewStatus(framework.Error, fmt.Sprintf("calculating preFilterState: %v", err))
|
|
|
|
|
}
|
|
|
|
|
existingPodAntiAffinityMap := getTPMapMatchingExistingAntiAffinity(pod, havePodsWithAffinityNodes)
|
|
|
|
|
|
|
|
|
|
// incomingPodAffinityMap will be used later for efficient check on incoming pod's affinity
|
|
|
|
|
// incomingPodAntiAffinityMap will be used later for efficient check on incoming pod's anti-affinity
|
|
|
|
|
incomingPodAffinityMap, incomingPodAntiAffinityMap, err := getTPMapMatchingIncomingAffinityAntiAffinity(pod, allNodes)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return framework.NewStatus(framework.Error, fmt.Sprintf("calculating preFilterState: %v", err))
|
|
|
|
|
}
|
|
|
|
|
incomingPodAffinityMap, incomingPodAntiAffinityMap := getTPMapMatchingIncomingAffinityAntiAffinity(podInfo, allNodes)
|
|
|
|
|
|
|
|
|
|
s := &preFilterState{
|
|
|
|
|
topologyToMatchedAffinityTerms: incomingPodAffinityMap,
|
|
|
|
|
topologyToMatchedAntiAffinityTerms: incomingPodAntiAffinityMap,
|
|
|
|
|
topologyToMatchedExistingAntiAffinityTerms: existingPodAntiAffinityMap,
|
|
|
|
|
podInfo: podInfo,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cycleState.Write(preFilterStateKey, s)
|
|
|
|
@ -370,7 +284,7 @@ func (pl *InterPodAffinity) AddPod(ctx context.Context, cycleState *framework.Cy
|
|
|
|
|
if err != nil {
|
|
|
|
|
return framework.NewStatus(framework.Error, err.Error())
|
|
|
|
|
}
|
|
|
|
|
state.updateWithPod(podToAdd, podToSchedule, nodeInfo.Node(), 1)
|
|
|
|
|
state.updateWithPod(podToAdd, nodeInfo.Node(), 1)
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -380,7 +294,7 @@ func (pl *InterPodAffinity) RemovePod(ctx context.Context, cycleState *framework
|
|
|
|
|
if err != nil {
|
|
|
|
|
return framework.NewStatus(framework.Error, err.Error())
|
|
|
|
|
}
|
|
|
|
|
state.updateWithPod(podToRemove, podToSchedule, nodeInfo.Node(), -1)
|
|
|
|
|
state.updateWithPod(podToRemove, nodeInfo.Node(), -1)
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -415,13 +329,13 @@ func (pl *InterPodAffinity) satisfiesExistingPodsAntiAffinity(pod *v1.Pod, state
|
|
|
|
|
return true, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// nodeMatchesAllTopologyTerms checks whether "nodeInfo" matches topology of all the "terms" for the given "pod".
|
|
|
|
|
func nodeMatchesAllTopologyTerms(pod *v1.Pod, topologyPairs topologyToMatchedTermCount, nodeInfo *framework.NodeInfo, terms []v1.PodAffinityTerm) bool {
|
|
|
|
|
// nodeMatchesAllAffinityTerms checks whether "nodeInfo" matches all affinity terms of the incoming pod.
|
|
|
|
|
func nodeMatchesAllAffinityTerms(nodeInfo *framework.NodeInfo, state *preFilterState) bool {
|
|
|
|
|
node := nodeInfo.Node()
|
|
|
|
|
for _, term := range terms {
|
|
|
|
|
for _, term := range state.podInfo.RequiredAffinityTerms {
|
|
|
|
|
if topologyValue, ok := node.Labels[term.TopologyKey]; ok {
|
|
|
|
|
pair := topologyPair{key: term.TopologyKey, value: topologyValue}
|
|
|
|
|
if topologyPairs[pair] <= 0 {
|
|
|
|
|
if state.topologyToMatchedAffinityTerms[pair] <= 0 {
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
@ -431,14 +345,13 @@ func nodeMatchesAllTopologyTerms(pod *v1.Pod, topologyPairs topologyToMatchedTer
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// nodeMatchesAnyTopologyTerm checks whether "nodeInfo" matches
|
|
|
|
|
// topology of any "term" for the given "pod".
|
|
|
|
|
func nodeMatchesAnyTopologyTerm(pod *v1.Pod, topologyPairs topologyToMatchedTermCount, nodeInfo *framework.NodeInfo, terms []v1.PodAffinityTerm) bool {
|
|
|
|
|
// nodeMatchesAnyTopologyTerm checks whether "nodeInfo" matches any of the pod's anti affinity terms.
|
|
|
|
|
func nodeMatchesAnyAntiAffinityTerm(nodeInfo *framework.NodeInfo, state *preFilterState) bool {
|
|
|
|
|
node := nodeInfo.Node()
|
|
|
|
|
for _, term := range terms {
|
|
|
|
|
for _, term := range state.podInfo.RequiredAntiAffinityTerms {
|
|
|
|
|
if topologyValue, ok := node.Labels[term.TopologyKey]; ok {
|
|
|
|
|
pair := topologyPair{key: term.TopologyKey, value: topologyValue}
|
|
|
|
|
if topologyPairs[pair] > 0 {
|
|
|
|
|
if state.topologyToMatchedAntiAffinityTerms[pair] > 0 {
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
@ -449,62 +362,38 @@ func nodeMatchesAnyTopologyTerm(pod *v1.Pod, topologyPairs topologyToMatchedTerm
|
|
|
|
|
// getMatchingAntiAffinityTopologyPairs calculates the following for "existingPod" on given node:
|
|
|
|
|
// (1) Whether it has PodAntiAffinity
|
|
|
|
|
// (2) Whether ANY AffinityTerm matches the incoming pod
|
|
|
|
|
func getMatchingAntiAffinityTopologyPairsOfPod(newPod *v1.Pod, existingPod *v1.Pod, node *v1.Node) (topologyToMatchedTermCount, error) {
|
|
|
|
|
affinity := existingPod.Spec.Affinity
|
|
|
|
|
if affinity == nil || affinity.PodAntiAffinity == nil {
|
|
|
|
|
return nil, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func getMatchingAntiAffinityTopologyPairsOfPod(newPod *v1.Pod, existingPod *framework.PodInfo, node *v1.Node) topologyToMatchedTermCount {
|
|
|
|
|
topologyMap := make(topologyToMatchedTermCount)
|
|
|
|
|
for _, term := range schedutil.GetPodAntiAffinityTerms(affinity.PodAntiAffinity) {
|
|
|
|
|
selector, err := metav1.LabelSelectorAsSelector(term.LabelSelector)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
namespaces := schedutil.GetNamespacesFromPodAffinityTerm(existingPod, &term)
|
|
|
|
|
if schedutil.PodMatchesTermsNamespaceAndSelector(newPod, namespaces, selector) {
|
|
|
|
|
for _, term := range existingPod.RequiredAntiAffinityTerms {
|
|
|
|
|
if schedutil.PodMatchesTermsNamespaceAndSelector(newPod, term.Namespaces, term.Selector) {
|
|
|
|
|
if topologyValue, ok := node.Labels[term.TopologyKey]; ok {
|
|
|
|
|
pair := topologyPair{key: term.TopologyKey, value: topologyValue}
|
|
|
|
|
topologyMap[pair]++
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return topologyMap, nil
|
|
|
|
|
return topologyMap
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// satisfiesPodsAffinityAntiAffinity checks if scheduling the pod onto this node would break any term of this pod.
|
|
|
|
|
// This function returns two boolean flags. The first boolean flag indicates whether the pod matches affinity rules
|
|
|
|
|
// or not. The second boolean flag indicates if the pod matches anti-affinity rules.
|
|
|
|
|
func (pl *InterPodAffinity) satisfiesPodsAffinityAntiAffinity(pod *v1.Pod,
|
|
|
|
|
state *preFilterState, nodeInfo *framework.NodeInfo,
|
|
|
|
|
affinity *v1.Affinity) (bool, bool, error) {
|
|
|
|
|
node := nodeInfo.Node()
|
|
|
|
|
if node == nil {
|
|
|
|
|
return false, false, fmt.Errorf("node not found")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (pl *InterPodAffinity) satisfiesPodsAffinityAntiAffinity(state *preFilterState, nodeInfo *framework.NodeInfo) (bool, bool, error) {
|
|
|
|
|
// Check all affinity terms.
|
|
|
|
|
topologyToMatchedAffinityTerms := state.topologyToMatchedAffinityTerms
|
|
|
|
|
if affinityTerms := schedutil.GetPodAffinityTerms(affinity.PodAffinity); len(affinityTerms) > 0 {
|
|
|
|
|
matchExists := nodeMatchesAllTopologyTerms(pod, topologyToMatchedAffinityTerms, nodeInfo, affinityTerms)
|
|
|
|
|
if !matchExists {
|
|
|
|
|
// This pod may the first pod in a series that have affinity to themselves. In order
|
|
|
|
|
// to not leave such pods in pending state forever, we check that if no other pod
|
|
|
|
|
// in the cluster matches the namespace and selector of this pod and the pod matches
|
|
|
|
|
// its own terms, then we allow the pod to pass the affinity check.
|
|
|
|
|
if len(topologyToMatchedAffinityTerms) != 0 || !targetPodMatchesAffinityOfPod(pod, pod) {
|
|
|
|
|
return false, false, nil
|
|
|
|
|
}
|
|
|
|
|
if !nodeMatchesAllAffinityTerms(nodeInfo, state) {
|
|
|
|
|
// This pod may be the first pod in a series that have affinity to themselves. In order
|
|
|
|
|
// to not leave such pods in pending state forever, we check that if no other pod
|
|
|
|
|
// in the cluster matches the namespace and selector of this pod and the pod matches
|
|
|
|
|
// its own terms, then we allow the pod to pass the affinity check.
|
|
|
|
|
podInfo := state.podInfo
|
|
|
|
|
if len(state.topologyToMatchedAffinityTerms) != 0 || !podMatchesAllAffinityTerms(podInfo.Pod, podInfo.RequiredAffinityTerms) {
|
|
|
|
|
return false, false, nil
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check all anti-affinity terms.
|
|
|
|
|
topologyToMatchedAntiAffinityTerms := state.topologyToMatchedAntiAffinityTerms
|
|
|
|
|
if antiAffinityTerms := schedutil.GetPodAntiAffinityTerms(affinity.PodAntiAffinity); len(antiAffinityTerms) > 0 {
|
|
|
|
|
matchExists := nodeMatchesAnyTopologyTerm(pod, topologyToMatchedAntiAffinityTerms, nodeInfo, antiAffinityTerms)
|
|
|
|
|
if matchExists {
|
|
|
|
|
return true, false, nil
|
|
|
|
|
}
|
|
|
|
|
if nodeMatchesAnyAntiAffinityTerm(nodeInfo, state) {
|
|
|
|
|
return true, false, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true, true, nil
|
|
|
|
@ -513,6 +402,10 @@ func (pl *InterPodAffinity) satisfiesPodsAffinityAntiAffinity(pod *v1.Pod,
|
|
|
|
|
// Filter invoked at the filter extension point.
|
|
|
|
|
// It checks if a pod can be scheduled on the specified node with pod affinity/anti-affinity configuration.
|
|
|
|
|
func (pl *InterPodAffinity) Filter(ctx context.Context, cycleState *framework.CycleState, pod *v1.Pod, nodeInfo *framework.NodeInfo) *framework.Status {
|
|
|
|
|
if nodeInfo.Node() == nil {
|
|
|
|
|
return framework.NewStatus(framework.Error, "node not found")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
state, err := getPreFilterState(cycleState)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return framework.NewStatus(framework.Error, err.Error())
|
|
|
|
@ -526,11 +419,7 @@ func (pl *InterPodAffinity) Filter(ctx context.Context, cycleState *framework.Cy
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Now check if <pod> requirements will be satisfied on this node.
|
|
|
|
|
affinity := pod.Spec.Affinity
|
|
|
|
|
if affinity == nil || (affinity.PodAffinity == nil && affinity.PodAntiAffinity == nil) {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
if satisfiesAffinity, satisfiesAntiAffinity, err := pl.satisfiesPodsAffinityAntiAffinity(pod, state, nodeInfo, affinity); err != nil || !satisfiesAffinity || !satisfiesAntiAffinity {
|
|
|
|
|
if satisfiesAffinity, satisfiesAntiAffinity, err := pl.satisfiesPodsAffinityAntiAffinity(state, nodeInfo); err != nil || !satisfiesAffinity || !satisfiesAntiAffinity {
|
|
|
|
|
if err != nil {
|
|
|
|
|
return framework.NewStatus(framework.Error, err.Error())
|
|
|
|
|
}
|
|
|
|
|