Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support EventRecorder #83

Merged
merged 7 commits into from
Jan 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion examples/advanced/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import (
"sigs.k8s.io/kube-scheduler-wasm-extension/examples/advanced/plugin"
"sigs.k8s.io/kube-scheduler-wasm-extension/guest/config"
"sigs.k8s.io/kube-scheduler-wasm-extension/guest/enqueue"
"sigs.k8s.io/kube-scheduler-wasm-extension/guest/eventrecorder"
"sigs.k8s.io/kube-scheduler-wasm-extension/guest/klog"
"sigs.k8s.io/kube-scheduler-wasm-extension/guest/prescore"
"sigs.k8s.io/kube-scheduler-wasm-extension/guest/score"
Expand All @@ -41,7 +42,7 @@ func main() {
//
// The real implementations, such as `config.Get()` use Wasm host functions
// (go:wasmimport), which cannot be tested with `tinygo test -target=wasi`.
plugin, err := plugin.New(klog.Get(), config.Get())
Gekko0114 marked this conversation as resolved.
Show resolved Hide resolved
plugin, err := plugin.New(klog.Get(), config.Get(), eventrecorder.Get())
if err != nil {
panic(err)
}
Expand Down
Binary file modified examples/advanced/main.wasm
Binary file not shown.
12 changes: 8 additions & 4 deletions examples/advanced/plugin/plugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import (

"sigs.k8s.io/kube-scheduler-wasm-extension/guest/api"
"sigs.k8s.io/kube-scheduler-wasm-extension/guest/api/proto"
eventrecorder "sigs.k8s.io/kube-scheduler-wasm-extension/guest/eventrecorder/api"
klog "sigs.k8s.io/kube-scheduler-wasm-extension/guest/klog/api"
)

Expand All @@ -49,8 +50,9 @@ import (
// - The reverse field inverts the score. For example, when `reverse == true`
// a numeric match gets a results in a lower score than a match.
type NodeNumber struct {
reverse bool
klog klog.Klog
reverse bool
klog klog.Klog
eventrecorder eventrecorder.EventRecorder
}

const (
Expand All @@ -77,9 +79,11 @@ func (pl *NodeNumber) PreScore(state api.CycleState, pod proto.Pod, _ proto.Node

podnum, ok := lastNumber(pod.Spec().GetNodeName())
if !ok {
pl.eventrecorder.Eventf(pod, nil, "PreScore", "not match lastNumber", "Skip", "")
return nil // return success even if its suffix is non-number.
}

pl.eventrecorder.Eventf(pod, nil, "PreScore", "match lastNumber", "Continue", "")
state.Write(preScoreStateKey, &preScoreState{podSuffixNumber: podnum})
return nil
}
Expand Down Expand Up @@ -127,15 +131,15 @@ func lastNumber(str string) (uint8, bool) {
//
// Note: This accepts config instead of implicitly calling config.Get for
// testing.
func New(klog klog.Klog, jsonConfig []byte) (*NodeNumber, error) {
func New(klog klog.Klog, jsonConfig []byte, eventrecorder eventrecorder.EventRecorder) (*NodeNumber, error) {
var args nodeNumberArgs
if jsonConfig != nil {
if err := json.Unmarshal(jsonConfig, &args); err != nil {
return nil, fmt.Errorf("decode arg into NodeNumberArgs: %w", err)
}
klog.Info("NodeNumberArgs is successfully applied")
}
return &NodeNumber{klog: klog, reverse: args.Reverse}, nil
return &NodeNumber{klog: klog, reverse: args.Reverse, eventrecorder: eventrecorder}, nil
}

type nodeNumberArgs struct {
Expand Down
15 changes: 14 additions & 1 deletion examples/advanced/plugin/plugin_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (

"sigs.k8s.io/kube-scheduler-wasm-extension/guest/api"
"sigs.k8s.io/kube-scheduler-wasm-extension/guest/api/proto"
eventrecorderapi "sigs.k8s.io/kube-scheduler-wasm-extension/guest/eventrecorder/api"
klogapi "sigs.k8s.io/kube-scheduler-wasm-extension/guest/klog/api"
protoapi "sigs.k8s.io/kube-scheduler-wasm-extension/kubernetes/proto/api"
)
Expand Down Expand Up @@ -52,7 +53,7 @@ func Test_NodeNumber(t *testing.T) {
expectedMatch = !expectedMatch
}
t.Run(name, func(t *testing.T) {
plugin := &NodeNumber{klog: klogapi.UnimplementedKlog{}, reverse: reverse}
plugin := &NodeNumber{klog: klogapi.UnimplementedKlog{}, reverse: reverse, eventrecorder: eventrecorderapi.UnimplementedEventRecorder{}}
state := testCycleState{}

status := plugin.PreScore(state, tc.pod, nil)
Expand Down Expand Up @@ -144,6 +145,18 @@ func (t testPod) GetNamespace() string {
return ""
}

func (t testPod) GetApiVersion() string {
return ""
}

func (t testPod) GetKind() string {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ABI doesn't include kind and resouce version. So we return static values here

return "pod"
}

func (t testPod) GetResourceVersion() string {
return "v1"
}

func (t testPod) Spec() *protoapi.PodSpec {
nodeName := t.nodeName
return &protoapi.PodSpec{NodeName: &nodeName}
Expand Down
19 changes: 16 additions & 3 deletions examples/nodenumber/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,21 +25,31 @@ import (
"sigs.k8s.io/kube-scheduler-wasm-extension/guest/api"
"sigs.k8s.io/kube-scheduler-wasm-extension/guest/api/proto"
"sigs.k8s.io/kube-scheduler-wasm-extension/guest/config"
"sigs.k8s.io/kube-scheduler-wasm-extension/guest/eventrecorder"
"sigs.k8s.io/kube-scheduler-wasm-extension/guest/klog"
klogapi "sigs.k8s.io/kube-scheduler-wasm-extension/guest/klog/api"
"sigs.k8s.io/kube-scheduler-wasm-extension/guest/plugin"
)

// main is compiled to a WebAssembly function named "_start", called by the
// wasm scheduler plugin during initialization.
func main() {
p, err := New(klog.Get(), config.Get())
if err != nil {
panic(err)
}
plugin.Set(p)
}

func New(klog klogapi.Klog, jsonConfig []byte) (api.Plugin, error) {
var args nodeNumberArgs
if jsonConfig := config.Get(); jsonConfig != nil {
if jsonConfig != nil {
if err := json.Unmarshal(jsonConfig, &args); err != nil {
panic(fmt.Errorf("decode arg into NodeNumberArgs: %w", err))
}
klog.Info("NodeNumberArgs is successfully applied")
}
plugin.Set(&NodeNumber{reverse: args.Reverse})
return &NodeNumber{reverse: args.Reverse}, nil
}

// NodeNumber is an example plugin that favors nodes that share a numerical
Expand Down Expand Up @@ -82,13 +92,16 @@ func (pl *NodeNumber) EventsToRegister() []api.ClusterEvent {

// PreScore implements api.PreScorePlugin
func (pl *NodeNumber) PreScore(state api.CycleState, pod proto.Pod, _ proto.NodeList) *api.Status {
recorder := eventrecorder.Get()

klog.InfoS("execute PreScore on NodeNumber plugin", "pod", klog.KObj(pod))

podnum, ok := lastNumber(pod.Spec().GetNodeName())
if !ok {
recorder.Eventf(pod, nil, "PreScore", "not match lastNumber", "Skip", "")
return nil // return success even if its suffix is non-number.
}

recorder.Eventf(pod, nil, "PreScore", "match lastNumber", "Continue", "")
state.Write(preScoreStateKey, &preScoreState{podSuffixNumber: podnum})
return nil
}
Expand Down
Binary file modified examples/nodenumber/main.wasm
Binary file not shown.
13 changes: 12 additions & 1 deletion examples/nodenumber/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,6 @@ func Test_NodeNumber(t *testing.T) {
t.Run(name, func(t *testing.T) {
plugin := &NodeNumber{reverse: reverse}
state := testCycleState{}

status := plugin.PreScore(state, tc.pod, nil)
if status != nil {
t.Fatalf("unexpected status: %v", status)
Expand Down Expand Up @@ -143,6 +142,18 @@ func (t testPod) GetNamespace() string {
return ""
}

func (t testPod) GetApiVersion() string {
return ""
}

func (t testPod) GetKind() string {
return "pod"
}

func (t testPod) GetResourceVersion() string {
return "v1"
}

func (t testPod) Spec() *protoapi.PodSpec {
nodeName := t.nodeName
return &protoapi.PodSpec{NodeName: &nodeName}
Expand Down
12 changes: 12 additions & 0 deletions guest/api/proto/proto.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,18 +19,28 @@ package proto

import api "sigs.k8s.io/kube-scheduler-wasm-extension/kubernetes/proto/api"

type KObject interface {
Metadata

GetKind() string
GetApiVersion() string
}

// Metadata are fields on top-level types, used for logging and metrics.
type Metadata interface {
GetUid() string
GetName() string
GetNamespace() string
GetResourceVersion() string
}

type Node interface {
Metadata

Spec() *api.NodeSpec
Status() *api.NodeStatus
GetKind() string
GetApiVersion() string
}

type NodeList interface {
Expand All @@ -42,4 +52,6 @@ type Pod interface {

Spec() *api.PodSpec
Status() *api.PodStatus
GetKind() string
GetApiVersion() string
}
33 changes: 33 additions & 0 deletions guest/eventrecorder/api/eventrecorder.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
Copyright 2023 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package api

import (
proto "sigs.k8s.io/kube-scheduler-wasm-extension/guest/api/proto"
)

// EventRecorder is a recorder that sends events to the host environment via eventrecorder.eventf.
type EventRecorder interface {
// Eventf calls EventRecorder.Eventf.
Eventf(regarding proto.KObject, related proto.KObject, eventtype, reason, action, note string)
}

type UnimplementedEventRecorder struct{}

// Eventf implements events.EventRecorder.Eventf()
func (UnimplementedEventRecorder) Eventf(regarding proto.KObject, related proto.KObject, eventtype, reason, action, note string) {
}
36 changes: 36 additions & 0 deletions guest/eventrecorder/api/eventrecorder_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
Copyright 2023 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package api_test

import (
"sigs.k8s.io/kube-scheduler-wasm-extension/guest/api/proto"
"sigs.k8s.io/kube-scheduler-wasm-extension/guest/eventrecorder/api"
)

var (
eventrecorder api.EventRecorder = api.UnimplementedEventRecorder{}
pod proto.Pod
node proto.Node
)

func ExampleEventRecorder() {
eventrecorder.Eventf(pod, pod, "event", "reason", "action", "note")
eventrecorder.Eventf(node, node, "event", "reason", "action", "note")

// Output:
//
}
53 changes: 53 additions & 0 deletions guest/eventrecorder/eventrecorder.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/*
Copyright 2023 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package eventrecorder

import (
"encoding/json"
"runtime"

"sigs.k8s.io/kube-scheduler-wasm-extension/guest/api/proto"
"sigs.k8s.io/kube-scheduler-wasm-extension/guest/eventrecorder/api"
"sigs.k8s.io/kube-scheduler-wasm-extension/guest/eventrecorder/internal"
"sigs.k8s.io/kube-scheduler-wasm-extension/guest/internal/mem"
)

func Get() api.EventRecorder {
return eventRecorderInstance
}

var eventRecorderInstance api.EventRecorder = &internal.EventRecorder{
EventfFn: EventfFn,
}

func EventfFn(msg internal.EventMessage) {
jsonByte, err := json.Marshal(msg)
if err != nil {
panic(err)
}
jsonStr := string(jsonByte)
ptr, size := mem.StringToPtr(jsonStr)
eventf(ptr, size)
runtime.KeepAlive(jsonStr)
}

// Eventf is a convenience that calls the same method documented on api.Eventf.
//
// Note: See Info for unit test and benchmarking impact.
func Eventf(regarding proto.KObject, related proto.KObject, eventtype, reason, action, note string) {
eventRecorderInstance.Eventf(regarding, related, eventtype, reason, action, note)
}
31 changes: 31 additions & 0 deletions guest/eventrecorder/eventrecorder_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*
Copyright 2023 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package eventrecorder_test

import (
"sigs.k8s.io/kube-scheduler-wasm-extension/guest/api/proto"
"sigs.k8s.io/kube-scheduler-wasm-extension/guest/eventrecorder"
)

var pod proto.Pod

func ExampleEventf() {
eventrecorder.Eventf(pod, pod, "event", "reason", "action", "note")

// Output:
//
}
22 changes: 22 additions & 0 deletions guest/eventrecorder/imports.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
//go:build tinygo.wasm

/*
Copyright 2023 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package eventrecorder

//go:wasmimport k8s.io/scheduler handle.eventrecorder.eventf
func eventf(ptr, size uint32)
Loading
Loading