Logo

dev-resources.site

for different kinds of informations.

Writing Kong plugins with Go

Published at
10/4/2023
Categories
go
apigateway
kong
Author
mfbmina
Categories
3 categories in total
go
open
apigateway
open
kong
open
Author
7 person written this
mfbmina
open
Writing Kong plugins with Go

The Kong, quoting its own documentation, is an open-source API gateway, cloud-native, platform-agnostic, scalable API Gateway distinguished for its high performance and extensibility via plugins. Kong has a lot of official plugins that allow us to customize what we need, and when there aren't any available, you can build your own.

The default language for building plugins its Lua, but other languages are supported, including Go. I don't have much experience writing code with Lua, so using Go allows me a higher development speed and quality on my plugins.

Development

Building a Go plugin is easy, as you need to follow the signature proposed by Kong's plugin development kit or PDK, but it is easy to understand. To demonstrate that, we will build a plugin that will read a request header and set a new response header.

The first thing to do is define both constants: version and priority. As the name says, we will configure the current version of the plugin and its execution priority within the other plugins. Its worth mentioning that the higher the priority, the earlier it will be executed. If you wanna understand more about it, check this post.

var Version = "0.0.1"
var Priority = 1
Enter fullscreen mode Exit fullscreen mode

After, we will define which params the plugin accepts by building a struct called Config. Here, we will receive the field message as a string.

type Config struct {
    Message string `json:"message"`
}
Enter fullscreen mode Exit fullscreen mode

All kong plugins work based on which phase access they need to run. The possible phase access are:

  • Certificate
  • Rewrite
  • Access
  • Response
  • Preread
  • Log

In this example, we will be using Access because we need to update the response before forwarding the request to the responsible microservice. You don't need to worry about it because the signatures for all phases are the same. To know more about all phase access, check this documentation.

func (conf Config) Access(kong *pdk.PDK) {
    host, err := kong.Request.GetHeader("host")
    if err != nil {
        log.Printf("Error reading 'host' header: %s", err.Error())
    }

    message := conf.Message
    if message == "" {
        message = "hello"
    }
    kong.Response.SetHeader("x-hello-from-go", fmt.Sprintf("Go says %s to %s", message, host))
}
Enter fullscreen mode Exit fullscreen mode

This function uses the PDK to read the available headers by using kong.Request.GetHeader, and later we add a response header using kong.Response.SetHeader. For more information about all available functions, you can check the Go PDK.

At last, let's check the main function:

package main

import (
    "fmt"
    "log"

    "github.com/Kong/go-pdk"
    "github.com/Kong/go-pdk/server"
)

func main() {
    server.StartServer(New, Version, Priority)
}

func New() interface{} {
    return &Config{}
}
Enter fullscreen mode Exit fullscreen mode

As we can notice, the main function initializes the Kong server, sending the Config, Version, and Priority defined before. The full code is:

package main

import (
    "fmt"
    "log"

    "github.com/Kong/go-pdk"
    "github.com/Kong/go-pdk/server"
)

var Version = "0.0.1"
var Priority = 1

func main() {
    server.StartServer(New, Version, Priority)
}

type Config struct {
    Message string `json:"message"`
}

func New() interface{} {
    return &Config{}
}

func (conf Config) Access(kong *pdk.PDK) {
    host, err := kong.Request.GetHeader("host")
    if err != nil {
        log.Printf("Error reading 'host' header: %s", err.Error())
    }

    message := conf.Message
    if message == "" {
        message = "hello"
    }
    kong.Response.SetHeader("x-hello-from-go", fmt.Sprintf("Go says %s to %s", message, host))
}
Enter fullscreen mode Exit fullscreen mode

Tests

Testing our Go plugins is also easy once the PDK gives us tools that make it straightforward. Let's test our plugin:

package main

import (
    "testing"

    "github.com/Kong/go-pdk/test"
    "github.com/stretchr/testify/assert"
)

func TestPluginWithoutConfig(t *testing.T) {
    env, err := test.New(t, test.Request{
        Method:  "GET",
        Url:     "http://example.com?q=search&x=9",
        Headers: map[string][]string{"host": {"localhost"}},
    })
    assert.NoError(t, err)

    env.DoHttps(&Config{})
    assert.Equal(t, 200, env.ClientRes.Status)
    assert.Equal(t, "Go says hello to localhost", env.ClientRes.Headers.Get("x-hello-from-go"))
}

