Skip to content

Commit 57b79e3

Browse files
authored
Merge pull request kubernetes#91037 from Huang-Wei/prefactor-PreemptExtender
Refactor preemption extender logic and move SchedulerExtender interface to framework pkg
2 parents 5b0d8d2 + eb17b75 commit 57b79e3

File tree

10 files changed

+182
-171
lines changed

10 files changed

+182
-171
lines changed

pkg/scheduler/core/extender.go

Lines changed: 20 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -38,55 +38,7 @@ const (
3838
DefaultExtenderTimeout = 5 * time.Second
3939
)
4040

41-
// SchedulerExtender is an interface for external processes to influence scheduling
42-
// decisions made by Kubernetes. This is typically needed for resources not directly
43-
// managed by Kubernetes.
44-
type SchedulerExtender interface {
45-
// Name returns a unique name that identifies the extender.
46-
Name() string
47-
48-
// Filter based on extender-implemented predicate functions. The filtered list is
49-
// expected to be a subset of the supplied list. failedNodesMap optionally contains
50-
// the list of failed nodes and failure reasons.
51-
Filter(pod *v1.Pod, nodes []*v1.Node) (filteredNodes []*v1.Node, failedNodesMap extenderv1.FailedNodesMap, err error)
52-
53-
// Prioritize based on extender-implemented priority functions. The returned scores & weight
54-
// are used to compute the weighted score for an extender. The weighted scores are added to
55-
// the scores computed by Kubernetes scheduler. The total scores are used to do the host selection.
56-
Prioritize(pod *v1.Pod, nodes []*v1.Node) (hostPriorities *extenderv1.HostPriorityList, weight int64, err error)
57-
58-
// Bind delegates the action of binding a pod to a node to the extender.
59-
Bind(binding *v1.Binding) error
60-
61-
// IsBinder returns whether this extender is configured for the Bind method.
62-
IsBinder() bool
63-
64-
// IsInterested returns true if at least one extended resource requested by
65-
// this pod is managed by this extender.
66-
IsInterested(pod *v1.Pod) bool
67-
68-
// ProcessPreemption returns nodes with their victim pods processed by extender based on
69-
// given:
70-
// 1. Pod to schedule
71-
// 2. Candidate nodes and victim pods (nodeToVictims) generated by previous scheduling process.
72-
// 3. nodeNameToInfo to restore v1.Node from node name if extender cache is enabled.
73-
// The possible changes made by extender may include:
74-
// 1. Subset of given candidate nodes after preemption phase of extender.
75-
// 2. A different set of victim pod for every given candidate node after preemption phase of extender.
76-
ProcessPreemption(
77-
pod *v1.Pod,
78-
nodeToVictims map[*v1.Node]*extenderv1.Victims,
79-
nodeInfos framework.NodeInfoLister) (map[*v1.Node]*extenderv1.Victims, error)
80-
81-
// SupportsPreemption returns if the scheduler extender support preemption or not.
82-
SupportsPreemption() bool
83-
84-
// IsIgnorable returns true indicates scheduling should not fail when this extender
85-
// is unavailable. This gives scheduler ability to fail fast and tolerate non-critical extenders as well.
86-
IsIgnorable() bool
87-
}
88-
89-
// HTTPExtender implements the SchedulerExtender interface.
41+
// HTTPExtender implements the Extender interface.
9042
type HTTPExtender struct {
9143
extenderURL string
9244
preemptVerb string
@@ -131,7 +83,7 @@ func makeTransport(config *schedulerapi.Extender) (http.RoundTripper, error) {
13183
}
13284

13385
// NewHTTPExtender creates an HTTPExtender object.
134-
func NewHTTPExtender(config *schedulerapi.Extender) (SchedulerExtender, error) {
86+
func NewHTTPExtender(config *schedulerapi.Extender) (framework.Extender, error) {
13587
if config.HTTPTimeout.Nanoseconds() == 0 {
13688
config.HTTPTimeout = time.Duration(DefaultExtenderTimeout)
13789
}
@@ -212,9 +164,9 @@ func (h *HTTPExtender) SupportsPreemption() bool {
212164
// ProcessPreemption returns filtered candidate nodes and victims after running preemption logic in extender.
213165
func (h *HTTPExtender) ProcessPreemption(
214166
pod *v1.Pod,
215-
nodeToVictims map[*v1.Node]*extenderv1.Victims,
167+
nodeNameToVictims map[string]*extenderv1.Victims,
216168
nodeInfos framework.NodeInfoLister,
217-
) (map[*v1.Node]*extenderv1.Victims, error) {
169+
) (map[string]*extenderv1.Victims, error) {
218170
var (
219171
result extenderv1.ExtenderPreemptionResult
220172
args *extenderv1.ExtenderPreemptionArgs
@@ -226,13 +178,12 @@ func (h *HTTPExtender) ProcessPreemption(
226178

227179
if h.nodeCacheCapable {
228180
// If extender has cached node info, pass NodeNameToMetaVictims in args.
229-
nodeNameToMetaVictims := convertToNodeNameToMetaVictims(nodeToVictims)
181+
nodeNameToMetaVictims := convertToNodeNameToMetaVictims(nodeNameToVictims)
230182
args = &extenderv1.ExtenderPreemptionArgs{
231183
Pod: pod,
232184
NodeNameToMetaVictims: nodeNameToMetaVictims,
233185
}
234186
} else {
235-
nodeNameToVictims := convertToNodeNameToVictims(nodeToVictims)
236187
args = &extenderv1.ExtenderPreemptionArgs{
237188
Pod: pod,
238189
NodeNameToVictims: nodeNameToVictims,
@@ -244,22 +195,22 @@ func (h *HTTPExtender) ProcessPreemption(
244195
}
245196

246197
// Extender will always return NodeNameToMetaVictims.
247-
// So let's convert it to NodeToVictims by using NodeNameToInfo.
248-
newNodeToVictims, err := h.convertToNodeToVictims(result.NodeNameToMetaVictims, nodeInfos)
198+
// So let's convert it to NodeNameToVictims by using <nodeInfos>.
199+
newNodeNameToVictims, err := h.convertToNodeNameToVictims(result.NodeNameToMetaVictims, nodeInfos)
249200
if err != nil {
250201
return nil, err
251202
}
252-
// Do not override nodeToVictims
253-
return newNodeToVictims, nil
203+
// Do not override <nodeNameToVictims>.
204+
return newNodeNameToVictims, nil
254205
}
255206

256-
// convertToNodeToVictims converts "nodeNameToMetaVictims" from object identifiers,
207+
// convertToNodeNameToVictims converts "nodeNameToMetaVictims" from object identifiers,
257208
// such as UIDs and names, to object pointers.
258-
func (h *HTTPExtender) convertToNodeToVictims(
209+
func (h *HTTPExtender) convertToNodeNameToVictims(
259210
nodeNameToMetaVictims map[string]*extenderv1.MetaVictims,
260211
nodeInfos framework.NodeInfoLister,
261-
) (map[*v1.Node]*extenderv1.Victims, error) {
262-
nodeToVictims := map[*v1.Node]*extenderv1.Victims{}
212+
) (map[string]*extenderv1.Victims, error) {
213+
nodeNameToVictims := map[string]*extenderv1.Victims{}
263214
for nodeName, metaVictims := range nodeNameToMetaVictims {
264215
nodeInfo, err := nodeInfos.Get(nodeName)
265216
if err != nil {
@@ -275,9 +226,9 @@ func (h *HTTPExtender) convertToNodeToVictims(
275226
}
276227
victims.Pods = append(victims.Pods, pod)
277228
}
278-
nodeToVictims[nodeInfo.Node()] = victims
229+
nodeNameToVictims[nodeName] = victims
279230
}
280-
return nodeToVictims, nil
231+
return nodeNameToVictims, nil
281232
}
282233

283234
// convertPodUIDToPod returns v1.Pod object for given MetaPod and node info.
@@ -298,10 +249,10 @@ func (h *HTTPExtender) convertPodUIDToPod(
298249

299250
// convertToNodeNameToMetaVictims converts from struct type to meta types.
300251
func convertToNodeNameToMetaVictims(
301-
nodeToVictims map[*v1.Node]*extenderv1.Victims,
252+
nodeNameToVictims map[string]*extenderv1.Victims,
302253
) map[string]*extenderv1.MetaVictims {
303-
nodeNameToVictims := map[string]*extenderv1.MetaVictims{}
304-
for node, victims := range nodeToVictims {
254+
nodeNameToMetaVictims := map[string]*extenderv1.MetaVictims{}
255+
for node, victims := range nodeNameToVictims {
305256
metaVictims := &extenderv1.MetaVictims{
306257
Pods: []*extenderv1.MetaPod{},
307258
}
@@ -311,20 +262,9 @@ func convertToNodeNameToMetaVictims(
311262
}
312263
metaVictims.Pods = append(metaVictims.Pods, metaPod)
313264
}
314-
nodeNameToVictims[node.GetName()] = metaVictims
315-
}
316-
return nodeNameToVictims
317-
}
318-
319-
// convertToNodeNameToVictims converts from node type to node name as key.
320-
func convertToNodeNameToVictims(
321-
nodeToVictims map[*v1.Node]*extenderv1.Victims,
322-
) map[string]*extenderv1.Victims {
323-
nodeNameToVictims := map[string]*extenderv1.Victims{}
324-
for node, victims := range nodeToVictims {
325-
nodeNameToVictims[node.GetName()] = victims
265+
nodeNameToMetaVictims[node] = metaVictims
326266
}
327-
return nodeNameToVictims
267+
return nodeNameToMetaVictims
328268
}
329269

330270
// Filter based on extender implemented predicate functions. The filtered list is

pkg/scheduler/core/extender_test.go

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -159,36 +159,37 @@ func (f *FakeExtender) SupportsPreemption() bool {
159159

160160
func (f *FakeExtender) ProcessPreemption(
161161
pod *v1.Pod,
162-
nodeToVictims map[*v1.Node]*extenderv1.Victims,
162+
nodeNameToVictims map[string]*extenderv1.Victims,
163163
nodeInfos framework.NodeInfoLister,
164-
) (map[*v1.Node]*extenderv1.Victims, error) {
165-
nodeToVictimsCopy := map[*v1.Node]*extenderv1.Victims{}
166-
// We don't want to change the original nodeToVictims
167-
for k, v := range nodeToVictims {
164+
) (map[string]*extenderv1.Victims, error) {
165+
nodeNameToVictimsCopy := map[string]*extenderv1.Victims{}
166+
// We don't want to change the original nodeNameToVictims
167+
for k, v := range nodeNameToVictims {
168168
// In real world implementation, extender's user should have their own way to get node object
169169
// by name if needed (e.g. query kube-apiserver etc).
170170
//
171171
// For test purpose, we just use node from parameters directly.
172-
nodeToVictimsCopy[k] = v
172+
nodeNameToVictimsCopy[k] = v
173173
}
174174

175-
for node, victims := range nodeToVictimsCopy {
175+
for nodeName, victims := range nodeNameToVictimsCopy {
176176
// Try to do preemption on extender side.
177-
extenderVictimPods, extendernPDBViolations, fits, err := f.selectVictimsOnNodeByExtender(pod, node)
177+
nodeInfo, _ := nodeInfos.Get(nodeName)
178+
extenderVictimPods, extenderPDBViolations, fits, err := f.selectVictimsOnNodeByExtender(pod, nodeInfo.Node())
178179
if err != nil {
179180
return nil, err
180181
}
181182
// If it's unfit after extender's preemption, this node is unresolvable by preemption overall,
182183
// let's remove it from potential preemption nodes.
183184
if !fits {
184-
delete(nodeToVictimsCopy, node)
185+
delete(nodeNameToVictimsCopy, nodeName)
185186
} else {
186187
// Append new victims to original victims
187-
nodeToVictimsCopy[node].Pods = append(victims.Pods, extenderVictimPods...)
188-
nodeToVictimsCopy[node].NumPDBViolations = victims.NumPDBViolations + int64(extendernPDBViolations)
188+
nodeNameToVictimsCopy[nodeName].Pods = append(victims.Pods, extenderVictimPods...)
189+
nodeNameToVictimsCopy[nodeName].NumPDBViolations = victims.NumPDBViolations + int64(extenderPDBViolations)
189190
}
190191
}
191-
return nodeToVictimsCopy, nil
192+
return nodeNameToVictimsCopy, nil
192193
}
193194

194195
// selectVictimsOnNodeByExtender checks the given nodes->pods map with predicates on extender's side.
@@ -352,7 +353,7 @@ func (f *FakeExtender) IsInterested(pod *v1.Pod) bool {
352353
return !f.unInterested
353354
}
354355

355-
var _ SchedulerExtender = &FakeExtender{}
356+
var _ framework.Extender = &FakeExtender{}
356357

357358
func TestGenericSchedulerWithExtenders(t *testing.T) {
358359
tests := []struct {
@@ -574,7 +575,7 @@ func TestGenericSchedulerWithExtenders(t *testing.T) {
574575
client := clientsetfake.NewSimpleClientset()
575576
informerFactory := informers.NewSharedInformerFactory(client, 0)
576577

577-
extenders := []SchedulerExtender{}
578+
extenders := []framework.Extender{}
578579
for ii := range test.extenders {
579580
extenders = append(extenders, &test.extenders[ii])
580581
}

0 commit comments

Comments
 (0)