Skip to content

Commit

Permalink
Merge pull request kubernetes-sigs#65 from codefromthecrypt/metadata
Browse files Browse the repository at this point in the history
guest: Add proto.Metadata and fix prefilter unmarshalling
  • Loading branch information
k8s-ci-robot authored Jul 28, 2023
2 parents 5cc5e14 + 9a0062f commit ce8959d
Show file tree
Hide file tree
Showing 22 changed files with 224 additions and 111 deletions.
Binary file modified examples/nodenumber/main.wasm
Binary file not shown.
13 changes: 4 additions & 9 deletions examples/nodenumber/plugin/plugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ func (pl *NodeNumber) EventsToRegister() []api.ClusterEvent {

// PreScore implements api.PreScorePlugin
func (pl *NodeNumber) PreScore(state api.CycleState, pod proto.Pod, _ proto.NodeList) *api.Status {
podnum, ok := lastNumber(pod.Spec().NodeName)
podnum, ok := lastNumber(pod.Spec().GetNodeName())
if !ok {
return nil // return success even if its suffix is non-number.
}
Expand All @@ -105,7 +105,7 @@ func (pl *NodeNumber) Score(state api.CycleState, _ proto.Pod, nodeName string)
var match bool
if data, ok := state.Read(preScoreStateKey); ok {
// Match is when there is a last digit, and it is the pod suffix.
nodenum, ok := lastNumber(&nodeName)
nodenum, ok := lastNumber(nodeName)
match = ok && data.(*preScoreState).podSuffixNumber == nodenum
} else {
// Match is also when there is no pod spec node name.
Expand All @@ -122,13 +122,8 @@ func (pl *NodeNumber) Score(state api.CycleState, _ proto.Pod, nodeName string)
return 0, nil
}

// lastNumber returns the last number in the string being pointed to or false.
func lastNumber(ptr *string) (uint8, bool) {
// Early exit on nil or empty string.
if ptr == nil {
return 0, false
}
str := *ptr
// lastNumber returns the last number in the string or false.
func lastNumber(str string) (uint8, bool) {
if len(str) == 0 {
return 0, false
}
Expand Down
49 changes: 26 additions & 23 deletions examples/nodenumber/plugin/plugin_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ import (
"sigs.k8s.io/kube-scheduler-wasm-extension/guest/api"
"sigs.k8s.io/kube-scheduler-wasm-extension/guest/api/proto"
protoapi "sigs.k8s.io/kube-scheduler-wasm-extension/kubernetes/proto/api"
meta "sigs.k8s.io/kube-scheduler-wasm-extension/kubernetes/proto/meta"
)

func Test_NodeNumber(t *testing.T) {
Expand All @@ -36,11 +35,11 @@ func Test_NodeNumber(t *testing.T) {
{name: "empty,empty", pod: &testPod{}, nodeName: "", expectedMatch: true},
{name: "empty,letter", pod: &testPod{}, nodeName: "a", expectedMatch: true},
{name: "empty,digit", pod: &testPod{}, nodeName: "1", expectedMatch: true},
{name: "letter,letter", pod: &testPod{nodeName: stringPtr("a")}, nodeName: "a", expectedMatch: true},
{name: "letter,digit", pod: &testPod{nodeName: stringPtr("a")}, nodeName: "1", expectedMatch: true},
{name: "digit,letter", pod: &testPod{nodeName: stringPtr("1")}, nodeName: "a", expectedMatch: false},
{name: "digit,digit", pod: &testPod{nodeName: stringPtr("1")}, nodeName: "1", expectedMatch: true},
{name: "digit,different digit", pod: &testPod{nodeName: stringPtr("1")}, nodeName: "2", expectedMatch: false},
{name: "letter,letter", pod: &testPod{nodeName: "a"}, nodeName: "a", expectedMatch: true},
{name: "letter,digit", pod: &testPod{nodeName: "a"}, nodeName: "1", expectedMatch: true},
{name: "digit,letter", pod: &testPod{nodeName: "1"}, nodeName: "a", expectedMatch: false},
{name: "digit,digit", pod: &testPod{nodeName: "1"}, nodeName: "1", expectedMatch: true},
{name: "digit,different digit", pod: &testPod{nodeName: "1"}, nodeName: "2", expectedMatch: false},
}

for _, reverse := range []bool{false, true} {
Expand Down Expand Up @@ -82,18 +81,17 @@ func Test_NodeNumber(t *testing.T) {
func Test_lastNumber(t *testing.T) {
tests := []struct {
name string
input *string
input string
expectedDigit uint8
expectedOk bool
}{
{name: "nil"},
{name: "empty", input: stringPtr("")},
{name: "not digit", input: stringPtr("a")},
{name: "unicode", input: stringPtr("ó")},
{name: "middle digit", input: stringPtr("a1a")},
{name: "digit after letter", input: stringPtr("a1"), expectedDigit: 1, expectedOk: true},
{name: "digit after digit", input: stringPtr("12"), expectedDigit: 2, expectedOk: true},
{name: "digit after unicode", input: stringPtr("ó2"), expectedDigit: 2, expectedOk: true},
{name: "empty", input: ""},
{name: "not digit", input: "a"},
{name: "unicode", input: "ó"},
{name: "middle digit", input: "a1a"},
{name: "digit after letter", input: "a1", expectedDigit: 1, expectedOk: true},
{name: "digit after digit", input: "12", expectedDigit: 2, expectedOk: true},
{name: "digit after unicode", input: "ó2", expectedDigit: 2, expectedOk: true},
}

for _, tc := range tests {
Expand All @@ -109,10 +107,6 @@ func Test_lastNumber(t *testing.T) {
}
}

func stringPtr(s string) *string {
return &s
}

var _ api.CycleState = testCycleState{}

type testCycleState map[string]any
Expand All @@ -134,15 +128,24 @@ var _ proto.Pod = &testPod{}

// testPod is test data just to set the nodeName
type testPod struct {
nodeName *string
nodeName string
}

func (t testPod) Metadata() *meta.ObjectMeta {
return nil
func (t testPod) GetUid() string {
return ""
}

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

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

func (t testPod) Spec() *protoapi.PodSpec {
return &protoapi.PodSpec{NodeName: t.nodeName}
nodeName := t.nodeName
return &protoapi.PodSpec{NodeName: &nodeName}
}

func (t testPod) Status() *protoapi.PodStatus {
Expand Down
17 changes: 12 additions & 5 deletions guest/api/proto/proto.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,22 +19,29 @@ package proto

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

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

type Node interface {
Metadata() *meta.ObjectMeta
Metadata

Spec() *api.NodeSpec
Status() *api.NodeStatus
}

type NodeList interface {
Metadata() *meta.ListMeta
Items() []*api.Node
Items() []Node
}

type Pod interface {
Metadata() *meta.ObjectMeta
Metadata

Spec() *api.PodSpec
Status() *api.PodStatus
}
22 changes: 5 additions & 17 deletions guest/filter/filter.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ import (
"sigs.k8s.io/kube-scheduler-wasm-extension/guest/internal/cyclestate"
"sigs.k8s.io/kube-scheduler-wasm-extension/guest/internal/imports"
"sigs.k8s.io/kube-scheduler-wasm-extension/guest/internal/plugin"
internalproto "sigs.k8s.io/kube-scheduler-wasm-extension/guest/internal/proto"
protoapi "sigs.k8s.io/kube-scheduler-wasm-extension/kubernetes/proto/api"
meta "sigs.k8s.io/kube-scheduler-wasm-extension/kubernetes/proto/meta"
)

// filter is the current plugin assigned with SetPlugin.
Expand Down Expand Up @@ -79,27 +79,15 @@ var _ api.NodeInfo = (*nodeInfo)(nil)
//
// Note: Unlike proto.Pod, this is not special cased for the scheduling cycle.
type nodeInfo struct {
node *protoapi.Node
node proto.Node
}

func (n *nodeInfo) Node() proto.Node {
return n
}

func (n *nodeInfo) Metadata() *meta.ObjectMeta {
return n.lazyNode().Metadata
}

func (n *nodeInfo) Spec() *protoapi.NodeSpec {
return n.lazyNode().Spec
}

func (n *nodeInfo) Status() *protoapi.NodeStatus {
return n.lazyNode().Status
return n.lazyNode()
}

// lazyNode lazy initializes node from imports.Node.
func (n *nodeInfo) lazyNode() *protoapi.Node {
func (n *nodeInfo) lazyNode() proto.Node {
if node := n.node; node != nil {
return node
}
Expand All @@ -108,6 +96,6 @@ func (n *nodeInfo) lazyNode() *protoapi.Node {
if err := imports.Node(msg.UnmarshalVT); err != nil {
panic(err.Error())
}
n.node = &msg
n.node = &internalproto.Node{Msg: &msg}
return n.node
}
14 changes: 11 additions & 3 deletions guest/internal/prefilter/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ 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/internal/imports"
internalproto "sigs.k8s.io/kube-scheduler-wasm-extension/guest/internal/proto"
protoapi "sigs.k8s.io/kube-scheduler-wasm-extension/kubernetes/proto/api"
meta "sigs.k8s.io/kube-scheduler-wasm-extension/kubernetes/proto/meta"
)

// Pod is exposed for the cyclestate package.
Expand Down Expand Up @@ -33,8 +33,16 @@ func (cycleState) Delete(key string) {

type pod struct{}

func (pod) Metadata() *meta.ObjectMeta {
return lazyPod().Metadata
func (pod) GetName() string {
return internalproto.GetName(lazyPod())
}

func (pod) GetNamespace() string {
return internalproto.GetNamespace(lazyPod())
}

func (pod) GetUid() string {
return internalproto.GetUid(lazyPod())
}

func (pod) Spec() *protoapi.PodSpec {
Expand Down
74 changes: 74 additions & 0 deletions guest/internal/proto/proto.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/*
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 proto

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

type object interface {
GetMetadata() *meta.ObjectMeta
}

func GetName[O object](o O) string {
if md := o.GetMetadata(); md != nil && md.Name != nil {
return *md.Name
}
return ""
}

func GetNamespace[O object](o O) string {
if md := o.GetMetadata(); md != nil && md.Namespace != nil {
return *md.Namespace
}
return ""
}

func GetUid[O object](o O) string {
if md := o.GetMetadata(); md != nil && md.Uid != nil {
return *md.Uid
}
return ""
}

var _ proto.Node = (*Node)(nil)

type Node struct {
Msg *protoapi.Node
}

func (o *Node) GetName() string {
return GetName(o.Msg)
}

func (o *Node) GetNamespace() string {
return GetNamespace(o.Msg)
}

func (o *Node) GetUid() string {
return GetUid(o.Msg)
}

func (o *Node) Spec() *protoapi.NodeSpec {
return o.Msg.Spec
}

func (o *Node) Status() *protoapi.NodeStatus {
return o.Msg.Status
}
24 changes: 24 additions & 0 deletions guest/prescore/imports.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
//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 prescore

import "sigs.k8s.io/kube-scheduler-wasm-extension/guest/internal/mem"

//go:wasmimport k8s.io/api nodeList
func k8sApiNodeList(ptr uint32, limit mem.BufLimit) (len uint32)
24 changes: 24 additions & 0 deletions guest/prescore/imports_stub.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
//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 prescore

import "sigs.k8s.io/kube-scheduler-wasm-extension/guest/internal/mem"

// k8sApiNodeList is stubbed for compilation outside TinyGo.
func k8sApiNodeList(uint32, mem.BufLimit) (len uint32) { return }
Loading

0 comments on commit ce8959d

Please sign in to comment.