func TestPluginWithConfig(t *testing.T) {
    env, err := test.New(t, test.Request{
        Method:  "GET",
        Url:     "http://example.com?q=search&x=9",
        Headers: map[string][]string{"host": {"localhost"}},
    })
    assert.NoError(t, err)

    env.DoHttps(&Config{Message: "nice to meet you"})
    assert.Equal(t, 200, env.ClientRes.Status)
    assert.Equal(t, "Go says nice to meet you to localhost", env.ClientRes.Headers.Get("x-hello-from-go"))
}
Enter fullscreen mode Exit fullscreen mode

Deploy

To deploy our plugin, we build an executable and must add it to the Kong image.

# Build Golang plugins

FROM golang:1.20 AS plugin-builder

WORKDIR /builder

COPY ./hello ./go_plugins/hello

RUN find ./go_plugins -maxdepth 1 -mindepth 1 -type d -not -path "*/.git*" | \
    while read dir; do \
        cd $dir && go build -o /builds/$dir main.go  ; \
    done

# Build Kong
FROM kong:3.4.0-ubuntu

COPY --from=plugin-builder ./builds/go_plugins/  ./kong/

USER kong
Enter fullscreen mode Exit fullscreen mode

Kong needs some information to be able to find and load the customized plugin, so we need to add some environment variables:

KONG_PLUGINS: "bundled,hello"
KONG_PLUGINSERVER_NAMES: hello
KONG_PLUGINSERVER_HELLO_START_CMD: /kong/hello
KONG_PLUGINSERVER_HELLO_QUERY_CMD: /kong/hello -dump
Enter fullscreen mode Exit fullscreen mode

Considerations about performance

The Kong company already did a study about the performance, and you can find it here.

Conclusion

Go is an excellent tool for writing plugins for Kong. We can have all the performance and tools it provides to us. In my case, I have also been able to speed up my deliveries and have a higher code coverage. It is also possible to isolate the plugin's logic with the PDK use, making it easy to write plugins that work for different technologies, decoupling Kong.

If you wanna check all the code shown in this post, you can access the repo.

You also can find me on Twitter, Github, or LinkedIn.

References

kong Article's
30 articles in total
Favicon
Configurable Kong API Gateway with Micronaut Services in Kotlin — A very odd Yucca tribute concert
Favicon
Protecting Applications with Kong security plugins and using StatsD to monitor system states — A healthy camera story
Favicon
Exploring Alternatives to Kong Enterprise for API Management
Favicon
From Legacy to Innovation: How APIs are Redefining Digital Experiences and Growth
Favicon
Custom plugin development with an emphasis on RSA/HMAC encryption
Favicon
Extension in Docker compose
Favicon
Kong Plugin Development: Local Development and Installation on Your Laptop/VM
Favicon
Automating Kong Konnect Configuration with Terraform
Favicon
Installing a Custom Plugin in Docker: Kong Plugin Development
Favicon
Kong Gateway - Validando configurações específicas para exposição de serviços
Favicon
Installing Custom Plugins in Kong API Gateway on Kubernetes: Helm Deployment in Hybrid Mode
Favicon
Simplifying Distributed Applications with Multi-Zone Kuma Service Mesh Deployment
Favicon
Kong Hybrid Mode Deployment: GKE and On-Prem
Favicon
Streamlining Microservices Orchestration: A Guide to Deploying Kong-Mesh Zones with Konnect
Favicon
What is deck and How can it be used in Kong? A Hands-on Guide
Favicon
Kong plugin development with breakpoint debugging
Favicon
Kuma Meshes Head-On - A beginners guide
Favicon
AWS ALB and ECS Service Connect unexpected behaviour
Favicon
Bridging IoT and Cloud: Enhancing Connectivity with Kong's TCPIngress in Kubernetes
Favicon
Demystifying Dynamic URLs: How to Build Them with Kong
Favicon
Kong Gateway on AWS EKS: A Journey into Cloud-native API Management
Favicon
Setup Kong in Almalinux 9
Favicon
Deploying Kong Gateway (OSS) in Production on AWS Using serverless Tools
Favicon
Need help in the KONG integration
Favicon
Writing Kong plugins with Go
Favicon
Escrevendo plugins para o Kong em Go
Favicon
Setup Kong Gateway with Docker
Favicon
What API Gateway to choose: Kong, Gravitee, Tyk or HAProxy?
Favicon
Observability in Kong API Gateway
Favicon
Integration Digest: April 2023

Featured ones: