mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-29 06:27:05 +00:00
Merge pull request #92866 from cofyc/fix91281
scheduler: Extend ExtenderFilterResult to include UnschedulableAndUnresolvable nodes
This commit is contained in:
commit
8096513aca
@ -269,11 +269,13 @@ func convertToNodeNameToMetaVictims(
|
|||||||
|
|
||||||
// Filter based on extender implemented predicate functions. The filtered list is
|
// Filter based on extender implemented predicate functions. The filtered list is
|
||||||
// expected to be a subset of the supplied list; otherwise the function returns an error.
|
// expected to be a subset of the supplied list; otherwise the function returns an error.
|
||||||
// failedNodesMap optionally contains the list of failed nodes and failure reasons.
|
// The failedNodes and failedAndUnresolvableNodes optionally contains the list
|
||||||
|
// of failed nodes and failure reasons, except nodes in the latter are
|
||||||
|
// unresolvable.
|
||||||
func (h *HTTPExtender) Filter(
|
func (h *HTTPExtender) Filter(
|
||||||
pod *v1.Pod,
|
pod *v1.Pod,
|
||||||
nodes []*v1.Node,
|
nodes []*v1.Node,
|
||||||
) ([]*v1.Node, extenderv1.FailedNodesMap, error) {
|
) (filteredList []*v1.Node, failedNodes, failedAndUnresolvableNodes extenderv1.FailedNodesMap, err error) {
|
||||||
var (
|
var (
|
||||||
result extenderv1.ExtenderFilterResult
|
result extenderv1.ExtenderFilterResult
|
||||||
nodeList *v1.NodeList
|
nodeList *v1.NodeList
|
||||||
@ -287,7 +289,7 @@ func (h *HTTPExtender) Filter(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if h.filterVerb == "" {
|
if h.filterVerb == "" {
|
||||||
return nodes, extenderv1.FailedNodesMap{}, nil
|
return nodes, extenderv1.FailedNodesMap{}, extenderv1.FailedNodesMap{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if h.nodeCacheCapable {
|
if h.nodeCacheCapable {
|
||||||
@ -310,10 +312,10 @@ func (h *HTTPExtender) Filter(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if err := h.send(h.filterVerb, args, &result); err != nil {
|
if err := h.send(h.filterVerb, args, &result); err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, nil, err
|
||||||
}
|
}
|
||||||
if result.Error != "" {
|
if result.Error != "" {
|
||||||
return nil, nil, fmt.Errorf(result.Error)
|
return nil, nil, nil, fmt.Errorf(result.Error)
|
||||||
}
|
}
|
||||||
|
|
||||||
if h.nodeCacheCapable && result.NodeNames != nil {
|
if h.nodeCacheCapable && result.NodeNames != nil {
|
||||||
@ -322,7 +324,7 @@ func (h *HTTPExtender) Filter(
|
|||||||
if n, ok := fromNodeName[nodeName]; ok {
|
if n, ok := fromNodeName[nodeName]; ok {
|
||||||
nodeResult[i] = n
|
nodeResult[i] = n
|
||||||
} else {
|
} else {
|
||||||
return nil, nil, fmt.Errorf(
|
return nil, nil, nil, fmt.Errorf(
|
||||||
"extender %q claims a filtered node %q which is not found in the input node list",
|
"extender %q claims a filtered node %q which is not found in the input node list",
|
||||||
h.extenderURL, nodeName)
|
h.extenderURL, nodeName)
|
||||||
}
|
}
|
||||||
@ -334,7 +336,7 @@ func (h *HTTPExtender) Filter(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nodeResult, result.FailedNodes, nil
|
return nodeResult, result.FailedNodes, result.FailedAndUnresolvableNodes, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prioritize based on extender implemented priority functions. Weight*priority is added
|
// Prioritize based on extender implemented priority functions. Weight*priority is added
|
||||||
|
@ -345,6 +345,9 @@ func (g *genericScheduler) findNodesThatPassFilters(
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (g *genericScheduler) findNodesThatPassExtenders(pod *v1.Pod, feasibleNodes []*v1.Node, statuses framework.NodeToStatusMap) ([]*v1.Node, error) {
|
func (g *genericScheduler) findNodesThatPassExtenders(pod *v1.Pod, feasibleNodes []*v1.Node, statuses framework.NodeToStatusMap) ([]*v1.Node, error) {
|
||||||
|
// Extenders are called sequentially.
|
||||||
|
// Nodes in original feasibleNodes can be excluded in one extender, and pass on to the next
|
||||||
|
// extender in a decreasing manner.
|
||||||
for _, extender := range g.extenders {
|
for _, extender := range g.extenders {
|
||||||
if len(feasibleNodes) == 0 {
|
if len(feasibleNodes) == 0 {
|
||||||
break
|
break
|
||||||
@ -352,7 +355,13 @@ func (g *genericScheduler) findNodesThatPassExtenders(pod *v1.Pod, feasibleNodes
|
|||||||
if !extender.IsInterested(pod) {
|
if !extender.IsInterested(pod) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
feasibleList, failedMap, err := extender.Filter(pod, feasibleNodes)
|
|
||||||
|
// Status of failed nodes in failedAndUnresolvableMap will be added or overwritten in <statuses>,
|
||||||
|
// so that the scheduler framework can respect the UnschedulableAndUnresolvable status for
|
||||||
|
// particular nodes, and this may eventually improve preemption efficiency.
|
||||||
|
// Note: users are recommended to configure the extenders that may return UnschedulableAndUnresolvable
|
||||||
|
// status ahead of others.
|
||||||
|
feasibleList, failedMap, failedAndUnresolvableMap, err := extender.Filter(pod, feasibleNodes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if extender.IsIgnorable() {
|
if extender.IsIgnorable() {
|
||||||
klog.InfoS("Skipping extender as it returned error and has ignorable flag set", "extender", extender, "err", err)
|
klog.InfoS("Skipping extender as it returned error and has ignorable flag set", "extender", extender, "err", err)
|
||||||
@ -361,13 +370,28 @@ func (g *genericScheduler) findNodesThatPassExtenders(pod *v1.Pod, feasibleNodes
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for failedNodeName, failedMsg := range failedAndUnresolvableMap {
|
||||||
|
var aggregatedReasons []string
|
||||||
|
if _, found := statuses[failedNodeName]; found {
|
||||||
|
aggregatedReasons = statuses[failedNodeName].Reasons()
|
||||||
|
}
|
||||||
|
aggregatedReasons = append(aggregatedReasons, failedMsg)
|
||||||
|
statuses[failedNodeName] = framework.NewStatus(framework.UnschedulableAndUnresolvable, aggregatedReasons...)
|
||||||
|
}
|
||||||
|
|
||||||
for failedNodeName, failedMsg := range failedMap {
|
for failedNodeName, failedMsg := range failedMap {
|
||||||
|
if _, found := failedAndUnresolvableMap[failedNodeName]; found {
|
||||||
|
// failedAndUnresolvableMap takes precedence over failedMap
|
||||||
|
// note that this only happens if the extender returns the node in both maps
|
||||||
|
continue
|
||||||
|
}
|
||||||
if _, found := statuses[failedNodeName]; !found {
|
if _, found := statuses[failedNodeName]; !found {
|
||||||
statuses[failedNodeName] = framework.NewStatus(framework.Unschedulable, failedMsg)
|
statuses[failedNodeName] = framework.NewStatus(framework.Unschedulable, failedMsg)
|
||||||
} else {
|
} else {
|
||||||
statuses[failedNodeName].AppendReason(failedMsg)
|
statuses[failedNodeName].AppendReason(failedMsg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
feasibleNodes = feasibleList
|
feasibleNodes = feasibleList
|
||||||
}
|
}
|
||||||
return feasibleNodes, nil
|
return feasibleNodes, nil
|
||||||
|
@ -273,6 +273,180 @@ func TestSelectHost(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestFindNodesThatPassExtenders(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
extenders []st.FakeExtender
|
||||||
|
nodes []*v1.Node
|
||||||
|
filteredNodesStatuses framework.NodeToStatusMap
|
||||||
|
expectsErr bool
|
||||||
|
expectedNodes []*v1.Node
|
||||||
|
expectedStatuses framework.NodeToStatusMap
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "error",
|
||||||
|
extenders: []st.FakeExtender{
|
||||||
|
{
|
||||||
|
Predicates: []st.FitPredicate{st.ErrorPredicateExtender},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
nodes: makeNodeList([]string{"a"}),
|
||||||
|
filteredNodesStatuses: make(framework.NodeToStatusMap),
|
||||||
|
expectsErr: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "success",
|
||||||
|
extenders: []st.FakeExtender{
|
||||||
|
{
|
||||||
|
Predicates: []st.FitPredicate{st.TruePredicateExtender},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
nodes: makeNodeList([]string{"a"}),
|
||||||
|
filteredNodesStatuses: make(framework.NodeToStatusMap),
|
||||||
|
expectsErr: false,
|
||||||
|
expectedNodes: makeNodeList([]string{"a"}),
|
||||||
|
expectedStatuses: make(framework.NodeToStatusMap),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "unschedulable",
|
||||||
|
extenders: []st.FakeExtender{
|
||||||
|
{
|
||||||
|
Predicates: []st.FitPredicate{func(pod *v1.Pod, node *v1.Node) *framework.Status {
|
||||||
|
if node.Name == "a" {
|
||||||
|
return framework.NewStatus(framework.Success)
|
||||||
|
}
|
||||||
|
return framework.NewStatus(framework.Unschedulable, fmt.Sprintf("node %q is not allowed", node.Name))
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
nodes: makeNodeList([]string{"a", "b"}),
|
||||||
|
filteredNodesStatuses: make(framework.NodeToStatusMap),
|
||||||
|
expectsErr: false,
|
||||||
|
expectedNodes: makeNodeList([]string{"a"}),
|
||||||
|
expectedStatuses: framework.NodeToStatusMap{
|
||||||
|
"b": framework.NewStatus(framework.Unschedulable, fmt.Sprintf("FakeExtender: node %q failed", "b")),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "unschedulable and unresolvable",
|
||||||
|
extenders: []st.FakeExtender{
|
||||||
|
{
|
||||||
|
Predicates: []st.FitPredicate{func(pod *v1.Pod, node *v1.Node) *framework.Status {
|
||||||
|
if node.Name == "a" {
|
||||||
|
return framework.NewStatus(framework.Success)
|
||||||
|
}
|
||||||
|
if node.Name == "b" {
|
||||||
|
return framework.NewStatus(framework.Unschedulable, fmt.Sprintf("node %q is not allowed", node.Name))
|
||||||
|
}
|
||||||
|
return framework.NewStatus(framework.UnschedulableAndUnresolvable, fmt.Sprintf("node %q is not allowed", node.Name))
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
nodes: makeNodeList([]string{"a", "b", "c"}),
|
||||||
|
filteredNodesStatuses: make(framework.NodeToStatusMap),
|
||||||
|
expectsErr: false,
|
||||||
|
expectedNodes: makeNodeList([]string{"a"}),
|
||||||
|
expectedStatuses: framework.NodeToStatusMap{
|
||||||
|
"b": framework.NewStatus(framework.Unschedulable, fmt.Sprintf("FakeExtender: node %q failed", "b")),
|
||||||
|
"c": framework.NewStatus(framework.UnschedulableAndUnresolvable, fmt.Sprintf("FakeExtender: node %q failed and unresolvable", "c")),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "extender may overwrite the statuses",
|
||||||
|
extenders: []st.FakeExtender{
|
||||||
|
{
|
||||||
|
Predicates: []st.FitPredicate{func(pod *v1.Pod, node *v1.Node) *framework.Status {
|
||||||
|
if node.Name == "a" {
|
||||||
|
return framework.NewStatus(framework.Success)
|
||||||
|
}
|
||||||
|
if node.Name == "b" {
|
||||||
|
return framework.NewStatus(framework.Unschedulable, fmt.Sprintf("node %q is not allowed", node.Name))
|
||||||
|
}
|
||||||
|
return framework.NewStatus(framework.UnschedulableAndUnresolvable, fmt.Sprintf("node %q is not allowed", node.Name))
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
nodes: makeNodeList([]string{"a", "b", "c"}),
|
||||||
|
filteredNodesStatuses: framework.NodeToStatusMap{
|
||||||
|
"c": framework.NewStatus(framework.Unschedulable, fmt.Sprintf("FakeFilterPlugin: node %q failed", "c")),
|
||||||
|
},
|
||||||
|
expectsErr: false,
|
||||||
|
expectedNodes: makeNodeList([]string{"a"}),
|
||||||
|
expectedStatuses: framework.NodeToStatusMap{
|
||||||
|
"b": framework.NewStatus(framework.Unschedulable, fmt.Sprintf("FakeExtender: node %q failed", "b")),
|
||||||
|
"c": framework.NewStatus(framework.UnschedulableAndUnresolvable, fmt.Sprintf("FakeFilterPlugin: node %q failed", "c"), fmt.Sprintf("FakeExtender: node %q failed and unresolvable", "c")),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "multiple extenders",
|
||||||
|
extenders: []st.FakeExtender{
|
||||||
|
{
|
||||||
|
Predicates: []st.FitPredicate{func(pod *v1.Pod, node *v1.Node) *framework.Status {
|
||||||
|
if node.Name == "a" {
|
||||||
|
return framework.NewStatus(framework.Success)
|
||||||
|
}
|
||||||
|
if node.Name == "b" {
|
||||||
|
return framework.NewStatus(framework.Unschedulable, fmt.Sprintf("node %q is not allowed", node.Name))
|
||||||
|
}
|
||||||
|
return framework.NewStatus(framework.UnschedulableAndUnresolvable, fmt.Sprintf("node %q is not allowed", node.Name))
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Predicates: []st.FitPredicate{func(pod *v1.Pod, node *v1.Node) *framework.Status {
|
||||||
|
if node.Name == "a" {
|
||||||
|
return framework.NewStatus(framework.Success)
|
||||||
|
}
|
||||||
|
return framework.NewStatus(framework.Unschedulable, fmt.Sprintf("node %q is not allowed", node.Name))
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
nodes: makeNodeList([]string{"a", "b", "c"}),
|
||||||
|
filteredNodesStatuses: make(framework.NodeToStatusMap),
|
||||||
|
expectsErr: false,
|
||||||
|
expectedNodes: makeNodeList([]string{"a"}),
|
||||||
|
expectedStatuses: framework.NodeToStatusMap{
|
||||||
|
"b": framework.NewStatus(framework.Unschedulable, fmt.Sprintf("FakeExtender: node %q failed", "b")),
|
||||||
|
"c": framework.NewStatus(framework.UnschedulableAndUnresolvable, fmt.Sprintf("FakeExtender: node %q failed and unresolvable", "c")),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
cmpOpts := []cmp.Option{
|
||||||
|
cmp.Comparer(func(s1 framework.Status, s2 framework.Status) bool {
|
||||||
|
return s1.Code() == s2.Code() && reflect.DeepEqual(s1.Reasons(), s2.Reasons())
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
var extenders []framework.Extender
|
||||||
|
for ii := range tt.extenders {
|
||||||
|
extenders = append(extenders, &tt.extenders[ii])
|
||||||
|
}
|
||||||
|
scheduler := &genericScheduler{
|
||||||
|
extenders: extenders,
|
||||||
|
}
|
||||||
|
pod := &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "1", UID: types.UID("1")}}
|
||||||
|
got, err := scheduler.findNodesThatPassExtenders(pod, tt.nodes, tt.filteredNodesStatuses)
|
||||||
|
if tt.expectsErr {
|
||||||
|
if err == nil {
|
||||||
|
t.Error("Unexpected non-error")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
if diff := cmp.Diff(tt.expectedNodes, got); diff != "" {
|
||||||
|
t.Errorf("filtered nodes (-want,+got):\n%s", diff)
|
||||||
|
}
|
||||||
|
if diff := cmp.Diff(tt.expectedStatuses, tt.filteredNodesStatuses, cmpOpts...); diff != "" {
|
||||||
|
t.Errorf("filtered statuses (-want,+got):\n%s", diff)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestGenericScheduler(t *testing.T) {
|
func TestGenericScheduler(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
|
@ -674,8 +674,8 @@ func (f *fakeExtender) SupportsPreemption() bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *fakeExtender) Filter(pod *v1.Pod, nodes []*v1.Node) (filteredNodes []*v1.Node, failedNodesMap extenderv1.FailedNodesMap, err error) {
|
func (f *fakeExtender) Filter(pod *v1.Pod, nodes []*v1.Node) ([]*v1.Node, extenderv1.FailedNodesMap, extenderv1.FailedNodesMap, error) {
|
||||||
return nil, nil, nil
|
return nil, nil, nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *fakeExtender) Prioritize(
|
func (f *fakeExtender) Prioritize(
|
||||||
|
@ -29,9 +29,11 @@ type Extender interface {
|
|||||||
Name() string
|
Name() string
|
||||||
|
|
||||||
// Filter based on extender-implemented predicate functions. The filtered list is
|
// Filter based on extender-implemented predicate functions. The filtered list is
|
||||||
// expected to be a subset of the supplied list. failedNodesMap optionally contains
|
// expected to be a subset of the supplied list.
|
||||||
// the list of failed nodes and failure reasons.
|
// The failedNodes and failedAndUnresolvableNodes optionally contains the list
|
||||||
Filter(pod *v1.Pod, nodes []*v1.Node) (filteredNodes []*v1.Node, failedNodesMap extenderv1.FailedNodesMap, err error)
|
// of failed nodes and failure reasons, except nodes in the latter are
|
||||||
|
// unresolvable.
|
||||||
|
Filter(pod *v1.Pod, nodes []*v1.Node) (filteredNodes []*v1.Node, failedNodesMap extenderv1.FailedNodesMap, failedAndUnresolvable extenderv1.FailedNodesMap, err error)
|
||||||
|
|
||||||
// Prioritize based on extender-implemented priority functions. The returned scores & weight
|
// Prioritize based on extender-implemented priority functions. The returned scores & weight
|
||||||
// are used to compute the weighted score for an extender. The weighted scores are added to
|
// are used to compute the weighted score for an extender. The weighted scores are added to
|
||||||
|
@ -31,7 +31,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// FitPredicate is a function type which is used in fake extender.
|
// FitPredicate is a function type which is used in fake extender.
|
||||||
type FitPredicate func(pod *v1.Pod, node *v1.Node) (bool, error)
|
type FitPredicate func(pod *v1.Pod, node *v1.Node) *framework.Status
|
||||||
|
|
||||||
// PriorityFunc is a function type which is used in fake extender.
|
// PriorityFunc is a function type which is used in fake extender.
|
||||||
type PriorityFunc func(pod *v1.Pod, nodes []*v1.Node) (*framework.NodeScoreList, error)
|
type PriorityFunc func(pod *v1.Pod, nodes []*v1.Node) (*framework.NodeScoreList, error)
|
||||||
@ -42,37 +42,42 @@ type PriorityConfig struct {
|
|||||||
Weight int64
|
Weight int64
|
||||||
}
|
}
|
||||||
|
|
||||||
// ErrorPredicateExtender implements FitPredicate function to always return error.
|
// ErrorPredicateExtender implements FitPredicate function to always return error status.
|
||||||
func ErrorPredicateExtender(pod *v1.Pod, node *v1.Node) (bool, error) {
|
func ErrorPredicateExtender(pod *v1.Pod, node *v1.Node) *framework.Status {
|
||||||
return false, fmt.Errorf("some error")
|
return framework.NewStatus(framework.Error, "some error")
|
||||||
}
|
}
|
||||||
|
|
||||||
// FalsePredicateExtender implements FitPredicate function to always return false.
|
// FalsePredicateExtender implements FitPredicate function to always return unschedulable status.
|
||||||
func FalsePredicateExtender(pod *v1.Pod, node *v1.Node) (bool, error) {
|
func FalsePredicateExtender(pod *v1.Pod, node *v1.Node) *framework.Status {
|
||||||
return false, nil
|
return framework.NewStatus(framework.Unschedulable, fmt.Sprintf("pod is unschedulable on the node %q", node.Name))
|
||||||
}
|
}
|
||||||
|
|
||||||
// TruePredicateExtender implements FitPredicate function to always return true.
|
// FalseAndUnresolvePredicateExtender implements fitPredicate to always return unschedulable and unresolvable status.
|
||||||
func TruePredicateExtender(pod *v1.Pod, node *v1.Node) (bool, error) {
|
func FalseAndUnresolvePredicateExtender(pod *v1.Pod, node *v1.Node) *framework.Status {
|
||||||
return true, nil
|
return framework.NewStatus(framework.UnschedulableAndUnresolvable, fmt.Sprintf("pod is unschedulable and unresolvable on the node %q", node.Name))
|
||||||
|
}
|
||||||
|
|
||||||
|
// TruePredicateExtender implements FitPredicate function to always return success status.
|
||||||
|
func TruePredicateExtender(pod *v1.Pod, node *v1.Node) *framework.Status {
|
||||||
|
return framework.NewStatus(framework.Success)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Node1PredicateExtender implements FitPredicate function to return true
|
// Node1PredicateExtender implements FitPredicate function to return true
|
||||||
// when the given node's name is "node1"; otherwise return false.
|
// when the given node's name is "node1"; otherwise return false.
|
||||||
func Node1PredicateExtender(pod *v1.Pod, node *v1.Node) (bool, error) {
|
func Node1PredicateExtender(pod *v1.Pod, node *v1.Node) *framework.Status {
|
||||||
if node.Name == "node1" {
|
if node.Name == "node1" {
|
||||||
return true, nil
|
return framework.NewStatus(framework.Success)
|
||||||
}
|
}
|
||||||
return false, nil
|
return framework.NewStatus(framework.Unschedulable, fmt.Sprintf("node %q is not allowed", node.Name))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Node2PredicateExtender implements FitPredicate function to return true
|
// Node2PredicateExtender implements FitPredicate function to return true
|
||||||
// when the given node's name is "node2"; otherwise return false.
|
// when the given node's name is "node2"; otherwise return false.
|
||||||
func Node2PredicateExtender(pod *v1.Pod, node *v1.Node) (bool, error) {
|
func Node2PredicateExtender(pod *v1.Pod, node *v1.Node) *framework.Status {
|
||||||
if node.Name == "node2" {
|
if node.Name == "node2" {
|
||||||
return true, nil
|
return framework.NewStatus(framework.Success)
|
||||||
}
|
}
|
||||||
return false, nil
|
return framework.NewStatus(framework.Unschedulable, fmt.Sprintf("node %q is not allowed", node.Name))
|
||||||
}
|
}
|
||||||
|
|
||||||
// ErrorPrioritizerExtender implements PriorityFunc function to always return error.
|
// ErrorPrioritizerExtender implements PriorityFunc function to always return error.
|
||||||
@ -211,14 +216,14 @@ func (f *FakeExtender) selectVictimsOnNodeByExtender(pod *v1.Pod, node *v1.Node)
|
|||||||
// If a extender support preemption but have no cached node info, let's run filter to make sure
|
// If a extender support preemption but have no cached node info, let's run filter to make sure
|
||||||
// default scheduler's decision still stand with given pod and node.
|
// default scheduler's decision still stand with given pod and node.
|
||||||
if !f.NodeCacheCapable {
|
if !f.NodeCacheCapable {
|
||||||
fits, err := f.runPredicate(pod, node)
|
err := f.runPredicate(pod, node)
|
||||||
if err != nil {
|
if err.IsSuccess() {
|
||||||
return nil, 0, false, err
|
|
||||||
}
|
|
||||||
if !fits {
|
|
||||||
return nil, 0, false, nil
|
|
||||||
}
|
|
||||||
return []*v1.Pod{}, 0, true, nil
|
return []*v1.Pod{}, 0, true, nil
|
||||||
|
} else if err.IsUnschedulable() {
|
||||||
|
return nil, 0, false, nil
|
||||||
|
} else {
|
||||||
|
return nil, 0, false, err.AsError()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Otherwise, as a extender support preemption and have cached node info, we will assume cachedNodeNameToInfo is available
|
// Otherwise, as a extender support preemption and have cached node info, we will assume cachedNodeNameToInfo is available
|
||||||
@ -246,12 +251,15 @@ func (f *FakeExtender) selectVictimsOnNodeByExtender(pod *v1.Pod, node *v1.Node)
|
|||||||
|
|
||||||
// If the new pod does not fit after removing all the lower priority pods,
|
// If the new pod does not fit after removing all the lower priority pods,
|
||||||
// we are almost done and this node is not suitable for preemption.
|
// we are almost done and this node is not suitable for preemption.
|
||||||
fits, err := f.runPredicate(pod, nodeInfoCopy.Node())
|
status := f.runPredicate(pod, nodeInfoCopy.Node())
|
||||||
if err != nil {
|
if status.IsSuccess() {
|
||||||
return nil, 0, false, err
|
// pass
|
||||||
}
|
} else if status.IsUnschedulable() {
|
||||||
if !fits {
|
// does not fit
|
||||||
return nil, 0, false, nil
|
return nil, 0, false, nil
|
||||||
|
} else {
|
||||||
|
// internal errors
|
||||||
|
return nil, 0, false, status.AsError()
|
||||||
}
|
}
|
||||||
|
|
||||||
var victims []*v1.Pod
|
var victims []*v1.Pod
|
||||||
@ -261,12 +269,12 @@ func (f *FakeExtender) selectVictimsOnNodeByExtender(pod *v1.Pod, node *v1.Node)
|
|||||||
|
|
||||||
reprievePod := func(p *v1.Pod) bool {
|
reprievePod := func(p *v1.Pod) bool {
|
||||||
addPod(p)
|
addPod(p)
|
||||||
fits, _ := f.runPredicate(pod, nodeInfoCopy.Node())
|
status := f.runPredicate(pod, nodeInfoCopy.Node())
|
||||||
if !fits {
|
if !status.IsSuccess() {
|
||||||
removePod(p)
|
removePod(p)
|
||||||
victims = append(victims, p)
|
victims = append(victims, p)
|
||||||
}
|
}
|
||||||
return fits
|
return status.IsSuccess()
|
||||||
}
|
}
|
||||||
|
|
||||||
// For now, assume all potential victims to be non-violating.
|
// For now, assume all potential victims to be non-violating.
|
||||||
@ -280,42 +288,39 @@ func (f *FakeExtender) selectVictimsOnNodeByExtender(pod *v1.Pod, node *v1.Node)
|
|||||||
|
|
||||||
// runPredicate run predicates of extender one by one for given pod and node.
|
// runPredicate run predicates of extender one by one for given pod and node.
|
||||||
// Returns: fits or not.
|
// Returns: fits or not.
|
||||||
func (f *FakeExtender) runPredicate(pod *v1.Pod, node *v1.Node) (bool, error) {
|
func (f *FakeExtender) runPredicate(pod *v1.Pod, node *v1.Node) *framework.Status {
|
||||||
fits := true
|
|
||||||
var err error
|
|
||||||
for _, predicate := range f.Predicates {
|
for _, predicate := range f.Predicates {
|
||||||
fits, err = predicate(pod, node)
|
status := predicate(pod, node)
|
||||||
if err != nil {
|
if !status.IsSuccess() {
|
||||||
return false, err
|
return status
|
||||||
}
|
|
||||||
if !fits {
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return fits, nil
|
return framework.NewStatus(framework.Success)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Filter implements the extender Filter function.
|
// Filter implements the extender Filter function.
|
||||||
func (f *FakeExtender) Filter(pod *v1.Pod, nodes []*v1.Node) ([]*v1.Node, extenderv1.FailedNodesMap, error) {
|
func (f *FakeExtender) Filter(pod *v1.Pod, nodes []*v1.Node) ([]*v1.Node, extenderv1.FailedNodesMap, extenderv1.FailedNodesMap, error) {
|
||||||
var filtered []*v1.Node
|
var filtered []*v1.Node
|
||||||
failedNodesMap := extenderv1.FailedNodesMap{}
|
failedNodesMap := extenderv1.FailedNodesMap{}
|
||||||
|
failedAndUnresolvableMap := extenderv1.FailedNodesMap{}
|
||||||
for _, node := range nodes {
|
for _, node := range nodes {
|
||||||
fits, err := f.runPredicate(pod, node)
|
status := f.runPredicate(pod, node)
|
||||||
if err != nil {
|
if status.IsSuccess() {
|
||||||
return []*v1.Node{}, extenderv1.FailedNodesMap{}, err
|
|
||||||
}
|
|
||||||
if fits {
|
|
||||||
filtered = append(filtered, node)
|
filtered = append(filtered, node)
|
||||||
|
} else if status.Code() == framework.Unschedulable {
|
||||||
|
failedNodesMap[node.Name] = fmt.Sprintf("FakeExtender: node %q failed", node.Name)
|
||||||
|
} else if status.Code() == framework.UnschedulableAndUnresolvable {
|
||||||
|
failedAndUnresolvableMap[node.Name] = fmt.Sprintf("FakeExtender: node %q failed and unresolvable", node.Name)
|
||||||
} else {
|
} else {
|
||||||
failedNodesMap[node.Name] = "FakeExtender failed"
|
return nil, nil, nil, status.AsError()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
f.FilteredNodes = filtered
|
f.FilteredNodes = filtered
|
||||||
if f.NodeCacheCapable {
|
if f.NodeCacheCapable {
|
||||||
return filtered, failedNodesMap, nil
|
return filtered, failedNodesMap, failedAndUnresolvableMap, nil
|
||||||
}
|
}
|
||||||
return filtered, failedNodesMap, nil
|
return filtered, failedNodesMap, failedAndUnresolvableMap, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prioritize implements the extender Prioritize function.
|
// Prioritize implements the extender Prioritize function.
|
||||||
|
@ -92,6 +92,10 @@ type ExtenderFilterResult struct {
|
|||||||
NodeNames *[]string
|
NodeNames *[]string
|
||||||
// Filtered out nodes where the pod can't be scheduled and the failure messages
|
// Filtered out nodes where the pod can't be scheduled and the failure messages
|
||||||
FailedNodes FailedNodesMap
|
FailedNodes FailedNodesMap
|
||||||
|
// Filtered out nodes where the pod can't be scheduled and preemption would
|
||||||
|
// not change anything. The value is the failure message same as FailedNodes.
|
||||||
|
// Nodes specified here takes precedence over FailedNodes.
|
||||||
|
FailedAndUnresolvableNodes FailedNodesMap
|
||||||
// Error message indicating failure
|
// Error message indicating failure
|
||||||
Error string
|
Error string
|
||||||
}
|
}
|
||||||
|
@ -70,9 +70,10 @@ func TestCompatibility(t *testing.T) {
|
|||||||
Nodes: &corev1.NodeList{Items: []corev1.Node{{ObjectMeta: metav1.ObjectMeta{Name: "nodename"}}}},
|
Nodes: &corev1.NodeList{Items: []corev1.Node{{ObjectMeta: metav1.ObjectMeta{Name: "nodename"}}}},
|
||||||
NodeNames: &[]string{"node1"},
|
NodeNames: &[]string{"node1"},
|
||||||
FailedNodes: FailedNodesMap{"foo": "bar"},
|
FailedNodes: FailedNodesMap{"foo": "bar"},
|
||||||
|
FailedAndUnresolvableNodes: FailedNodesMap{"baz": "qux"},
|
||||||
Error: "myerror",
|
Error: "myerror",
|
||||||
},
|
},
|
||||||
expectJSON: `{"Nodes":{"metadata":{},"items":[{"metadata":{"name":"nodename","creationTimestamp":null},"spec":{},"status":{"daemonEndpoints":{"kubeletEndpoint":{"Port":0}},"nodeInfo":{"machineID":"","systemUUID":"","bootID":"","kernelVersion":"","osImage":"","containerRuntimeVersion":"","kubeletVersion":"","kubeProxyVersion":"","operatingSystem":"","architecture":""}}}]},"NodeNames":["node1"],"FailedNodes":{"foo":"bar"},"Error":"myerror"}`,
|
expectJSON: `{"Nodes":{"metadata":{},"items":[{"metadata":{"name":"nodename","creationTimestamp":null},"spec":{},"status":{"daemonEndpoints":{"kubeletEndpoint":{"Port":0}},"nodeInfo":{"machineID":"","systemUUID":"","bootID":"","kernelVersion":"","osImage":"","containerRuntimeVersion":"","kubeletVersion":"","kubeProxyVersion":"","operatingSystem":"","architecture":""}}}]},"NodeNames":["node1"],"FailedNodes":{"foo":"bar"},"FailedAndUnresolvableNodes":{"baz":"qux"},"Error":"myerror"}`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
emptyObj: &ExtenderBindingArgs{},
|
emptyObj: &ExtenderBindingArgs{},
|
||||||
|
@ -115,6 +115,13 @@ func (in *ExtenderFilterResult) DeepCopyInto(out *ExtenderFilterResult) {
|
|||||||
(*out)[key] = val
|
(*out)[key] = val
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if in.FailedAndUnresolvableNodes != nil {
|
||||||
|
in, out := &in.FailedAndUnresolvableNodes, &out.FailedAndUnresolvableNodes
|
||||||
|
*out = make(FailedNodesMap, len(*in))
|
||||||
|
for key, val := range *in {
|
||||||
|
(*out)[key] = val
|
||||||
|
}
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user