Skip to content

Commit fbddc96

Browse files
authored
Support PrefilterExtensions (#92)
* Support PrefilterExtensions * Address the feedbacks * Fix argument for plugin test * Fix the variable name to lowercase * Fix type errors * Update test * Update plugin test * Address feedback and update testcode * Restore unneeded changes of wasm * Fix message * Fix wrong URL * Add Plugin * Remove redundant print debug * Add wasm file for updated go file * Fix typos * Remove redundant URL definition * Add podInfoToAdd & nodeInfo to stack * Fix `params` to pass Node and Pod * Address the feedback * Add `PodInfo{}` instead of cyclestate.Pod * Remove unused type * Update the type between host & guest * Update the definition of guest * Address feedback * Fix type * Fix the definition of PodInfo * Fix type of testdata * Pass PodInfo to function * Remove unnecessary comment * Fix e2e test for PrefilterExtensions * Rollback unncecessary change of wasm * Fix comment typo and oddity * Remove redundant definition in wasm * Merge redundant definition * Add necessary information to testcase output * Fix function for host api * Fix the order of plugin definition * Implement Pod for PodInfoToAdd * Update the naming of the pod for add/remove * Add comment for k8sApiPodInfoFn * Update error message * Tweak return string of `GetKind()` * Upddate testdata to give concrete information * Rename to `targetPod` * fix: rollack wasm
1 parent fb4c8bc commit fbddc96

File tree

22 files changed

+621
-21
lines changed

22 files changed

+621
-21
lines changed

guest/api/types.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,14 @@ type PreFilterPlugin interface {
5959
PreFilter(state CycleState, pod proto.Pod) (nodeNames []string, status *Status)
6060
}
6161

62+
// PreFilterExtensions is a WebAssembly implementation of framework.PrefFilterExtensions.
63+
type PreFilterExtensions interface {
64+
Plugin
65+
66+
AddPod(state CycleState, podToSchedule proto.Pod, podInfoToAdd PodInfo, nodeInfo NodeInfo) *Status
67+
RemovePod(state CycleState, podToSchedule proto.Pod, podInfoToRemove PodInfo, nodeInfo NodeInfo) *Status
68+
}
69+
6270
// FilterPlugin is a WebAssembly implementation of framework.FilterPlugin.
6371
//
6472
// Note: The pod and nodeInfo parameters are lazy to avoid unmarshal overhead
@@ -160,6 +168,13 @@ type NodeInfo interface {
160168
Node() proto.Node
161169
}
162170

171+
type PodInfo interface {
172+
// Metadata is a convenience that triggers Get.
173+
proto.Metadata
174+
175+
Pod() proto.Pod
176+
}
177+
163178
// NodeToStatus contains which Node got which status during the scheduling cycle.
164179
type NodeToStatus interface {
165180
// NodeToStatus returns a map

guest/filter/filter.go

Lines changed: 63 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -67,43 +67,43 @@ func _filter() uint32 { //nolint
6767
return 0
6868
}
6969

70-
s := filter.Filter(cyclestate.Values, cyclestate.Pod, &nodeInfo{})
70+
s := filter.Filter(cyclestate.Values, cyclestate.Pod, &NodeInfo{})
7171

7272
return imports.StatusToCode(s)
7373
}
7474

75-
var _ api.NodeInfo = (*nodeInfo)(nil)
75+
var _ api.NodeInfo = (*NodeInfo)(nil)
7676

7777
// nodeInfo is lazy so that a plugin which doesn't read fields avoids a
7878
// relatively expensive unmarshal penalty.
7979
//
8080
// Note: Unlike proto.Pod, this is not special cased for the scheduling cycle.
81-
type nodeInfo struct {
81+
type NodeInfo struct {
8282
node proto.Node
8383
}
8484

85-
func (n *nodeInfo) GetUid() string {
85+
func (n *NodeInfo) GetUid() string {
8686
return n.lazyNode().GetUid()
8787
}
8888

89-
func (n *nodeInfo) GetName() string {
89+
func (n *NodeInfo) GetName() string {
9090
return n.lazyNode().GetName()
9191
}
9292

93-
func (n *nodeInfo) GetNamespace() string {
93+
func (n *NodeInfo) GetNamespace() string {
9494
return n.lazyNode().GetNamespace()
9595
}
9696

97-
func (n *nodeInfo) GetResourceVersion() string {
97+
func (n *NodeInfo) GetResourceVersion() string {
9898
return n.lazyNode().GetResourceVersion()
9999
}
100100

101-
func (n *nodeInfo) Node() proto.Node {
101+
func (n *NodeInfo) Node() proto.Node {
102102
return n.lazyNode()
103103
}
104104

105105
// lazyNode lazy initializes node from imports.Node.
106-
func (n *nodeInfo) lazyNode() proto.Node {
106+
func (n *NodeInfo) lazyNode() proto.Node {
107107
if node := n.node; node != nil {
108108
return node
109109
}
@@ -115,3 +115,57 @@ func (n *nodeInfo) lazyNode() proto.Node {
115115
n.node = &internalproto.Node{Msg: &msg}
116116
return n.node
117117
}
118+
119+
type PodInfo struct {
120+
pod proto.Pod
121+
}
122+
123+
func (p *PodInfo) GetApiVersion() string {
124+
return p.lazyPod().GetApiVersion()
125+
}
126+
127+
func (p *PodInfo) GetKind() string {
128+
return p.lazyPod().GetKind()
129+
}
130+
131+
func (p *PodInfo) GetName() string {
132+
return p.lazyPod().GetName()
133+
}
134+
135+
func (p *PodInfo) GetNamespace() string {
136+
return p.lazyPod().GetNamespace()
137+
}
138+
139+
func (p *PodInfo) GetResourceVersion() string {
140+
return p.lazyPod().GetNamespace()
141+
}
142+
143+
func (p *PodInfo) GetUid() string {
144+
return p.lazyPod().GetUid()
145+
}
146+
147+
func (p *PodInfo) Pod() proto.Pod {
148+
return p.lazyPod()
149+
}
150+
151+
func (p *PodInfo) Spec() *protoapi.PodSpec {
152+
return p.lazyPod().Spec()
153+
}
154+
155+
func (p *PodInfo) Status() *protoapi.PodStatus {
156+
return p.lazyPod().Status()
157+
}
158+
159+
// lazyPod lazy initializes pod from imports.Pod.
160+
func (p *PodInfo) lazyPod() proto.Pod {
161+
if pod := p.pod; pod != nil {
162+
return pod
163+
}
164+
165+
var msg protoapi.Pod
166+
if err := imports.Pod(msg.UnmarshalVT); err != nil {
167+
panic(err.Error())
168+
}
169+
p.pod = &internalproto.Pod{Msg: &msg}
170+
return p.pod
171+
}

guest/internal/proto/proto.go

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,3 +91,41 @@ func (o *Node) Spec() *protoapi.NodeSpec {
9191
func (o *Node) Status() *protoapi.NodeStatus {
9292
return o.Msg.Status
9393
}
94+
95+
var _ proto.Pod = (*Pod)(nil)
96+
97+
type Pod struct {
98+
Msg *protoapi.Pod
99+
}
100+
101+
func (o *Pod) GetName() string {
102+
return GetName(o.Msg)
103+
}
104+
105+
func (o *Pod) GetNamespace() string {
106+
return GetNamespace(o.Msg)
107+
}
108+
109+
func (o *Pod) GetUid() string {
110+
return GetUid(o.Msg)
111+
}
112+
113+
func (o *Pod) GetResourceVersion() string {
114+
return GetResourceVersion(o.Msg)
115+
}
116+
117+
func (o *Pod) GetKind() string {
118+
return "Pod"
119+
}
120+
121+
func (o *Pod) GetApiVersion() string {
122+
return "v1"
123+
}
124+
125+
func (o *Pod) Spec() *protoapi.PodSpec {
126+
return o.Msg.Spec
127+
}
128+
129+
func (o *Pod) Status() *protoapi.PodStatus {
130+
return o.Msg.Status
131+
}

guest/plugin/plugin.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import (
2626
"sigs.k8s.io/kube-scheduler-wasm-extension/guest/postbind"
2727
"sigs.k8s.io/kube-scheduler-wasm-extension/guest/postfilter"
2828
"sigs.k8s.io/kube-scheduler-wasm-extension/guest/prebind"
29+
"sigs.k8s.io/kube-scheduler-wasm-extension/guest/prefilterextensions"
2930
"sigs.k8s.io/kube-scheduler-wasm-extension/guest/prescore"
3031
"sigs.k8s.io/kube-scheduler-wasm-extension/guest/reserve"
3132
"sigs.k8s.io/kube-scheduler-wasm-extension/guest/score"
@@ -55,6 +56,9 @@ func Set(plugin api.Plugin) {
5556
if plugin, ok := plugin.(api.PreFilterPlugin); ok {
5657
prefilter.SetPlugin(plugin)
5758
}
59+
if plugin, ok := plugin.(api.PreFilterExtensions); ok {
60+
prefilterextensions.SetPlugin(plugin)
61+
}
5862
if plugin, ok := plugin.(api.FilterPlugin); ok {
5963
filter.SetPlugin(plugin)
6064
}
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
/*
2+
Copyright 2023 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
// Package prefilterextensions exports an api.PreFilterExtensions to the host. Only import this
18+
// package when setting Plugin, as doing otherwise will cause overhead.
19+
20+
package prefilterextensions
21+
22+
import (
23+
"sigs.k8s.io/kube-scheduler-wasm-extension/guest/api"
24+
"sigs.k8s.io/kube-scheduler-wasm-extension/guest/filter"
25+
"sigs.k8s.io/kube-scheduler-wasm-extension/guest/internal/cyclestate"
26+
"sigs.k8s.io/kube-scheduler-wasm-extension/guest/internal/imports"
27+
"sigs.k8s.io/kube-scheduler-wasm-extension/guest/internal/plugin"
28+
)
29+
30+
// prefilterextensions is the current plugin assigned with SetPlugin.
31+
var prefilterextensions api.PreFilterExtensions
32+
33+
// SetPlugin is exposed to prevent package cycles.
34+
func SetPlugin(preFilterExtensions api.PreFilterExtensions) {
35+
if preFilterExtensions == nil {
36+
panic("nil preFilterExtensions")
37+
}
38+
prefilterextensions = preFilterExtensions
39+
plugin.MustSet(prefilterextensions)
40+
}
41+
42+
// prevent unused lint errors (lint is run with normal go).
43+
var (
44+
_ func() uint32 = _addpod
45+
_ func() uint32 = _removepod
46+
)
47+
48+
// _addPod is only exported to the host.
49+
//
50+
//export addpod
51+
func _addpod() uint32 { //nolint
52+
if prefilterextensions == nil { // Then, the user didn't define one.
53+
// Unlike most plugins we always export reserve so that we can reset
54+
// the cycle state: return success to avoid no-op overhead.
55+
return 0
56+
}
57+
58+
status := prefilterextensions.AddPod(cyclestate.Values, cyclestate.Pod, &filter.PodInfo{}, &filter.NodeInfo{})
59+
60+
return imports.StatusToCode(status)
61+
}
62+
63+
// _removePod is only exported to the host.
64+
//
65+
//export removepod
66+
func _removepod() uint32 { //nolint
67+
if prefilterextensions == nil { // Then, the user didn't define one.
68+
// Unlike most plugins we always export unreserve so that we can reset
69+
// the cycle state: return success to avoid no-op overhead.
70+
return 0
71+
}
72+
73+
status := prefilterextensions.RemovePod(cyclestate.Values, cyclestate.Pod, &filter.PodInfo{}, &filter.NodeInfo{})
74+
75+
return imports.StatusToCode(status)
76+
}

guest/testdata/bind/main.wasm

6.67 KB
Binary file not shown.

guest/testdata/cyclestate/main.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import (
3232
"sigs.k8s.io/kube-scheduler-wasm-extension/guest/postfilter"
3333
"sigs.k8s.io/kube-scheduler-wasm-extension/guest/prebind"
3434
"sigs.k8s.io/kube-scheduler-wasm-extension/guest/prefilter"
35+
"sigs.k8s.io/kube-scheduler-wasm-extension/guest/prefilterextensions"
3536
"sigs.k8s.io/kube-scheduler-wasm-extension/guest/prescore"
3637
"sigs.k8s.io/kube-scheduler-wasm-extension/guest/reserve"
3738
"sigs.k8s.io/kube-scheduler-wasm-extension/guest/score"
@@ -62,6 +63,7 @@ func main() {
6263
plugin := statePlugin{}
6364
enqueue.SetPlugin(plugin)
6465
prefilter.SetPlugin(plugin)
66+
prefilterextensions.SetPlugin(plugin)
6567
filter.SetPlugin(plugin)
6668
postfilter.SetPlugin(plugin)
6769
prescore.SetPlugin(plugin)
@@ -229,6 +231,14 @@ func (statePlugin) PostBind(state api.CycleState, pod proto.Pod, _ string) {
229231
}
230232
}
231233

234+
func (statePlugin) AddPod(state api.CycleState, pod proto.Pod, podInfoToAdd api.PodInfo, nodeInfo api.NodeInfo) (status *api.Status) {
235+
return
236+
}
237+
238+
func (statePlugin) RemovePod(state api.CycleState, pod proto.Pod, podInfoToAdd api.PodInfo, nodeInfo api.NodeInfo) (status *api.Status) {
239+
return
240+
}
241+
232242
// mustNotScoreState ensures that score state, written after filter, cannot
233243
// be read by extension points before it.
234244
//
784 Bytes
Binary file not shown.

guest/testdata/filter/main.go

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,14 @@ import (
2424
"sigs.k8s.io/kube-scheduler-wasm-extension/guest/filter"
2525
"sigs.k8s.io/kube-scheduler-wasm-extension/guest/postfilter"
2626
"sigs.k8s.io/kube-scheduler-wasm-extension/guest/prefilter"
27+
"sigs.k8s.io/kube-scheduler-wasm-extension/guest/prefilterextensions"
2728
)
2829

2930
type extensionPoints interface {
3031
api.PreFilterPlugin
3132
api.FilterPlugin
3233
api.PostFilterPlugin
34+
api.PreFilterExtensions
3335
}
3436

3537
func main() {
@@ -44,11 +46,14 @@ func main() {
4446
plugin = preFilterPlugin{}
4547
case "postFilter":
4648
plugin = postFilterPlugin{}
49+
case "preFilterExtensions":
50+
plugin = preFilterExtensions{}
4751
}
4852
}
4953
prefilter.SetPlugin(plugin)
5054
filter.SetPlugin(plugin)
5155
postfilter.SetPlugin(plugin)
56+
prefilterextensions.SetPlugin(plugin)
5257
}
5358

5459
// noopPlugin doesn't do anything, except evaluate each parameter.
@@ -74,6 +79,20 @@ func (noopPlugin) PostFilter(state api.CycleState, pod proto.Pod, nodeMap api.No
7479
return
7580
}
7681

82+
func (noopPlugin) AddPod(state api.CycleState, pod proto.Pod, podInfoToAdd api.PodInfo, nodeInfo api.NodeInfo) (status *api.Status) {
83+
_, _ = state.Read("ok")
84+
_ = pod.Spec()
85+
_ = nodeInfo.Node().Spec() // trigger lazy loading
86+
return
87+
}
88+
89+
func (noopPlugin) RemovePod(state api.CycleState, pod proto.Pod, podInfoToRemove api.PodInfo, nodeInfo api.NodeInfo) (status *api.Status) {
90+
_, _ = state.Read("ok")
91+
_ = pod.Spec()
92+
_ = nodeInfo.Node().Spec() // trigger lazy loading
93+
return
94+
}
95+
7796
// preFilterPlugin schedules a node if its name equals its pod spec.
7897
type preFilterPlugin struct{ noopPlugin }
7998

@@ -133,3 +152,25 @@ func (postFilterPlugin) PostFilter(_ api.CycleState, pod proto.Pod, nodeMap api.
133152
Reason: podSpecNodeName + " is unschedulable",
134153
}
135154
}
155+
156+
type preFilterExtensions struct{ noopPlugin }
157+
158+
func (preFilterExtensions) AddPod(state api.CycleState, pod proto.Pod, podInfoToAdd api.PodInfo, nodeInfo api.NodeInfo) *api.Status {
159+
nodeName := nodeInfo.Node().GetName()
160+
state.Write(nodeName+pod.Spec().GetNodeName(), "ok")
161+
status := 0
162+
if nodeName == "bad" {
163+
status = 1
164+
}
165+
return &api.Status{Code: api.StatusCode(status), Reason: "Node name is " + nodeName + " " + "and PodInfo name is " + podInfoToAdd.Pod().GetName()}
166+
}
167+
168+
func (preFilterExtensions) RemovePod(state api.CycleState, pod proto.Pod, podInfoToRemove api.PodInfo, nodeInfo api.NodeInfo) *api.Status {
169+
nodeName := nodeInfo.Node().GetName()
170+
state.Write(nodeName+pod.Spec().GetNodeName(), "ok")
171+
status := 0
172+
if nodeName == "bad" {
173+
status = 1
174+
}
175+
return &api.Status{Code: api.StatusCode(status), Reason: "Node name is " + nodeName + " " + "and PodInfo name is " + podInfoToRemove.Pod().GetName()}
176+
}

guest/testdata/filter/main.wasm

65.3 KB
Binary file not shown.

0 commit comments

Comments
 (0)