mirror of https://github.com/k3s-io/k3s
Merge pull request #50362 from k82cn/k8s_50360
Automatic merge from submit-queue Moved node condition filter into a predicates. **Which issue this PR fixes** *(optional, in `fixes #<issue number>(, fixes #<issue_number>, ...)` format, will close that issue when PR gets merged)*: fixes #50360 **Release note**: ```release-note A new predicates, named 'CheckNodeCondition', was added to replace node condition filter. 'NetworkUnavailable', 'OutOfDisk' and 'NotReady' maybe reported as a reason when failed to schedule pods. ```pull/6/head
commit
2820b45caa
|
@ -38,6 +38,10 @@ var (
|
||||||
ErrNodeUnderMemoryPressure = newPredicateFailureError("NodeUnderMemoryPressure")
|
ErrNodeUnderMemoryPressure = newPredicateFailureError("NodeUnderMemoryPressure")
|
||||||
ErrNodeUnderDiskPressure = newPredicateFailureError("NodeUnderDiskPressure")
|
ErrNodeUnderDiskPressure = newPredicateFailureError("NodeUnderDiskPressure")
|
||||||
ErrNodeOutOfDisk = newPredicateFailureError("NodeOutOfDisk")
|
ErrNodeOutOfDisk = newPredicateFailureError("NodeOutOfDisk")
|
||||||
|
ErrNodeNotReady = newPredicateFailureError("NodeNotReady")
|
||||||
|
ErrNodeNetworkUnavailable = newPredicateFailureError("NodeNetworkUnavailable")
|
||||||
|
ErrNodeUnschedulable = newPredicateFailureError("NodeUnschedulable")
|
||||||
|
ErrNodeUnknownCondition = newPredicateFailureError("NodeUnknownCondition")
|
||||||
ErrVolumeNodeConflict = newPredicateFailureError("NoVolumeNodeConflict")
|
ErrVolumeNodeConflict = newPredicateFailureError("NoVolumeNodeConflict")
|
||||||
// ErrFakePredicate is used for test only. The fake predicates returning false also returns error
|
// ErrFakePredicate is used for test only. The fake predicates returning false also returns error
|
||||||
// as ErrFakePredicate.
|
// as ErrFakePredicate.
|
||||||
|
|
|
@ -1301,6 +1301,37 @@ func CheckNodeDiskPressurePredicate(pod *v1.Pod, meta interface{}, nodeInfo *sch
|
||||||
return true, nil, nil
|
return true, nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CheckNodeConditionPredicate checks if a pod can be scheduled on a node reporting out of disk,
|
||||||
|
// network unavailable and not ready condition. Only node conditions are accounted in this predicate.
|
||||||
|
func CheckNodeConditionPredicate(pod *v1.Pod, meta interface{}, nodeInfo *schedulercache.NodeInfo) (bool, []algorithm.PredicateFailureReason, error) {
|
||||||
|
reasons := []algorithm.PredicateFailureReason{}
|
||||||
|
|
||||||
|
if nodeInfo == nil || nodeInfo.Node() == nil {
|
||||||
|
return false, []algorithm.PredicateFailureReason{ErrNodeUnknownCondition}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
node := nodeInfo.Node()
|
||||||
|
for _, cond := range node.Status.Conditions {
|
||||||
|
// We consider the node for scheduling only when its:
|
||||||
|
// - NodeReady condition status is ConditionTrue,
|
||||||
|
// - NodeOutOfDisk condition status is ConditionFalse,
|
||||||
|
// - NodeNetworkUnavailable condition status is ConditionFalse.
|
||||||
|
if cond.Type == v1.NodeReady && cond.Status != v1.ConditionTrue {
|
||||||
|
reasons = append(reasons, ErrNodeNotReady)
|
||||||
|
} else if cond.Type == v1.NodeOutOfDisk && cond.Status != v1.ConditionFalse {
|
||||||
|
reasons = append(reasons, ErrNodeOutOfDisk)
|
||||||
|
} else if cond.Type == v1.NodeNetworkUnavailable && cond.Status != v1.ConditionFalse {
|
||||||
|
reasons = append(reasons, ErrNodeNetworkUnavailable)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if node.Spec.Unschedulable {
|
||||||
|
reasons = append(reasons, ErrNodeUnschedulable)
|
||||||
|
}
|
||||||
|
|
||||||
|
return len(reasons) == 0, reasons, nil
|
||||||
|
}
|
||||||
|
|
||||||
type VolumeNodeChecker struct {
|
type VolumeNodeChecker struct {
|
||||||
pvInfo PersistentVolumeInfo
|
pvInfo PersistentVolumeInfo
|
||||||
pvcInfo PersistentVolumeClaimInfo
|
pvcInfo PersistentVolumeClaimInfo
|
||||||
|
|
|
@ -3442,6 +3442,78 @@ func TestPodSchedulesOnNodeWithDiskPressureCondition(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestNodeConditionPredicate(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
node *v1.Node
|
||||||
|
schedulable bool
|
||||||
|
}{
|
||||||
|
// node1 considered
|
||||||
|
{
|
||||||
|
node: &v1.Node{ObjectMeta: metav1.ObjectMeta{Name: "node1"}, Status: v1.NodeStatus{Conditions: []v1.NodeCondition{{Type: v1.NodeReady, Status: v1.ConditionTrue}}}},
|
||||||
|
schedulable: true,
|
||||||
|
},
|
||||||
|
// node2 ignored - node not Ready
|
||||||
|
{
|
||||||
|
node: &v1.Node{ObjectMeta: metav1.ObjectMeta{Name: "node2"}, Status: v1.NodeStatus{Conditions: []v1.NodeCondition{{Type: v1.NodeReady, Status: v1.ConditionFalse}}}},
|
||||||
|
schedulable: false,
|
||||||
|
},
|
||||||
|
// node3 ignored - node out of disk
|
||||||
|
{
|
||||||
|
node: &v1.Node{ObjectMeta: metav1.ObjectMeta{Name: "node3"}, Status: v1.NodeStatus{Conditions: []v1.NodeCondition{{Type: v1.NodeOutOfDisk, Status: v1.ConditionTrue}}}},
|
||||||
|
schedulable: false,
|
||||||
|
},
|
||||||
|
|
||||||
|
// node4 considered
|
||||||
|
{
|
||||||
|
node: &v1.Node{ObjectMeta: metav1.ObjectMeta{Name: "node4"}, Status: v1.NodeStatus{Conditions: []v1.NodeCondition{{Type: v1.NodeOutOfDisk, Status: v1.ConditionFalse}}}},
|
||||||
|
schedulable: true,
|
||||||
|
},
|
||||||
|
// node5 ignored - node out of disk
|
||||||
|
{
|
||||||
|
node: &v1.Node{ObjectMeta: metav1.ObjectMeta{Name: "node5"}, Status: v1.NodeStatus{Conditions: []v1.NodeCondition{{Type: v1.NodeReady, Status: v1.ConditionTrue}, {Type: v1.NodeOutOfDisk, Status: v1.ConditionTrue}}}},
|
||||||
|
schedulable: false,
|
||||||
|
},
|
||||||
|
// node6 considered
|
||||||
|
{
|
||||||
|
node: &v1.Node{ObjectMeta: metav1.ObjectMeta{Name: "node6"}, Status: v1.NodeStatus{Conditions: []v1.NodeCondition{{Type: v1.NodeReady, Status: v1.ConditionTrue}, {Type: v1.NodeOutOfDisk, Status: v1.ConditionFalse}}}},
|
||||||
|
schedulable: true,
|
||||||
|
},
|
||||||
|
// node7 ignored - node out of disk, node not Ready
|
||||||
|
{
|
||||||
|
node: &v1.Node{ObjectMeta: metav1.ObjectMeta{Name: "node7"}, Status: v1.NodeStatus{Conditions: []v1.NodeCondition{{Type: v1.NodeReady, Status: v1.ConditionFalse}, {Type: v1.NodeOutOfDisk, Status: v1.ConditionTrue}}}},
|
||||||
|
schedulable: false,
|
||||||
|
},
|
||||||
|
// node8 ignored - node not Ready
|
||||||
|
{
|
||||||
|
node: &v1.Node{ObjectMeta: metav1.ObjectMeta{Name: "node8"}, Status: v1.NodeStatus{Conditions: []v1.NodeCondition{{Type: v1.NodeReady, Status: v1.ConditionFalse}, {Type: v1.NodeOutOfDisk, Status: v1.ConditionFalse}}}},
|
||||||
|
schedulable: false,
|
||||||
|
},
|
||||||
|
// node9 ignored - node unschedulable
|
||||||
|
{
|
||||||
|
node: &v1.Node{ObjectMeta: metav1.ObjectMeta{Name: "node9"}, Spec: v1.NodeSpec{Unschedulable: true}},
|
||||||
|
schedulable: false,
|
||||||
|
},
|
||||||
|
// node10 considered
|
||||||
|
{
|
||||||
|
node: &v1.Node{ObjectMeta: metav1.ObjectMeta{Name: "node10"}, Spec: v1.NodeSpec{Unschedulable: false}},
|
||||||
|
schedulable: true,
|
||||||
|
},
|
||||||
|
// node11 considered
|
||||||
|
{
|
||||||
|
node: &v1.Node{ObjectMeta: metav1.ObjectMeta{Name: "node11"}},
|
||||||
|
schedulable: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
nodeInfo := makeEmptyNodeInfo(test.node)
|
||||||
|
if fit, reasons, err := CheckNodeConditionPredicate(nil, nil, nodeInfo); fit != test.schedulable {
|
||||||
|
t.Errorf("%s: expected: %t, got %t; %+v, %v",
|
||||||
|
test.node.Name, test.schedulable, fit, reasons, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func createPodWithVolume(pod, pv, pvc string) *v1.Pod {
|
func createPodWithVolume(pod, pv, pvc string) *v1.Pod {
|
||||||
return &v1.Pod{
|
return &v1.Pod{
|
||||||
ObjectMeta: metav1.ObjectMeta{Name: pod, Namespace: "default"},
|
ObjectMeta: metav1.ObjectMeta{Name: pod, Namespace: "default"},
|
||||||
|
|
|
@ -387,6 +387,79 @@ func TestCompatibility_v1_Scheduler(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
// Do not change this JSON after the corresponding release has been tagged.
|
||||||
|
// A failure indicates backwards compatibility with the specified release was broken.
|
||||||
|
"1.8": {
|
||||||
|
JSON: `{
|
||||||
|
"kind": "Policy",
|
||||||
|
"apiVersion": "v1",
|
||||||
|
"predicates": [
|
||||||
|
{"name": "MatchNodeSelector"},
|
||||||
|
{"name": "PodFitsResources"},
|
||||||
|
{"name": "PodFitsHostPorts"},
|
||||||
|
{"name": "HostName"},
|
||||||
|
{"name": "NoDiskConflict"},
|
||||||
|
{"name": "NoVolumeZoneConflict"},
|
||||||
|
{"name": "PodToleratesNodeTaints"},
|
||||||
|
{"name": "CheckNodeMemoryPressure"},
|
||||||
|
{"name": "CheckNodeDiskPressure"},
|
||||||
|
{"name": "CheckNodeCondition"},
|
||||||
|
{"name": "MaxEBSVolumeCount"},
|
||||||
|
{"name": "MaxGCEPDVolumeCount"},
|
||||||
|
{"name": "MaxAzureDiskVolumeCount"},
|
||||||
|
{"name": "MatchInterPodAffinity"},
|
||||||
|
{"name": "GeneralPredicates"},
|
||||||
|
{"name": "TestServiceAffinity", "argument": {"serviceAffinity" : {"labels" : ["region"]}}},
|
||||||
|
{"name": "TestLabelsPresence", "argument": {"labelsPresence" : {"labels" : ["foo"], "presence":true}}},
|
||||||
|
{"name": "NoVolumeNodeConflict"}
|
||||||
|
],"priorities": [
|
||||||
|
{"name": "EqualPriority", "weight": 2},
|
||||||
|
{"name": "ImageLocalityPriority", "weight": 2},
|
||||||
|
{"name": "LeastRequestedPriority", "weight": 2},
|
||||||
|
{"name": "BalancedResourceAllocation", "weight": 2},
|
||||||
|
{"name": "SelectorSpreadPriority", "weight": 2},
|
||||||
|
{"name": "NodePreferAvoidPodsPriority", "weight": 2},
|
||||||
|
{"name": "NodeAffinityPriority", "weight": 2},
|
||||||
|
{"name": "TaintTolerationPriority", "weight": 2},
|
||||||
|
{"name": "InterPodAffinityPriority", "weight": 2},
|
||||||
|
{"name": "MostRequestedPriority", "weight": 2}
|
||||||
|
]
|
||||||
|
}`,
|
||||||
|
ExpectedPolicy: schedulerapi.Policy{
|
||||||
|
Predicates: []schedulerapi.PredicatePolicy{
|
||||||
|
{Name: "MatchNodeSelector"},
|
||||||
|
{Name: "PodFitsResources"},
|
||||||
|
{Name: "PodFitsHostPorts"},
|
||||||
|
{Name: "HostName"},
|
||||||
|
{Name: "NoDiskConflict"},
|
||||||
|
{Name: "NoVolumeZoneConflict"},
|
||||||
|
{Name: "PodToleratesNodeTaints"},
|
||||||
|
{Name: "CheckNodeMemoryPressure"},
|
||||||
|
{Name: "CheckNodeDiskPressure"},
|
||||||
|
{Name: "CheckNodeCondition"},
|
||||||
|
{Name: "MaxEBSVolumeCount"},
|
||||||
|
{Name: "MaxGCEPDVolumeCount"},
|
||||||
|
{Name: "MaxAzureDiskVolumeCount"},
|
||||||
|
{Name: "MatchInterPodAffinity"},
|
||||||
|
{Name: "GeneralPredicates"},
|
||||||
|
{Name: "TestServiceAffinity", Argument: &schedulerapi.PredicateArgument{ServiceAffinity: &schedulerapi.ServiceAffinity{Labels: []string{"region"}}}},
|
||||||
|
{Name: "TestLabelsPresence", Argument: &schedulerapi.PredicateArgument{LabelsPresence: &schedulerapi.LabelsPresence{Labels: []string{"foo"}, Presence: true}}},
|
||||||
|
{Name: "NoVolumeNodeConflict"},
|
||||||
|
},
|
||||||
|
Priorities: []schedulerapi.PriorityPolicy{
|
||||||
|
{Name: "EqualPriority", Weight: 2},
|
||||||
|
{Name: "ImageLocalityPriority", Weight: 2},
|
||||||
|
{Name: "LeastRequestedPriority", Weight: 2},
|
||||||
|
{Name: "BalancedResourceAllocation", Weight: 2},
|
||||||
|
{Name: "SelectorSpreadPriority", Weight: 2},
|
||||||
|
{Name: "NodePreferAvoidPodsPriority", Weight: 2},
|
||||||
|
{Name: "NodeAffinityPriority", Weight: 2},
|
||||||
|
{Name: "TaintTolerationPriority", Weight: 2},
|
||||||
|
{Name: "InterPodAffinityPriority", Weight: 2},
|
||||||
|
{Name: "MostRequestedPriority", Weight: 2},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
registeredPredicates := sets.NewString(factory.ListRegisteredFitPredicates()...)
|
registeredPredicates := sets.NewString(factory.ListRegisteredFitPredicates()...)
|
||||||
|
|
|
@ -175,6 +175,10 @@ func defaultPredicates() sets.String {
|
||||||
// Fit is determined by node disk pressure condition.
|
// Fit is determined by node disk pressure condition.
|
||||||
factory.RegisterFitPredicate("CheckNodeDiskPressure", predicates.CheckNodeDiskPressurePredicate),
|
factory.RegisterFitPredicate("CheckNodeDiskPressure", predicates.CheckNodeDiskPressurePredicate),
|
||||||
|
|
||||||
|
// Fit is determied by node condtions: not ready, network unavailable and out of disk.
|
||||||
|
factory.RegisterFitPredicate("CheckNodeCondition", predicates.CheckNodeConditionPredicate),
|
||||||
|
factory.RegisterMandatoryFitPredicate("CheckNodeCondition", predicates.CheckNodeConditionPredicate),
|
||||||
|
|
||||||
// Fit is determined by volume zone requirements.
|
// Fit is determined by volume zone requirements.
|
||||||
factory.RegisterFitPredicateFactory(
|
factory.RegisterFitPredicateFactory(
|
||||||
"NoVolumeNodeConflict",
|
"NoVolumeNodeConflict",
|
||||||
|
|
|
@ -705,7 +705,7 @@ func (f *ConfigFactory) CreateFromKeys(predicateKeys, priorityKeys sets.String,
|
||||||
SchedulerCache: f.schedulerCache,
|
SchedulerCache: f.schedulerCache,
|
||||||
Ecache: f.equivalencePodCache,
|
Ecache: f.equivalencePodCache,
|
||||||
// The scheduler only needs to consider schedulable nodes.
|
// The scheduler only needs to consider schedulable nodes.
|
||||||
NodeLister: &nodePredicateLister{f.nodeLister},
|
NodeLister: &nodeLister{f.nodeLister},
|
||||||
Algorithm: algo,
|
Algorithm: algo,
|
||||||
Binder: f.getBinder(extenders),
|
Binder: f.getBinder(extenders),
|
||||||
PodConditionUpdater: &podConditionUpdater{f.client},
|
PodConditionUpdater: &podConditionUpdater{f.client},
|
||||||
|
@ -720,12 +720,12 @@ func (f *ConfigFactory) CreateFromKeys(predicateKeys, priorityKeys sets.String,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type nodePredicateLister struct {
|
type nodeLister struct {
|
||||||
corelisters.NodeLister
|
corelisters.NodeLister
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *nodePredicateLister) List() ([]*v1.Node, error) {
|
func (n *nodeLister) List() ([]*v1.Node, error) {
|
||||||
return n.ListWithPredicate(getNodeConditionPredicate())
|
return n.NodeLister.List(labels.Everything())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *ConfigFactory) GetPriorityFunctionConfigs(priorityKeys sets.String) ([]algorithm.PriorityConfig, error) {
|
func (f *ConfigFactory) GetPriorityFunctionConfigs(priorityKeys sets.String) ([]algorithm.PriorityConfig, error) {
|
||||||
|
@ -770,11 +770,10 @@ func (f *ConfigFactory) getPluginArgs() (*PluginFactoryArgs, error) {
|
||||||
ControllerLister: f.controllerLister,
|
ControllerLister: f.controllerLister,
|
||||||
ReplicaSetLister: f.replicaSetLister,
|
ReplicaSetLister: f.replicaSetLister,
|
||||||
StatefulSetLister: f.statefulSetLister,
|
StatefulSetLister: f.statefulSetLister,
|
||||||
// All fit predicates only need to consider schedulable nodes.
|
NodeLister: &nodeLister{f.nodeLister},
|
||||||
NodeLister: &nodePredicateLister{f.nodeLister},
|
NodeInfo: &predicates.CachedNodeInfo{NodeLister: f.nodeLister},
|
||||||
NodeInfo: &predicates.CachedNodeInfo{NodeLister: f.nodeLister},
|
PVInfo: &predicates.CachedPersistentVolumeInfo{PersistentVolumeLister: f.pVLister},
|
||||||
PVInfo: &predicates.CachedPersistentVolumeInfo{PersistentVolumeLister: f.pVLister},
|
PVCInfo: &predicates.CachedPersistentVolumeClaimInfo{PersistentVolumeClaimLister: f.pVCLister},
|
||||||
PVCInfo: &predicates.CachedPersistentVolumeClaimInfo{PersistentVolumeClaimLister: f.pVCLister},
|
|
||||||
HardPodAffinitySymmetricWeight: f.hardPodAffinitySymmetricWeight,
|
HardPodAffinitySymmetricWeight: f.hardPodAffinitySymmetricWeight,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
@ -793,34 +792,6 @@ func (f *ConfigFactory) ResponsibleForPod(pod *v1.Pod) bool {
|
||||||
return f.schedulerName == pod.Spec.SchedulerName
|
return f.schedulerName == pod.Spec.SchedulerName
|
||||||
}
|
}
|
||||||
|
|
||||||
func getNodeConditionPredicate() corelisters.NodeConditionPredicate {
|
|
||||||
return func(node *v1.Node) bool {
|
|
||||||
for i := range node.Status.Conditions {
|
|
||||||
cond := &node.Status.Conditions[i]
|
|
||||||
// We consider the node for scheduling only when its:
|
|
||||||
// - NodeReady condition status is ConditionTrue,
|
|
||||||
// - NodeOutOfDisk condition status is ConditionFalse,
|
|
||||||
// - NodeNetworkUnavailable condition status is ConditionFalse.
|
|
||||||
if cond.Type == v1.NodeReady && cond.Status != v1.ConditionTrue {
|
|
||||||
glog.V(4).Infof("Ignoring node %v with %v condition status %v", node.Name, cond.Type, cond.Status)
|
|
||||||
return false
|
|
||||||
} else if cond.Type == v1.NodeOutOfDisk && cond.Status != v1.ConditionFalse {
|
|
||||||
glog.V(4).Infof("Ignoring node %v with %v condition status %v", node.Name, cond.Type, cond.Status)
|
|
||||||
return false
|
|
||||||
} else if cond.Type == v1.NodeNetworkUnavailable && cond.Status != v1.ConditionFalse {
|
|
||||||
glog.V(4).Infof("Ignoring node %v with %v condition status %v", node.Name, cond.Type, cond.Status)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Ignore nodes that are marked unschedulable
|
|
||||||
if node.Spec.Unschedulable {
|
|
||||||
glog.V(4).Infof("Ignoring node %v since it is unschedulable", node.Name)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// unassignedNonTerminatedPod selects pods that are unassigned and non-terminal.
|
// unassignedNonTerminatedPod selects pods that are unassigned and non-terminal.
|
||||||
func unassignedNonTerminatedPod(pod *v1.Pod) bool {
|
func unassignedNonTerminatedPod(pod *v1.Pod) bool {
|
||||||
if len(pod.Spec.NodeName) != 0 {
|
if len(pod.Spec.NodeName) != 0 {
|
||||||
|
|
|
@ -525,46 +525,3 @@ func TestInvalidFactoryArgs(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNodeConditionPredicate(t *testing.T) {
|
|
||||||
nodeFunc := getNodeConditionPredicate()
|
|
||||||
nodeList := &v1.NodeList{
|
|
||||||
Items: []v1.Node{
|
|
||||||
// node1 considered
|
|
||||||
{ObjectMeta: metav1.ObjectMeta{Name: "node1"}, Status: v1.NodeStatus{Conditions: []v1.NodeCondition{{Type: v1.NodeReady, Status: v1.ConditionTrue}}}},
|
|
||||||
// node2 ignored - node not Ready
|
|
||||||
{ObjectMeta: metav1.ObjectMeta{Name: "node2"}, Status: v1.NodeStatus{Conditions: []v1.NodeCondition{{Type: v1.NodeReady, Status: v1.ConditionFalse}}}},
|
|
||||||
// node3 ignored - node out of disk
|
|
||||||
{ObjectMeta: metav1.ObjectMeta{Name: "node3"}, Status: v1.NodeStatus{Conditions: []v1.NodeCondition{{Type: v1.NodeOutOfDisk, Status: v1.ConditionTrue}}}},
|
|
||||||
// node4 considered
|
|
||||||
{ObjectMeta: metav1.ObjectMeta{Name: "node4"}, Status: v1.NodeStatus{Conditions: []v1.NodeCondition{{Type: v1.NodeOutOfDisk, Status: v1.ConditionFalse}}}},
|
|
||||||
|
|
||||||
// node5 ignored - node out of disk
|
|
||||||
{ObjectMeta: metav1.ObjectMeta{Name: "node5"}, Status: v1.NodeStatus{Conditions: []v1.NodeCondition{{Type: v1.NodeReady, Status: v1.ConditionTrue}, {Type: v1.NodeOutOfDisk, Status: v1.ConditionTrue}}}},
|
|
||||||
// node6 considered
|
|
||||||
{ObjectMeta: metav1.ObjectMeta{Name: "node6"}, Status: v1.NodeStatus{Conditions: []v1.NodeCondition{{Type: v1.NodeReady, Status: v1.ConditionTrue}, {Type: v1.NodeOutOfDisk, Status: v1.ConditionFalse}}}},
|
|
||||||
// node7 ignored - node out of disk, node not Ready
|
|
||||||
{ObjectMeta: metav1.ObjectMeta{Name: "node7"}, Status: v1.NodeStatus{Conditions: []v1.NodeCondition{{Type: v1.NodeReady, Status: v1.ConditionFalse}, {Type: v1.NodeOutOfDisk, Status: v1.ConditionTrue}}}},
|
|
||||||
// node8 ignored - node not Ready
|
|
||||||
{ObjectMeta: metav1.ObjectMeta{Name: "node8"}, Status: v1.NodeStatus{Conditions: []v1.NodeCondition{{Type: v1.NodeReady, Status: v1.ConditionFalse}, {Type: v1.NodeOutOfDisk, Status: v1.ConditionFalse}}}},
|
|
||||||
|
|
||||||
// node9 ignored - node unschedulable
|
|
||||||
{ObjectMeta: metav1.ObjectMeta{Name: "node9"}, Spec: v1.NodeSpec{Unschedulable: true}},
|
|
||||||
// node10 considered
|
|
||||||
{ObjectMeta: metav1.ObjectMeta{Name: "node10"}, Spec: v1.NodeSpec{Unschedulable: false}},
|
|
||||||
// node11 considered
|
|
||||||
{ObjectMeta: metav1.ObjectMeta{Name: "node11"}},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
nodeNames := []string{}
|
|
||||||
for _, node := range nodeList.Items {
|
|
||||||
if nodeFunc(&node) {
|
|
||||||
nodeNames = append(nodeNames, node.Name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
expectedNodes := []string{"node1", "node4", "node6", "node10", "node11"}
|
|
||||||
if !reflect.DeepEqual(expectedNodes, nodeNames) {
|
|
||||||
t.Errorf("expected: %v, got %v", expectedNodes, nodeNames)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -72,9 +72,10 @@ var (
|
||||||
schedulerFactoryMutex sync.Mutex
|
schedulerFactoryMutex sync.Mutex
|
||||||
|
|
||||||
// maps that hold registered algorithm types
|
// maps that hold registered algorithm types
|
||||||
fitPredicateMap = make(map[string]FitPredicateFactory)
|
fitPredicateMap = make(map[string]FitPredicateFactory)
|
||||||
priorityFunctionMap = make(map[string]PriorityConfigFactory)
|
mandatoryFitPredicateMap = make(map[string]FitPredicateFactory)
|
||||||
algorithmProviderMap = make(map[string]AlgorithmProviderConfig)
|
priorityFunctionMap = make(map[string]PriorityConfigFactory)
|
||||||
|
algorithmProviderMap = make(map[string]AlgorithmProviderConfig)
|
||||||
|
|
||||||
// Registered metadata producers
|
// Registered metadata producers
|
||||||
priorityMetadataProducer MetadataProducerFactory
|
priorityMetadataProducer MetadataProducerFactory
|
||||||
|
@ -99,6 +100,17 @@ func RegisterFitPredicate(name string, predicate algorithm.FitPredicate) string
|
||||||
return RegisterFitPredicateFactory(name, func(PluginFactoryArgs) algorithm.FitPredicate { return predicate })
|
return RegisterFitPredicateFactory(name, func(PluginFactoryArgs) algorithm.FitPredicate { return predicate })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RegisterMandatoryFitPredicate registers a fit predicate with the algorithm registry, the predicate is used by
|
||||||
|
// kubelet, DaemonSet; it is always included in configuration. Returns the name with which the predicate was
|
||||||
|
// registered.
|
||||||
|
func RegisterMandatoryFitPredicate(name string, predicate algorithm.FitPredicate) string {
|
||||||
|
schedulerFactoryMutex.Lock()
|
||||||
|
defer schedulerFactoryMutex.Unlock()
|
||||||
|
validateAlgorithmNameOrDie(name)
|
||||||
|
mandatoryFitPredicateMap[name] = func(PluginFactoryArgs) algorithm.FitPredicate { return predicate }
|
||||||
|
return name
|
||||||
|
}
|
||||||
|
|
||||||
// RegisterFitPredicateFactory registers a fit predicate factory with the
|
// RegisterFitPredicateFactory registers a fit predicate factory with the
|
||||||
// algorithm registry. Returns the name with which the predicate was registered.
|
// algorithm registry. Returns the name with which the predicate was registered.
|
||||||
func RegisterFitPredicateFactory(name string, predicateFactory FitPredicateFactory) string {
|
func RegisterFitPredicateFactory(name string, predicateFactory FitPredicateFactory) string {
|
||||||
|
@ -308,6 +320,14 @@ func getFitPredicateFunctions(names sets.String, args PluginFactoryArgs) (map[st
|
||||||
}
|
}
|
||||||
predicates[name] = factory(args)
|
predicates[name] = factory(args)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Always include required fit predicates.
|
||||||
|
for name, factory := range mandatoryFitPredicateMap {
|
||||||
|
if _, found := predicates[name]; !found {
|
||||||
|
predicates[name] = factory(args)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return predicates, nil
|
return predicates, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -139,7 +139,8 @@ func TestSchedulerCreationFromConfigMap(t *testing.T) {
|
||||||
// Verify that the config is applied correctly.
|
// Verify that the config is applied correctly.
|
||||||
schedPredicates := sched.Config().Algorithm.Predicates()
|
schedPredicates := sched.Config().Algorithm.Predicates()
|
||||||
schedPrioritizers := sched.Config().Algorithm.Prioritizers()
|
schedPrioritizers := sched.Config().Algorithm.Prioritizers()
|
||||||
if len(schedPredicates) != 2 || len(schedPrioritizers) != 2 {
|
// Includes one mandatory predicates.
|
||||||
|
if len(schedPredicates) != 3 || len(schedPrioritizers) != 2 {
|
||||||
t.Errorf("Unexpected number of predicates or priority functions. Number of predicates: %v, number of prioritizers: %v", len(schedPredicates), len(schedPrioritizers))
|
t.Errorf("Unexpected number of predicates or priority functions. Number of predicates: %v, number of prioritizers: %v", len(schedPredicates), len(schedPrioritizers))
|
||||||
}
|
}
|
||||||
// Check a predicate and a priority function.
|
// Check a predicate and a priority function.
|
||||||
|
|
Loading…
Reference in New Issue