visit
This post will cover how to get set up for writing Kong plugins.
$ mkdir kong-go-plugin
$ cd kong-go-plugin/
~/kong-go-plugin$ go mod init kong-go-plugin
go: creating new go.mod: module kong-go-plugin
Next, we run go get
and go build
to build the plugin server:
~/kong-go-plugin$ go get -d -v github.com/Kong/go-pluginserver
go: downloading github.com/Kong/go-pluginserver v0.6.1
go: downloading github.com/Kong/go-pdk v0.6.0
go: downloading github.com/ugorji/go v1.2.1
go: downloading github.com/ugorji/go/codec v1.2.1
go get: added github.com/Kong/go-pluginserver v0.6.1
~/kong-go-plugin$ go build github.com/Kong/go-pluginserver
By default, Kong expects go-pluginserver
to be in /usr/local/bin
, but you can modify this behavior by changing the value for go_pluginserver_exe
in your Kong configuration. For the sake of consistency, let’s move the newly built binary to the default location.
~/kong-go-plugin$ sudo mv go-pluginserver /usr/local/bin/
One other default that we’ll want to modify is go_plugins_dir
which defaults to off
. This is where Kong looks for our Go plugins. For this example, we’ll use $HOME/goplugins/
, so if you’re following along, you’ll need to update your configuration to match.
failed to open plugin kong: plugin.Open("/path/go-plugins/myplugin"): plugin was built with a different version of package github.com/Kong/go-pdk/bridge
Our next step is to write the plugin and verify it works as expected. We’ll start with a simple bit of code to add a header to a response. If you’re following along, you can copy this directly into a file called goplug.go
:
package main
import (
"github.com/Kong/go-pdk"
)
type Config struct {
Attach bool
}
func New() interface{} {
return &Config{}
}
func (c *Config) Access(kong *pdk.PDK) {
if c.Attach {
kong.ServiceRequest.SetHeader("x-goplug", "Set by custom go plugin")
}
}
If you’ve worked with Go before, this should be pretty straightforward. That said, you can find more details in the . The documentation explains the structs and functions, and it provides a full list of the phases you can tap into. We’re using the Access
phase at this point, so we can attach the header before it gets proxied to the upstream service. This is useful in cases where you want to add data to the response based on some incoming criteria that the upstream service can use.
Next, we need to build our plugin and “install” it with go build -buildmode plugin goplug.go && mv goplug.so ~/goplugins/
~/kong-go-plugin$ sudo kong restart
Now, our plugin is loaded and ready to use, but we still need a service to use it on. I’ve used and added the following to a kong.yaml
file to test out the plugin. All you need to do is run deck sync
once you’ve created the kong.yaml
file.
_format_version: "1.1"
services:
- host: mockbin.org
name: example_service
port: 80
protocol: http
plugins:
- name: goplug
config:
attach: true
routes:
- name: mocking
paths:
- /mock
strip_path: true
~/kong-go-plugin$ curl -s -i -X GET //localhost:8000/mock/request \
| grep "x-goplug"
x-goplug: Set by custom go plugin
Success! For good measure, let’s tie into one more phase of the request and response lifecycle. In this next function, we’ll tap into the Response
phase to add a debugging header in case our plugin runs into issues.
func (c *Config) Response(kong *pdk.PDK) {
v := runtime.Version()
kong.Response.AddHeader("x-goplug-go-version", v)
}
Add the above function to goplug.go
, then repeat the process to rebuild the plugin. Next, restart Kong. When we rerun our curl command, we see that x-goplug
is included with the request to the upstream service, but it is removed before sending a response back to the client.
$ curl -s -i -X GET //localhost:8000/mock/request \
| grep "x-goplug-go-version"
x-goplug-go-version: go1.16.5