Sharing data¶
This lab will add code to the existing Wasm extension that initializes the shared value and then increments the shared value in the OnHttpRequestHeaders
function. We'll also use a second (pre-built) extension that will only read the value - that's how we'll demonstrate the data is being shared between the extensions using the same VM ID.
Defining the shared data key¶
Sharing and retrieving data between different plugins is done through the SetSharedData(string, []byte, uint32)
and GetSharedData(string) : ([]byte, unint32, error)
API.
We'll start by defining the shared key name and the initial value at the top of the main.go
file:
We'll create the OnVMStart
function where we'll set the initial shared value:
// Override types.VMContext.
func (*vmContext) OnVMStart(vmConfigurationSize int) types.OnVMStartStatus {
initialValueBuf := make([]byte, 8)
binary.LittleEndian.PutUint64(initialValueBuf, sharedDataInitialValue) // (1)
if err := proxywasm.SetSharedData(sharedDataKey, initialValueBuf, 0); err != nil {
proxywasm.LogWarnf("error setting shared data on OnVMStart: %v", err)
}
proxywasm.LogInfof("[wasm-extension]: Setting initial shared value %v", sharedDataInitialValue)
return types.OnVMStartStatusOK
}
- We're converting the
sharedDataInitialValue
to a byte array using LittleEndian byte order.
Note
Make sure to add the encoding/binary
to the import statements at the top of the file.
Incrementing the value in shared data¶
Finally, we need the function that increments the shared value by 1 and the OnHttpRequestHeaders
function where we increment and output the shared value:
// Override types.DefaultHttpContext.
func (ctx *httpContext) OnHttpRequestHeaders(numHeaders int, endOfStream bool) types.Action {
for {
value, err := ctx.incrementData()
if err == nil {
proxywasm.LogInfof("incremented shared value: %d", value)
} else if errors.Is(err, types.ErrorStatusCasMismatch) {
continue
}
break
}
return types.ActionContinue
}
func (ctx *httpContext) incrementData() (uint64, error) {
value, cas, err := proxywasm.GetSharedData(sharedDataKey) // (1)
if err != nil {
proxywasm.LogWarnf("error getting shared data: %v", err)
return 0, err
}
buf := make([]byte, 8)
ret := binary.LittleEndian.Uint64(value) + 1 // (2)
binary.LittleEndian.PutUint64(buf, ret)
if err := proxywasm.SetSharedData(sharedDataKey, buf, cas); err != nil { // (3)
proxywasm.LogWarnf("error setting shared data: %v", err)
return 0, err
}
return ret, err
}
- We use
GetSharedData
and the key to read the value - We increment value from the shared data
- We write the incremented value back to the shared data using
SetSharedData
Note
We'll also have to add errors
to the import statements at the top of the file.
Here's the complete main.go
file with these changes included.
Complete main.go file
main.go | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 |
|
We can rebuild the main extension now:
Creating the second extension¶
We'll also use a second extension that reads the shared value and writes it to the log. You can check out the code below and build the extension yourself.
Code for second-extension.wasm
Alternatively, download the second-extension.wasm
to the same folder where you built the main.wasm
extension.
Creating the Envoy configuration¶
Finally, we need to modify the Envoy configuration and include the second extensions like this:
http_filters:
- name: envoy.filters.http.wasm # (1)
...
- name: envoy.filters.http.wasm
typed_config:
"@type": type.googleapis.com/udpa.type.v1.TypedStruct
type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm
value:
config:
vm_config:
vm_id: "my_vm" # (2)
runtime: "envoy.wasm.runtime.v8"
code:
local:
filename: "second-extension.wasm"
- name: envoy.filters.http.router
- Configuration for the first extension
- The
vm_id
for both extensions has to match to allow data shared between them
Complete envoy.yaml
Note
Note the vm_id
value for both extensions is the same - this is what allows us to share the data between two extensions.
Save the above YAML to envoy.yaml
and run Envoy:
When Envoy starts, we'll notice the log entry from the wasm-extension
that tells us the shared value is initialized to 1:
[2022-01-26 21:38:48.766][1346][info][wasm] [source/extensions/common/wasm/context.cc:1167] wasm log my_vm: [wasm-extension]: Setting initial shared value 1
Here's the output we'll get when we send a request to http://localhost:18000
:
curl output
[2022-01-26 21:46:09.517][1345][info][wasm] [source/extensions/common/wasm/context.cc:1167] wasm log my_vm: NewHttpContext
[2022-01-26 21:46:09.517][1345][info][wasm] [source/extensions/common/wasm/context.cc:1167] wasm log my_vm: incremented shared value: 2
[2022-01-26 21:46:09.517][1345][info][wasm] [source/extensions/common/wasm/context.cc:1167] wasm log my_vm: NewHttpContext
[2022-01-26 21:46:09.517][1345][info][wasm] [source/extensions/common/wasm/context.cc:1167] wasm log my_vm: [second-extension]: Reading shared data: 2
[2022-01-26 21:46:09.518][1345][info][wasm] [source/extensions/common/wasm/context.cc:1167] wasm log my_vm: OnHttpResponseHeaders
[2022-01-26 21:46:09.518][1345][info][wasm] [source/extensions/common/wasm/context.cc:1167] wasm log my_vm: header set: header_1=some_value_1
[2022-01-26 21:46:09.518][1345][info][wasm] [source/extensions/common/wasm/context.cc:1167] wasm log my_vm: header set: header_2=another_value
hello world
The first Wasm extension invoked is the one in main.wasm
, so the first two log entries from the NewHttpContext
are coming from that extension. At startup, we've set the initial value to 1. Then when a new request came in, we incremented the value to 2. The following two lines are coming from the second extension, where we read the shared data with value 2 (as expected). The remaining entries are response headers and, finally, the proxy's hello world
response.
To stop running the Envoy proxy, type fg
and press Ctrl+C.