diff --git a/guest/api/types.go b/guest/api/types.go index c8e62d80..ad82401e 100644 --- a/guest/api/types.go +++ b/guest/api/types.go @@ -128,6 +128,13 @@ type BindPlugin interface { Bind(state CycleState, pod proto.Pod, nodeName string) *Status } +// PostBindPlugin is a WebAssembly implementation of framework.PostBindPlugin. +type PostBindPlugin interface { + Plugin + + PostBind(state CycleState, pod proto.Pod, nodeName string) +} + type NodeInfo interface { // Metadata is a convenience that triggers Get. proto.Metadata diff --git a/guest/bind/bind.go b/guest/bind/bind.go index cf67677a..fb54679f 100644 --- a/guest/bind/bind.go +++ b/guest/bind/bind.go @@ -44,7 +44,7 @@ func SetPlugin(bindPlugin api.BindPlugin) { panic("nil bindPlugin") } bind = bindPlugin - plugin.MustSet(bindPlugin) + plugin.MustSet(bind) } // prevent unused lint errors (lint is run with normal go). diff --git a/guest/plugin/plugin.go b/guest/plugin/plugin.go index 6caf34ed..0d26cdd5 100644 --- a/guest/plugin/plugin.go +++ b/guest/plugin/plugin.go @@ -22,6 +22,7 @@ import ( "sigs.k8s.io/kube-scheduler-wasm-extension/guest/enqueue" "sigs.k8s.io/kube-scheduler-wasm-extension/guest/filter" "sigs.k8s.io/kube-scheduler-wasm-extension/guest/internal/prefilter" + "sigs.k8s.io/kube-scheduler-wasm-extension/guest/postbind" "sigs.k8s.io/kube-scheduler-wasm-extension/guest/postfilter" "sigs.k8s.io/kube-scheduler-wasm-extension/guest/prebind" "sigs.k8s.io/kube-scheduler-wasm-extension/guest/prescore" @@ -73,4 +74,7 @@ func Set(plugin api.Plugin) { if plugin, ok := plugin.(api.BindPlugin); ok { bind.SetPlugin(plugin) } + if plugin, ok := plugin.(api.PostBindPlugin); ok { + postbind.SetPlugin(plugin) + } } diff --git a/guest/postbind/postbind.go b/guest/postbind/postbind.go new file mode 100644 index 00000000..08d2fbb8 --- /dev/null +++ b/guest/postbind/postbind.go @@ -0,0 +1,72 @@ +/* + 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 postbind exports an api.PostBindPlugin to the host. +package postbind + +import ( + "sigs.k8s.io/kube-scheduler-wasm-extension/guest/api" + "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" +) + +// postbind is the current plugin assigned with SetPlugin. +var postbind api.PostBindPlugin + +// SetPlugin should be called in `main` to assign an api.PostBindPlugin +// instance. +// +// For example: +// +// func main() { +// plugin := bindPlugin{} +// bind.SetPlugin(plugin) +// postbind.SetPlugin(plugin) +// } +// +// type bindPlugin struct{} +// +// func (bindPlugin) Bind(state api.CycleState, pod proto.Pod, nodeName string) (status *api.Status) { +// // Write state you need on Bind +// } +// +// func (bindPlugin) PostBind(state api.CycleState, pod proto.Pod, nodeName string) { +// // Write state you need on PostBind +// } +func SetPlugin(postbindPlugin api.PostBindPlugin) { + if postbindPlugin == nil { + panic("nil postbindPlugin") + } + postbind = postbindPlugin + plugin.MustSet(postbind) +} + +// prevent unused lint errors (lint is run with normal go). +var _ func() = _postbind + +// _postbind is only exported to the host. +// +//export postbind +func _postbind() { //nolint + if postbind == nil { // Then, the user didn't define one. + // This is likely caused by use of plugin.Set(p), where 'p' didn't + // implement PostBindPlugin: return success. + return + } + + nodeName := imports.NodeName() + // The parameters passed are lazy with regard to host functions. This means + // a no-op plugin should not have any unmarshal penalty. + postbind.PostBind(cyclestate.Values, cyclestate.Pod, nodeName) +} diff --git a/guest/postfilter/postfilter.go b/guest/postfilter/postfilter.go index 17522f82..94e85eac 100644 --- a/guest/postfilter/postfilter.go +++ b/guest/postfilter/postfilter.go @@ -57,7 +57,7 @@ func SetPlugin(postfilterPlugin api.PostFilterPlugin) { panic("nil postfilterPlugin") } postfilter = postfilterPlugin - plugin.MustSet(postfilterPlugin) + plugin.MustSet(postfilter) } // prevent unused lint errors (lint is run with normal go). diff --git a/guest/prebind/prebind.go b/guest/prebind/prebind.go index 16e9f2ab..cd8b9a0a 100644 --- a/guest/prebind/prebind.go +++ b/guest/prebind/prebind.go @@ -29,27 +29,27 @@ var prebind api.PreBindPlugin // // For example: // -// func main() { -// plugin := bindPlugin{} -// bind.SetPlugin(plugin) -// prebind.SetPlugin(plugin) -// } +// func main() { +// plugin := bindPlugin{} +// bind.SetPlugin(plugin) +// prebind.SetPlugin(plugin) +// } // -// type bindPlugin struct{} +// type bindPlugin struct{} // -// func (bindPlugin) Bind(state api.CycleState, pod proto.Pod, nodeName string) (status *api.Status) { -// // Write state you need on Bind -// } +// func (bindPlugin) Bind(state api.CycleState, pod proto.Pod, nodeName string) (status *api.Status) { +// // Write state you need on Bind +// } // -// func (bindPlugin) PreBind(state api.CycleState, pod proto.Pod, nodeName string) (status *api.Status) { -// // Write state you need on Bind -// } +// func (bindPlugin) PreBind(state api.CycleState, pod proto.Pod, nodeName string) (status *api.Status) { +// // Write state you need on Bind +// } func SetPlugin(prebindPlugin api.PreBindPlugin) { if prebindPlugin == nil { panic("nil prebindPlugin") } prebind = prebindPlugin - plugin.MustSet(prebindPlugin) + plugin.MustSet(prebind) } // prevent unused lint errors (lint is run with normal go). diff --git a/guest/testdata/bind/main.go b/guest/testdata/bind/main.go index 0327b88e..96fd7463 100644 --- a/guest/testdata/bind/main.go +++ b/guest/testdata/bind/main.go @@ -22,12 +22,14 @@ 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/bind" + "sigs.k8s.io/kube-scheduler-wasm-extension/guest/postbind" "sigs.k8s.io/kube-scheduler-wasm-extension/guest/prebind" ) type extensionPoints interface { api.PreBindPlugin api.BindPlugin + api.PostBindPlugin } func main() { @@ -40,10 +42,13 @@ func main() { plugin = preBindPlugin{} case "bind": plugin = bindPlugin{} + case "postBind": + plugin = postBindPlugin{} } } prebind.SetPlugin(plugin) bind.SetPlugin(plugin) + postbind.SetPlugin(plugin) } // noopPlugin doesn't do anything, except evaluate each parameter. @@ -63,6 +68,12 @@ func (noopPlugin) Bind(state api.CycleState, pod proto.Pod, nodeName string) *ap return nil } +func (noopPlugin) PostBind(state api.CycleState, pod proto.Pod, nodeName string) { + _, _ = state.Read("ok") + _ = pod.Spec() + _ = nodeName +} + // preBindPlugin returns the length of nodeName type preBindPlugin struct{ noopPlugin } @@ -84,3 +95,12 @@ func (bindPlugin) Bind(_ api.CycleState, _ proto.Pod, nodeName string) *api.Stat } return &api.Status{Code: api.StatusCode(status), Reason: "name is " + nodeName} } + +// postBindPlugin returns nothing +type postBindPlugin struct{ noopPlugin } + +func (postBindPlugin) PostBind(_ api.CycleState, _ proto.Pod, nodeName string) { + if nodeName == "bad" { + panic("name is bad") + } +} diff --git a/guest/testdata/bind/main.wasm b/guest/testdata/bind/main.wasm index f8e94cfb..ee8b089e 100755 Binary files a/guest/testdata/bind/main.wasm and b/guest/testdata/bind/main.wasm differ diff --git a/guest/testdata/cyclestate/main.go b/guest/testdata/cyclestate/main.go index c7da1d0a..995833dd 100644 --- a/guest/testdata/cyclestate/main.go +++ b/guest/testdata/cyclestate/main.go @@ -27,6 +27,7 @@ import ( "sigs.k8s.io/kube-scheduler-wasm-extension/guest/bind" "sigs.k8s.io/kube-scheduler-wasm-extension/guest/enqueue" "sigs.k8s.io/kube-scheduler-wasm-extension/guest/filter" + "sigs.k8s.io/kube-scheduler-wasm-extension/guest/postbind" "sigs.k8s.io/kube-scheduler-wasm-extension/guest/postfilter" "sigs.k8s.io/kube-scheduler-wasm-extension/guest/prebind" "sigs.k8s.io/kube-scheduler-wasm-extension/guest/prefilter" @@ -66,6 +67,7 @@ func main() { scoreextensions.SetPlugin(plugin) prebind.SetPlugin(plugin) bind.SetPlugin(plugin) + postbind.SetPlugin(plugin) } const ( @@ -178,7 +180,6 @@ func (statePlugin) PreBind(state api.CycleState, pod proto.Pod, _ string) (statu if unsafe.Pointer(pod.Spec()) != unsafe.Pointer(podSpec) { panic("didn't cache pod from score") } - mustFilterState(state) if _, ok := state.Read(preBindStateKey); ok { panic("didn't reset pre-bind state on pre-bind") } else { @@ -191,7 +192,6 @@ func (statePlugin) Bind(state api.CycleState, pod proto.Pod, _ string) (status * if unsafe.Pointer(pod.Spec()) != unsafe.Pointer(podSpec) { panic("didn't cache pod from pre-bind") } - mustFilterState(state) if val, ok := state.Read(preBindStateKey); !ok { panic("didn't propagate pre-bind state from pre-bind") } else { @@ -200,6 +200,17 @@ func (statePlugin) Bind(state api.CycleState, pod proto.Pod, _ string) (status * return } +func (statePlugin) PostBind(state api.CycleState, pod proto.Pod, _ string) { + if unsafe.Pointer(pod.Spec()) != unsafe.Pointer(podSpec) { + panic("didn't cache pod from pre-bind") + } + if val, ok := state.Read(preBindStateKey); !ok { + panic("didn't propagate pre-bind state from pre-bind") + } else if _, ok = val.(preBindStateVal)["bind"]; !ok { + panic("bind value lost propagating from bind") + } +} + // mustNotScoreState ensures that score state, written after filter, cannot // be read by extension points before it. // diff --git a/guest/testdata/cyclestate/main.wasm b/guest/testdata/cyclestate/main.wasm index 180a31ee..330cad11 100755 Binary files a/guest/testdata/cyclestate/main.wasm and b/guest/testdata/cyclestate/main.wasm differ diff --git a/internal/e2e/e2e.go b/internal/e2e/e2e.go index aceae341..e970de1e 100644 --- a/internal/e2e/e2e.go +++ b/internal/e2e/e2e.go @@ -52,6 +52,10 @@ func RunAll(ctx context.Context, t Testing, plugin framework.Plugin, pod *v1.Pod s = bindP.Bind(ctx, nil, pod, "") RequireSuccess(t, s) } + + if postbindP, ok := plugin.(framework.PostBindPlugin); ok { + postbindP.PostBind(ctx, nil, pod, "") + } return } diff --git a/internal/e2e/scheduler_perf/scheduler_perf_test.go b/internal/e2e/scheduler_perf/scheduler_perf_test.go index c4ef786c..9d854a00 100644 --- a/internal/e2e/scheduler_perf/scheduler_perf_test.go +++ b/internal/e2e/scheduler_perf/scheduler_perf_test.go @@ -91,7 +91,7 @@ var ( Metrics: map[string]*labelValues{ "scheduler_framework_extension_point_duration_seconds": { label: extensionPointsLabelName, - values: []string{"PreFilter", "Filter", "PostFilter", "PreScore", "Score", "PreBind", "Bind"}, + values: []string{"PreFilter", "Filter", "PostFilter", "PreScore", "Score", "PreBind", "Bind", "PostBind"}, }, "scheduler_scheduling_attempt_duration_seconds": nil, "scheduler_pod_scheduling_duration_seconds": nil, diff --git a/scheduler/plugin/guest.go b/scheduler/plugin/guest.go index 0297909f..13f19b56 100644 --- a/scheduler/plugin/guest.go +++ b/scheduler/plugin/guest.go @@ -24,6 +24,7 @@ import ( "github.com/tetratelabs/wazero" wazeroapi "github.com/tetratelabs/wazero/api" + "k8s.io/klog/v2" "k8s.io/kubernetes/pkg/scheduler/framework" ) @@ -38,6 +39,7 @@ const ( guestExportNormalizeScore = "normalizescore" guestExportPreBind = "prebind" guestExportBind = "bind" + guestExportPostBind = "postbind" ) type guest struct { @@ -52,6 +54,7 @@ type guest struct { normalizescoreFn wazeroapi.Function prebindFn wazeroapi.Function bindFn wazeroapi.Function + postbindFn wazeroapi.Function callStack []uint64 } @@ -101,6 +104,7 @@ func (pl *wasmPlugin) newGuest(ctx context.Context) (*guest, error) { normalizescoreFn: g.ExportedFunction(guestExportNormalizeScore), prebindFn: g.ExportedFunction(guestExportPreBind), bindFn: g.ExportedFunction(guestExportBind), + postbindFn: g.ExportedFunction(guestExportPostBind), callStack: callStack, }, nil } @@ -231,6 +235,16 @@ func (g *guest) bind(ctx context.Context) *framework.Status { return framework.NewStatus(framework.Code(statusCode), statusReason) } +// postBind calls guestExportPostBind. +func (g *guest) postBind(ctx context.Context) { + defer g.out.Reset() + callStack := g.callStack + logger := klog.FromContext(ctx) + if err := g.postbindFn.CallWithStack(ctx, callStack); err != nil { + logger.Error(decorateError(g.out, guestExportPostBind, err), "failed postbind") + } +} + func decorateError(out fmt.Stringer, fn string, err error) error { detail := out.String() if detail != "" { @@ -290,6 +304,11 @@ func detectInterfaces(exportedFns map[string]wazeroapi.FunctionDefinition) (inte return 0, fmt.Errorf("wasm: guest exports the wrong signature for func[%s]. should be () -> (i32)", name) } e |= iBindPlugin + case guestExportPostBind: + if len(f.ParamTypes()) != 0 || !bytes.Equal(f.ResultTypes(), []wazeroapi.ValueType{}) { + return 0, fmt.Errorf("wasm: guest exports the wrong signature for func[%s]. should be () -> ()", name) + } + e |= iPostBindPlugin } } if e == 0 { diff --git a/scheduler/plugin/plugin.go b/scheduler/plugin/plugin.go index 076a2c8f..25400d70 100644 --- a/scheduler/plugin/plugin.go +++ b/scheduler/plugin/plugin.go @@ -27,6 +27,7 @@ import ( v1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/util/sets" + "k8s.io/klog/v2" "k8s.io/kubernetes/pkg/scheduler/framework" frameworkruntime "k8s.io/kubernetes/pkg/scheduler/framework/runtime" ) @@ -406,7 +407,14 @@ func (pl *wasmPlugin) PostBind(ctx context.Context, state *framework.CycleState, } defer pl.pool.freeFromBinding(pod.UID) // the cycle is over, put it back into the pool. - // TODO: partially implemented for testing + params := &stack{pod: pod, nodeName: nodeName} + ctx = context.WithValue(ctx, stackKey{}, params) + logger := klog.FromContext(ctx) + if err := pl.pool.doWithSchedulingGuest(ctx, pod.UID, func(g *guest) { + g.postBind(ctx) + }); err != nil { + logger.Error(err, "doWithSchedulingGuest Failed") + } } var _ framework.PermitPlugin = (*wasmPlugin)(nil) diff --git a/scheduler/plugin/plugin_test.go b/scheduler/plugin/plugin_test.go index d8a6290a..9ff041b9 100644 --- a/scheduler/plugin/plugin_test.go +++ b/scheduler/plugin/plugin_test.go @@ -17,6 +17,7 @@ package wasm_test import ( + "bytes" "context" "fmt" "io" @@ -27,6 +28,7 @@ import ( "os" "path" "reflect" + "strings" "testing" "github.com/google/uuid" @@ -114,6 +116,18 @@ func Test_guestPool_bindingCycles(t *testing.T) { t.Fatalf("expected bindingCycles to have entry for `nextPod`, have %v", bindingCycles) } + // nextPod is going to PreBind process. + status = pl.PreBind(ctx, nil, nextPod, "node") + if !status.IsSuccess() { + t.Fatalf("prebind failed: %v", status) + } + + // nextPod is going to Bind process. + status = pl.Bind(ctx, nil, nextPod, "node") + if !status.IsSuccess() { + t.Fatalf("bind failed: %v", status) + } + // nextPod is rejected in the binding cycle. pl.PostBind(ctx, nil, nextPod, "node") if len(pl.GetBindingCycles()) != 0 { @@ -1138,6 +1152,134 @@ wasm stack trace: } } +func TestPostBind(t *testing.T) { + tests := []struct { + name string + guestURL string + args []string + globals map[string]int32 + pod *v1.Pod + nodeName string + expectedError string + }{ + { + name: "Success", + args: []string{"test", "postBind"}, + pod: test.PodSmall, + nodeName: "good", + }, + { + name: "Error", + args: []string{"test", "postBind"}, + pod: test.PodSmall, + nodeName: "bad", + expectedError: `"failed postbind" err=< + wasm: postbind error: panic: name is bad + + wasm error: unreachable + wasm stack trace: + .runtime._panic(i32,i32) + .postbind() + >`, + }, + { + name: "reachable: flag is 0", + guestURL: test.URLTestPostBindFromGlobal, + pod: test.PodSmall, + nodeName: test.NodeSmall.Name, + globals: map[string]int32{"flag": 0}, + }, + { + name: "unreachable: flag is 1", + guestURL: test.URLTestPostBindFromGlobal, + pod: test.PodSmall, + nodeName: test.NodeSmall.Name, + globals: map[string]int32{"flag": 1}, + expectedError: `"failed postbind" err=< + wasm: postbind error: wasm error: unreachable + wasm stack trace: + postbind_from_global.$0() + >`, + }, + { + name: "panic", + guestURL: test.URLErrorPanicOnPostBind, + pod: test.PodSmall, + nodeName: test.NodeSmall.Name, + expectedError: `"failed postbind" err=< + wasm: postbind error: panic! + wasm error: unreachable + wasm stack trace: + panic_on_postbind.$1() + >`, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + guestURL := tc.guestURL + if guestURL == "" { + guestURL = test.URLTestBind + } + + p, err := wasm.NewFromConfig(ctx, "wasm", wasm.PluginConfig{GuestURL: guestURL, Args: tc.args}) + if err != nil { + t.Fatal(err) + } + defer p.(io.Closer).Close() + + if len(tc.globals) > 0 { + pl := wasm.NewTestWasmPlugin(p) + pl.SetGlobals(tc.globals) + } + + // Because postBind doesn't return any values, we use klog's error for testing. + klogErr, err := captureStderr(func() { + p.(framework.PostBindPlugin).PostBind(ctx, nil, tc.pod, tc.nodeName) + }) + if err != nil { + t.Fatalf("got an error during captureStderr %v", err) + } + if want, have := tc.expectedError, extractMessage(klogErr); want != have { + t.Fatalf("unexpected log: want%v, have%v", want, have) + } + }) + } +} + +// Extracts and trims the actual log message from a formatted klog string +// (klog includes timestamp before actual log message) +func extractMessage(log string) string { + parts := strings.SplitN(log, "]", 2) + if len(parts) < 2 { + return "" + } + return strings.TrimSpace(parts[1]) +} + +// captureStderr temporarily redirects the standard error output to capture any data written to it. +// This function is particularly useful for capturing klog's error output during tests. +// It takes a function f, executes it, and captures anything written to stderr during its execution. +// After the function execution, it restores the original stderr and returns the captured output as a string. +func captureStderr(f func()) (string, error) { + originalStderr := os.Stderr + r, w, _ := os.Pipe() + os.Stderr = w + + f() + + w.Close() + os.Stderr = originalStderr + + var buf bytes.Buffer + if _, err := io.Copy(&buf, r); err != nil { + return "", err + } + r.Close() + + return buf.String(), nil +} + func requireError(t *testing.T, err error, expectedError string) { var have string if err != nil { diff --git a/scheduler/test/testdata.go b/scheduler/test/testdata.go index 49035281..ba4cc362 100644 --- a/scheduler/test/testdata.go +++ b/scheduler/test/testdata.go @@ -38,6 +38,8 @@ var URLErrorPanicOnPreBind = localURL(pathWatError("panic_on_prebind")) var URLErrorPanicOnBind = localURL(pathWatError("panic_on_bind")) +var URLErrorPanicOnPostBind = localURL(pathWatError("panic_on_postbind")) + var URLErrorPanicOnScoreExtensions = localURL(pathWatError("panic_on_scoreextensions")) var URLErrorScoreExtensionsWithoutScore = localURL(pathWatError("scoreextensions_without_score")) @@ -68,6 +70,8 @@ var URLTestScoreFromGlobal = localURL(pathWatTest("score_from_global")) var URLTestPreBindFromGlobal = localURL(pathWatTest("prebind_from_global")) +var URLTestPostBindFromGlobal = localURL(pathWatTest("postbind_from_global")) + var URLTestBindFromGlobal = localURL(pathWatTest("bind_from_global")) var URLTestBind = localURL(pathTinyGoTest("bind")) diff --git a/scheduler/test/testdata/error/panic_on_postbind.wasm b/scheduler/test/testdata/error/panic_on_postbind.wasm new file mode 100644 index 00000000..663208e0 Binary files /dev/null and b/scheduler/test/testdata/error/panic_on_postbind.wasm differ diff --git a/scheduler/test/testdata/error/panic_on_postbind.wat b/scheduler/test/testdata/error/panic_on_postbind.wat new file mode 100644 index 00000000..2799f63f --- /dev/null +++ b/scheduler/test/testdata/error/panic_on_postbind.wat @@ -0,0 +1,32 @@ +;; panic_on_postbind is a postbind which issues an unreachable instruction +;; after writing an error to stdout. This simulates a panic in TinyGo. +(module $panic_on_postbind + ;; Import the fd_write function from wasi, used in TinyGo for println. + (import "wasi_snapshot_preview1" "fd_write" + (func $wasi.fd_write (param $fd i32) (param $iovs i32) (param $iovs_len i32) (param $result.size i32) (result (;errno;) i32))) + + ;; Allocate the minimum amount of memory, 1 page (64KB). + (memory (export "memory") 1 1) + + ;; Pre-populate memory with the panic message, in iovec format + (data (i32.const 0) "\08") ;; iovs[0].offset + (data (i32.const 4) "\06") ;; iovs[0].length + (data (i32.const 8) "panic!") ;; iovs[0] + + ;; On postbind, write "panic!" to stdout and crash. + (func (export "postbind") + ;; Write the panic to stdout via its iovec [offset, len]. + (call $wasi.fd_write + (i32.const 1) ;; stdout + (i32.const 0) ;; where's the iovec + (i32.const 1) ;; only one iovec + (i32.const 0) ;; overwrite the iovec with the ignored result. + ) + drop ;; ignore the errno returned + + ;; Issue the unreachable instruction instead of returning a code + (unreachable)) + + ;; We require exporting bind with postbind + (func (export "bind") (result i32) (unreachable)) +) diff --git a/scheduler/test/testdata/test/postbind_from_global.wasm b/scheduler/test/testdata/test/postbind_from_global.wasm new file mode 100644 index 00000000..84ddffc6 Binary files /dev/null and b/scheduler/test/testdata/test/postbind_from_global.wasm differ diff --git a/scheduler/test/testdata/test/postbind_from_global.wat b/scheduler/test/testdata/test/postbind_from_global.wat new file mode 100644 index 00000000..47207709 --- /dev/null +++ b/scheduler/test/testdata/test/postbind_from_global.wat @@ -0,0 +1,18 @@ +;; postbind_from_global lets us test the value range of status_code +(module $postbind_from_global + + ;; flag is set by the host. + (global $flag (export "flag_global") (mut i32) (i32.const 0)) + + ;; Allocate the minimum amount of memory, 1 page (64KB). + (memory (export "memory") 1 1) + + (func (export "postbind") + (if (i32.eq (global.get $flag) (i32.const 1)) + (unreachable) + ) + ) + + ;; We require exporting bind with postbind + (func (export "bind") (result i32) (unreachable)) +)