Don't require failureDomains in PodAffinityChecker

failureDomains are only used for PreferredDuringScheduling pod
anti-affinity, which is ignored by PodAffinityChecker.
This unnecessary requirement was making it hard to move
PodAffinityChecker to GeneralPredicates because that would require
passing --failure-domains to both kubelet and kube-controller-manager.
This commit is contained in:
Ivan Shvedunov 2016-10-11 16:31:47 +03:00
parent 6b9a944285
commit d40a8f3279
5 changed files with 29 additions and 24 deletions

View File

@ -51,7 +51,6 @@ go_test(
"//pkg/labels:go_default_library", "//pkg/labels:go_default_library",
"//pkg/util/codeinspector:go_default_library", "//pkg/util/codeinspector:go_default_library",
"//plugin/pkg/scheduler/algorithm:go_default_library", "//plugin/pkg/scheduler/algorithm:go_default_library",
"//plugin/pkg/scheduler/algorithm/priorities/util:go_default_library",
"//plugin/pkg/scheduler/schedulercache:go_default_library", "//plugin/pkg/scheduler/schedulercache:go_default_library",
"//vendor:k8s.io/gengo/parser", "//vendor:k8s.io/gengo/parser",
"//vendor:k8s.io/gengo/types", "//vendor:k8s.io/gengo/types",

View File

@ -17,6 +17,7 @@ limitations under the License.
package predicates package predicates
import ( import (
"errors"
"fmt" "fmt"
"math/rand" "math/rand"
"strconv" "strconv"
@ -859,16 +860,14 @@ func GeneralPredicates(pod *v1.Pod, meta interface{}, nodeInfo *schedulercache.N
} }
type PodAffinityChecker struct { type PodAffinityChecker struct {
info NodeInfo info NodeInfo
podLister algorithm.PodLister podLister algorithm.PodLister
failureDomains priorityutil.Topologies
} }
func NewPodAffinityPredicate(info NodeInfo, podLister algorithm.PodLister, failureDomains []string) algorithm.FitPredicate { func NewPodAffinityPredicate(info NodeInfo, podLister algorithm.PodLister) algorithm.FitPredicate {
checker := &PodAffinityChecker{ checker := &PodAffinityChecker{
info: info, info: info,
podLister: podLister, podLister: podLister,
failureDomains: priorityutil.Topologies{DefaultKeys: failureDomains},
} }
return checker.InterPodAffinityMatches return checker.InterPodAffinityMatches
} }
@ -903,11 +902,14 @@ func (c *PodAffinityChecker) InterPodAffinityMatches(pod *v1.Pod, meta interface
return true, nil, nil return true, nil, nil
} }
// AnyPodMatchesPodAffinityTerm checks if any of given pods can match the specific podAffinityTerm. // anyPodMatchesPodAffinityTerm checks if any of given pods can match the specific podAffinityTerm.
// First return value indicates whether a matching pod exists on a node that matches the topology key, // First return value indicates whether a matching pod exists on a node that matches the topology key,
// while the second return value indicates whether a matching pod exists anywhere. // while the second return value indicates whether a matching pod exists anywhere.
// TODO: Do we really need any pod matching, or all pods matching? I think the latter. // TODO: Do we really need any pod matching, or all pods matching? I think the latter.
func (c *PodAffinityChecker) anyPodMatchesPodAffinityTerm(pod *v1.Pod, allPods []*v1.Pod, node *v1.Node, term *v1.PodAffinityTerm) (bool, bool, error) { func (c *PodAffinityChecker) anyPodMatchesPodAffinityTerm(pod *v1.Pod, allPods []*v1.Pod, node *v1.Node, term *v1.PodAffinityTerm) (bool, bool, error) {
if len(term.TopologyKey) == 0 {
return false, false, errors.New("Empty topologyKey is not allowed except for PreferredDuringScheduling pod anti-affinity")
}
matchingPodExists := false matchingPodExists := false
for _, existingPod := range allPods { for _, existingPod := range allPods {
match, err := priorityutil.PodMatchesTermsNamespaceAndSelector(existingPod, pod, term) match, err := priorityutil.PodMatchesTermsNamespaceAndSelector(existingPod, pod, term)
@ -920,7 +922,7 @@ func (c *PodAffinityChecker) anyPodMatchesPodAffinityTerm(pod *v1.Pod, allPods [
if err != nil { if err != nil {
return false, matchingPodExists, err return false, matchingPodExists, err
} }
if c.failureDomains.NodesHaveSameTopologyKey(node, existingPodNode, term.TopologyKey) { if priorityutil.NodesHaveSameTopologyKey(node, existingPodNode, term.TopologyKey) {
return true, matchingPodExists, nil return true, matchingPodExists, nil
} }
} }
@ -1056,7 +1058,11 @@ func (c *PodAffinityChecker) satisfiesExistingPodsAntiAffinity(pod *v1.Pod, meta
} }
} }
for _, term := range matchingTerms { for _, term := range matchingTerms {
if c.failureDomains.NodesHaveSameTopologyKey(node, term.node, term.term.TopologyKey) { if len(term.term.TopologyKey) == 0 {
glog.V(10).Infof("Empty topologyKey is not allowed except for PreferredDuringScheduling pod anti-affinity")
return false
}
if priorityutil.NodesHaveSameTopologyKey(node, term.node, term.term.TopologyKey) {
glog.V(10).Infof("Cannot schedule pod %+v onto node %v,because of PodAntiAffinityTerm %v", glog.V(10).Infof("Cannot schedule pod %+v onto node %v,because of PodAntiAffinityTerm %v",
podName(pod), node.Name, term.term) podName(pod), node.Name, term.term)
return false return false

View File

@ -21,7 +21,6 @@ import (
"os/exec" "os/exec"
"path/filepath" "path/filepath"
"reflect" "reflect"
"strings"
"testing" "testing"
"k8s.io/gengo/parser" "k8s.io/gengo/parser"
@ -30,7 +29,6 @@ import (
"k8s.io/kubernetes/pkg/api/v1" "k8s.io/kubernetes/pkg/api/v1"
"k8s.io/kubernetes/pkg/util/codeinspector" "k8s.io/kubernetes/pkg/util/codeinspector"
"k8s.io/kubernetes/plugin/pkg/scheduler/algorithm" "k8s.io/kubernetes/plugin/pkg/scheduler/algorithm"
priorityutil "k8s.io/kubernetes/plugin/pkg/scheduler/algorithm/priorities/util"
"k8s.io/kubernetes/plugin/pkg/scheduler/schedulercache" "k8s.io/kubernetes/plugin/pkg/scheduler/schedulercache"
) )
@ -2463,9 +2461,8 @@ func TestInterPodAffinity(t *testing.T) {
} }
fit := PodAffinityChecker{ fit := PodAffinityChecker{
info: FakeNodeInfo(*node), info: FakeNodeInfo(*node),
podLister: algorithm.FakePodLister(test.pods), podLister: algorithm.FakePodLister(test.pods),
failureDomains: priorityutil.Topologies{DefaultKeys: strings.Split(v1.DefaultFailureDomains, ",")},
} }
nodeInfo := schedulercache.NewNodeInfo(podsOnNode...) nodeInfo := schedulercache.NewNodeInfo(podsOnNode...)
nodeInfo.SetNode(test.node) nodeInfo.SetNode(test.node)
@ -2708,9 +2705,8 @@ func TestInterPodAffinityWithMultipleNodes(t *testing.T) {
} }
testFit := PodAffinityChecker{ testFit := PodAffinityChecker{
info: nodeListInfo, info: nodeListInfo,
podLister: algorithm.FakePodLister(test.pods), podLister: algorithm.FakePodLister(test.pods),
failureDomains: priorityutil.Topologies{DefaultKeys: strings.Split(v1.DefaultFailureDomains, ",")},
} }
nodeInfo := schedulercache.NewNodeInfo(podsOnNode...) nodeInfo := schedulercache.NewNodeInfo(podsOnNode...)
nodeInfo.SetNode(&node) nodeInfo.SetNode(&node)

View File

@ -52,8 +52,12 @@ func PodMatchesTermsNamespaceAndSelector(pod *v1.Pod, affinityPod *v1.Pod, term
return true, nil return true, nil
} }
// nodesHaveSameTopologyKeyInternal checks if nodeA and nodeB have same label value with given topologyKey as label key. // NodesHaveSameTopologyKey checks if nodeA and nodeB have same label value with given topologyKey as label key.
func nodesHaveSameTopologyKeyInternal(nodeA, nodeB *v1.Node, topologyKey string) bool { // Returns false if topologyKey is empty.
func NodesHaveSameTopologyKey(nodeA, nodeB *v1.Node, topologyKey string) bool {
if len(topologyKey) == 0 {
return false
}
return nodeA.Labels != nil && nodeB.Labels != nil && len(nodeA.Labels[topologyKey]) > 0 && nodeA.Labels[topologyKey] == nodeB.Labels[topologyKey] return nodeA.Labels != nil && nodeB.Labels != nil && len(nodeA.Labels[topologyKey]) > 0 && nodeA.Labels[topologyKey] == nodeB.Labels[topologyKey]
} }
@ -67,12 +71,12 @@ func (tps *Topologies) NodesHaveSameTopologyKey(nodeA, nodeB *v1.Node, topologyK
if len(topologyKey) == 0 { if len(topologyKey) == 0 {
// assumes this is allowed only for PreferredDuringScheduling pod anti-affinity (ensured by api/validation) // assumes this is allowed only for PreferredDuringScheduling pod anti-affinity (ensured by api/validation)
for _, defaultKey := range tps.DefaultKeys { for _, defaultKey := range tps.DefaultKeys {
if nodesHaveSameTopologyKeyInternal(nodeA, nodeB, defaultKey) { if NodesHaveSameTopologyKey(nodeA, nodeB, defaultKey) {
return true return true
} }
} }
return false return false
} else { } else {
return nodesHaveSameTopologyKeyInternal(nodeA, nodeB, topologyKey) return NodesHaveSameTopologyKey(nodeA, nodeB, topologyKey)
} }
} }

View File

@ -139,7 +139,7 @@ func defaultPredicates() sets.String {
factory.RegisterFitPredicateFactory( factory.RegisterFitPredicateFactory(
"MatchInterPodAffinity", "MatchInterPodAffinity",
func(args factory.PluginFactoryArgs) algorithm.FitPredicate { func(args factory.PluginFactoryArgs) algorithm.FitPredicate {
return predicates.NewPodAffinityPredicate(args.NodeInfo, args.PodLister, args.FailureDomains) return predicates.NewPodAffinityPredicate(args.NodeInfo, args.PodLister)
}, },
), ),