GithubHelp home page GithubHelp logo

kong / go-pdk Goto Github PK

View Code? Open in Web Editor NEW
144.0 18.0 48.0 233 KB

Write Kong plugins in Go! ๐Ÿฆ

Home Page: https://pkg.go.dev/github.com/Kong/go-pdk

License: Apache License 2.0

Go 99.40% Makefile 0.60%
go golang kong kong-gateway kong-plugin

go-pdk's Introduction

go-pdk's People

Contributors

barockok avatar ctrox avatar dela1974 avatar dependabot[bot] avatar eyal-shalev avatar fffonion avatar gszr avatar hishamhm avatar javierguerragiraldez avatar jgramoll avatar kikito avatar lvermeire avatar mayocream avatar sayboras avatar starlightibuki avatar syanukov avatar team-eng-enablement avatar winslowdibona avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

go-pdk's Issues

go test: fails, when server.StartServer(...) is in main - violated testing rules

I want to integrate my plugin with the go-pdk server:

func main() {
	log.Info().Str("version", version).Msg("Starting pdk server")
	server.StartServer(New, version, 100)
}

If I run go test I get

flag provided but not defined: -test.paniconexit0
Usage of /tmp/go-build1002799689/b001/bm-authx.test:
  -dump
        Dump info about plugins
  -help
        Show usage info
  -kong-prefix string
        Kong prefix path (specified by the -p argument commonly used in the kong cli) (default
"/usr/local/kong")
exit status 2
FAIL    bm-authx        0.003s

After reading through docs at https://golang.org/doc/go1.13#testing

As a result, testing flags are now only registered when running a test binary, and packages that call flag.Parse during package initialization may cause tests to fail.

I think https://github.com/Kong/go-pdk/blob/master/server/server.go#L32 violates the testing rules for golang, e.g.:

func init() {
	flag.Parse()
	// ...
}

how to set kong.ctx?

There only getXXX methods inkong/db/dao/plugins/go.lua and bridge.go, can I have a way to set kong.ctx in go plugin?

To add CONTRIBUTE.md

This surely helps for other developer to contribute into this repo. Moreover, we can help the predefinied standard and guideline.

Feature request: plugin instance lifecycle hooks

Currently, I don't think there's a simple way to perform expensive initialization steps that depend on each plugin instance Config attributes.

For instance, I'm currently working on a (private) plugin that has a parameter_name config setting, which refers to AWS SSM Parameter Store. This value is relatively stable, can be cached for long periods, and is needed on my Access implementation.

Currently, I have to do something along these lines:

type Config struct {
    ParameterName `json:"parameter_name"`
    parameterValueInit sync.Once
    parameterValue string
}

func (c *Config) Access(kong *pdk.Pdk) {
  c.parameterValueInit.Do(func() {
    c.parameterValue = actuallyGetParameter(c.ParameterName)
  })

  // actually use c.parameterValue
}

Which means that the first request per plugin instance is burdened with this extra latency.

At first glance, the plugin constructor function would be the place for this kind of expensive shared initialization, but at that point I don't have ParameterName yet. Which means that I can only perform initialization that is global and doesn't depend on plugin configuration.


My suggestion here would be to introduce some kind of "init" hook, called after plugin instance construction but before any requests are served to the plugin instance. If applied to the example above, it could become something like this:

type Config struct {
    ParameterName `json:"parameter_name"`
    parameterValue string
    // Once is no longer necessary
}

// only called once per plugin instance
// assuming ParameterName is already unmarshalled here
func (c *Config) Init() {
    c.parameterValue = actuallyGetParameter(c.ParameterName)
}

func (c *Config) Access(kong *pdk.Pdk) {
  // simply use c.parameterValue
}

I also feel like some kind of shutdown hook could be useful. It could be used for things like flushing/closing files etc; for instance, the current go-log example plugin leaks file descriptors as far as I can tell.

Basic Log Serializer has been deprecated

I'm trying to create a go plugin and it looks like pdk is complaining about using kong.log.serialize under the hood?

Kong version: 2.1 (beta)

2020/08/04 19:43:51 [notice] 65021#0: *26 [kong] go.lua:97 go-pluginserver terminated: exit 2, context: ngx.timer
2020/08/04 19:43:51 [notice] 65021#0: *26 [kong] go.lua:86 Starting go-pluginserver, context: ngx.timer
2020/08/04 19:43:51 [warn] 65021#0: *426 [kong] basic.lua:12 [guts] basic log serializer has been deprecated, please modify your plugin to use the kong.log.serialize PDK function instead while logging request, client: 127.0.0.1, server: kong, request: "POST /phoenix/api/0.1/rebuild HTTP/1.1", upstream: "https://10.34.1.201:443/api/0.1/rebuild", host: "localhost:8000"

package main

import (
  "github.com/Kong/go-pdk"
)

type Config struct {
  Path   string
  Reopen bool
}

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



func (conf Config) Access(kong *pdk.PDK) {
  // after request has been sent stop timer with correct tags
  uuid, err := kong.Request.GetHeader("Kong-Request-ID")
  if err != nil {
    kong.Log.Info("[Request]: error receiving request id", uuid)
  }
}

func (conf Config) Log(kong *pdk.PDK) {
  uuid, err := kong.Request.GetHeader("Kong-Request-ID")
  if err != nil {
    kong.Log.Err("[Response]: error receiving request id", uuid)
  }
}


Supported Go versions

Are there any plans to update the supported Go versions? You're still running on Go version 1.13, which is over 2 years old at this point and has a number of security vulnerabilities.

Question: How I can find the logs printed by kong.Log.Notice()

I'm trying to debug my go-plugin by printing some debug logs with kong.Log

However, I don't know where to find the printed logs ?

I'm running kong with alpine docker image, and the logs I printed are not shown from stdout/stderr.

And where I can find the debug logs of go-pluginserver, I had an issue that sometimes my go-plugin stops to work, looks like Access() is not called. I'm trying to figure out why..

Any idea ?

kong.Router.GetRoute() returns error "expected type: entities.Route"

i am using kong 2.1.0 , and try to get current request route as below:

func getRoute(kong *pdk.PDK) (string, error) {
var routeObj, routeErr = kong.Router.GetRoute()
if routeErr != nil {
var msg = fmt.Sprintf("unable to get current request route , error : %v", routeErr)
logger(msg)
return "", errors.New(msg)
}

 ....

}

but see lots errors like this:

2020/08/07 21:57:11 [debug] 37#0: *1851 [kong] go.lua:352 unable to get current request route , error : expected type: entities.Route

What caused this ?
How to fix or workaround ?

Configuring corporate proxy for go-pdk plugin

I'm trying to configure corporate proxy for go kong plugin

here is my code:

package main

import (
    "github.com/Kong/go-pdk"
)

type Config struct {
    Apikey string
}

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

func (conf Config) Access(kong *pdk.PDK) {

    err := kong.Nginx.SetCtx("balancer_address.host", "127.0.0.1")
    if err != nil {
        kong.Log.Err(err.Error())
    }
    errC := kong.Nginx.SetCtx("balancer_address.port","8080")
    if errC != nil {
        kong.Log.Err(errC)
    }

    key, err := kong.Request.GetQueryArg("key")
    apiKey := conf.Apikey

    if err != nil {
        kong.Log.Err(err.Error())
    }

    x := make(map[string][]string)
    x["Content-Type"] = append(x["Content-Type"], "application/json")

    if apiKey != key {
        kong.Response.Exit(403, "Youu have no correct key", x)
    }
}

of course i created test proxy server at my host: 127.0.0.1:8080

but it's not working

by plugin doc: kong.Nginx.SetCtx() sets a value in the ngx.ctx request context table

i made my go plugin by example https://github.com/tfabien/kong-forward-proxy/blob/master/src/access.lua
where
ngx.ctx.balancer_address.host = plugin_conf.proxy_host
ngx.ctx.balancer_address.port = plugin_conf.proxy_port
are configuring proxy

so, anyway it's not working

my dockerfile is

FROM kong/go-plugin-tool:2.0.4-alpine-latest AS builder

RUN mkdir -p /tmp/key-checker/

COPY . /tmp/key-checker/

RUN cd /tmp/key-checker/ && \
    go get github.com/Kong/go-pdk && \
    go mod init kong-go-plugin && \
    go get -d -v github.com/Kong/go-pluginserver && \
    go build github.com/Kong/go-pluginserver && \
    go build -buildmode plugin key-checker.go

FROM kong:2.3-alpine

RUN mkdir /tmp/go-plugins
COPY --from=builder  /tmp/key-checker/go-pluginserver /usr/local/bin/go-pluginserver
COPY --from=builder  /tmp/key-checker/key-checker.so /tmp/go-plugins

ENV KONG_PLUGINSERVER_NAMES="go"
ENV KONG_PLUGINSERVER_GO_SOCKET="/tmp/go-plugins/go_pluginserver.sock"
ENV KONG_PLUGINSERVER_GO_START_CMD="/usr/local/bin/go-pluginserver -kong-prefix /tmp/go-plugins/ -plugins-directory /tmp/go-plugins"
ENV KONG_PLUGINSERVER_GO_QUERY_CMD="/usr/local/bin/go-pluginserver -dump-all-plugins -plugins-directory /tmp/go-plugins"

COPY config.yml /tmp/config.yml

USER root
RUN chmod -R 777 /tmp
RUN /usr/local/bin/go-pluginserver -version && \
    cd /tmp/go-plugins && \
    /usr/local/bin/go-pluginserver -dump-plugin-info key-checker
USER kong

starting docker by:

docker run -ti --rm --name kong-go-plugins \
  -e "KONG_DATABASE=off" \
  -e "KONG_GO_PLUGINS_DIR=/tmp/go-plugins" \
  -e "KONG_DECLARATIVE_CONFIG=/tmp/config.yml" \
  -e "KONG_GO_PLUGINSERVER_EXE=/usr/local/bin/go-pluginserver" \
  -e "KONG_PLUGINS=key-checker" \
  -e "KONG_PROXY_LISTEN=0.0.0.0:8000" \
  -p 8000:8000 \
  kong-demo

how can i resolve it?

Test library errors when using kong.Response.Exit

I just gave the new test library a try and ran into an issue where a call to kong.Response.Exit in the plugin can cause errors in the testing library.

$ go test ./... -v -count=1
=== RUN   TestPlugin
    test.go:573: Access
    test.go:573: Access
    test.go:573: Access
    test.go:240: Can't read method name
    test.go:240: Can't read method name
    test.go:573: Access
    test.go:240: Can't read method name
    test.go:573: Access
    test.go:240: Can't read method name
--- FAIL: TestPlugin (0.00s)
FAIL
FAIL    plugintest      0.005s
FAIL

Occasionally it also produces a panic when running the test repeatedly:

$ go test ./... -v -count=1
=== RUN   TestPlugin
    test.go:573: Access
    test.go:573: Access
    test.go:573: Access
    test.go:240: Can't read method name
    test.go:240: Can't read method name
    test.go:573: Access
    test.go:240: Can't read method name
    test.go:573: Access
    test.go:240: Can't read method name
--- FAIL: TestPlugin (0.00s)
panic: Fail in goroutine after TestPlugin has completed

goroutine 50 [running]:
testing.(*common).Fail(0xc000102780)
        /usr/lib/golang/src/testing/testing.go:697 +0x125
testing.(*common).Errorf(0xc000102780, 0x88abac, 0x16, 0x0, 0x0, 0x0)
        /usr/lib/golang/src/testing/testing.go:803 +0x93
github.com/Kong/go-pdk/test.TestEnv.Errorf(...)
        /home/cyrill/tools/golang/pkg/mod/github.com/!kong/[email protected]/test/test.go:240
github.com/Kong/go-pdk/bridge/bridgetest.MockFunc.func1(0x9126f0, 0xc000312080, 0x90a1f8, 0xc00030e000)
        /home/cyrill/tools/golang/pkg/mod/github.com/!kong/[email protected]/bridge/bridgetest/bridgetest.go:124 +0x267
created by github.com/Kong/go-pdk/bridge/bridgetest.MockFunc
        /home/cyrill/tools/golang/pkg/mod/github.com/!kong/[email protected]/bridge/bridgetest/bridgetest.go:117 +0x65
FAIL    plugintest      0.008s
FAIL

Here is a minimal example to reproduce the issue:

// main.go
package main

import (
	"net/http"

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

type Config struct {
}

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

func (conf Config) Access(kong *pdk.PDK) {
	kong.Response.Exit(http.StatusUnauthorized, http.StatusText(http.StatusUnauthorized), nil)
}

func main() {
	server.StartServer(New, "v0.0.1", 0)
}
// main_test.go
package main

import (
	"testing"

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

func TestPlugin(t *testing.T) {
	for i := 0; i < 5; i++ {
		env, err := test.New(t, test.Request{
			Method: "GET",
			Url:    "http://example.com?q=search&x=9",
		})
		assert.NoError(t, err)
		env.DoHttps(&Config{})
	}
}
// go.mod
module plugintest

go 1.16

require (
	github.com/Kong/go-pdk v0.7.0-beta1.0.20210526164613-49a95960d27c
	github.com/stretchr/testify v1.5.1
)

Am I doing something wrong here? If I reuse the env and simply call env.DoHttps(&Config{}) within the loop, the error still appears although way less frequently (~1 out of 20 test runs). But in an actual plugin that I'm currently writing I have to create different env instances per subtest, since I need to test with a bunch of different client headers.

Response.ExitStatus causes 500 in kong: plugin_servers/pb_rpc.lua:45: attempt to index local 'v' (a nil value)

If I use ExitStatus(http.StatusFound) I get a 500 and

plugin_servers/pb_rpc.lua:45: attempt to index local 'v' (a nil value)

If I use Exit(http.StatusFound, "", nil) everything is ok.

While inspecting the code, I would guess from the lua error, that the rpc call is missing some values.
IMHO I would just call Exit from ExitStatus with empty values for body and headers

Also setting the headers in Exit seems not to work - I had to use SetHeader to create a "working" 302.

Enable go.mod

I will be great if we can enable go.mod for this repo. This surely helps for development of plugin.

go-pdk v0.3.0 is missing kong.response.Exit method

Hi
I'm trying to develop go plugin for kong, I'm using go mod for dependency management by default it is using a latest tag from go-pdk which is v0.3.0, but this tag is missing kong.response.Exit.

v0.3.0...master

When a new release of go-pdk will be released?

PS Yes, I know that I can use the git hash.

Bug: Log phase called twice

It seems that the Log hook is called twice.
Also, the Ctx data set by previous phases isn't available in the second Log call.

To test it, I created a counter logic that runs in Access, Response & Log.
Output log:

2021/01/20 08:10:02 [error] 36#0: *35 [kong] init.lua:310 [custom-udp-log] Access[counter]: 0., client: 172.17.0.1, server: kong, request: "GET /greet HTTP/1.1", host: "localhost:8000"
2021/01/20 08:10:02 [error] 36#0: *35 [kong] init.lua:310 [custom-udp-log] Response[counter]: 1. while sending to client, client: 172.17.0.1, server: kong, request: "GET /greet HTTP/1.1", host: "localhost:8000"
2021/01/20 08:10:02 [error] 36#0: *42 [kong] init.lua:310 [custom-udp-log] Log[counter]: 2., context: ngx.timer, client: 172.17.0.1, server: 0.0.0.0:8000
2021/01/20 08:10:03 [error] 36#0: *43 [kong] init.lua:310 [custom-udp-log] Log[counter]: 0., context: ngx.timer, client: 172.17.0.1, server: 0.0.0.0:8000
2021/01/20 08:10:03 [error] 36#0: *35 [kong] init.lua:310 [custom-udp-log] Access[counter]: 0., client: 172.17.0.1, server: kong, request: "GET /greet HTTP/1.1", host: "localhost:8000"
2021/01/20 08:10:03 [error] 36#0: *35 [kong] init.lua:310 [custom-udp-log] Response[counter]: 1. while sending to client, client: 172.17.0.1, server: kong, request: "GET /greet HTTP/1.1", host: "localhost:8000"
2021/01/20 08:10:03 [error] 36#0: *46 [kong] init.lua:310 [custom-udp-log] Log[counter]: 2., context: ngx.timer, client: 172.17.0.1, server: 0.0.0.0:8000
2021/01/20 08:10:03 [error] 36#0: *47 [kong] init.lua:310 [custom-udp-log] Log[counter]: 0., context: ngx.timer, client: 172.17.0.1, server: 0.0.0.0:8000
2021/01/20 08:10:03 [error] 36#0: *35 [kong] init.lua:310 [custom-udp-log] Access[counter]: 0., client: 172.17.0.1, server: kong, request: "GET /greet HTTP/1.1", host: "localhost:8000"
2021/01/20 08:10:03 [error] 36#0: *35 [kong] init.lua:310 [custom-udp-log] Response[counter]: 1. while sending to client, client: 172.17.0.1, server: kong, request: "GET /greet HTTP/1.1", host: "localhost:8000"
2021/01/20 08:10:03 [error] 36#0: *58 [kong] init.lua:310 [custom-udp-log] Log[counter]: 2., context: ngx.timer, client: 172.17.0.1, server: 0.0.0.0:8000
2021/01/20 08:10:03 [error] 36#0: *59 [kong] init.lua:310 [custom-udp-log] Log[counter]: 0., context: ngx.timer, client: 172.17.0.1, server: 0.0.0.0:8000

about get header error

When I request with a header such as A_SESSION, I fail to use GetHeader function to extrace A_SESSION.
I finally successfully get header by key a_session.

I noticed document said that Header names in are case-insensitive and are normalized to lowercase, and dashes (-) can be written as underscores (_); that is, the header X-Custom-Header can also be retrieved as x_custom_header.
However, it is Inconsistent with the experimental results.

Thanks for such good open source tools.

How to use Kong cache?

I've opened a question on Kong forum but as I didn't get any answer I allow myself to ask the question here.

We wrote a custom plugins in Go (an enhance version of the JWT plugin)thanks to this new go-pdk but we quickly faced performances problems.

In the plugin, we need to load consumers with the LoadConsumer which basically calls the load_consumer Lua function.
The documentation of this function says:

Returns the consumer from the datastore (or cache).

But it seems that the cache is not managed by the function actually, so every call will trigger a select in the datastore (in our case Cassandra) which leads to dramatic performances.

We saw that every plugins has to write something like that:

  local consumer_cache_key = kong.db.consumers:cache_key(jwt_secret.consumer.id)
  local consumer, err      = kong.cache:get(consumer_cache_key, nil,
                                            kong.client.load_consumer,
                                            jwt_secret.consumer.id, true)

But I didn't find how to use the cache with the go-pdk.

So my questions are:

  • Is there a way to use Kong cache in Go plugins.
  • Or is it possible for the Lua client to expose functions that manage the cache for main entities (e.g.: load_consumer_with_cache).

Thanks in advance for your help.

Any issues with using exec.Cmd inside a Go plugin?

Wondering what the implications might be using exec.Cmd inside a bespoke plugin to execute another program. In my case, I want to write a plugin which processes XSLT against a legacy XML web service to transform the response body. I was able to do this with a JavaScript plugin, but was able to find an npm module which was easy to use in my code. There doesn't seem to be a comparably simple Go library to do similar (at least not that I could find), but one example I did come across was using Go to call the xlstproc executable (passing the XSLT and XML as file objects) and capture what that produces to stdout.

But I wasn't sure whether trying to do this in the context of a Kong plugin would create issues in the flow of processing an API request, so curious if anyone else has attempted it.

nginx worker stuck on shutdown when plugin panics

If there is a broken error handling in the plugin, the appropriate nginx process is stuck on shutdown

ps ax| rg nginx
4034477 ?        Ss     0:01 nginx: master process /usr/local/openresty/nginx/sbin/nginx -p /usr/local/kong -c nginx.conf
4034739 ?        S      0:00 nginx: worker process is shutting down
4042041 ?        S      0:00 nginx: worker process is shutting down
4048195 ?        S      0:00 nginx: worker process is shutting down
4054000 ?        S      0:00 nginx: worker process
4054001 ?        S      0:00 nginx: worker process
4054002 ?        S      0:00 nginx: worker process
4054003 ?        S      0:00 nginx: worker process
4054004 ?        S      0:00 nginx: worker process
4054005 ?        S      0:00 nginx: worker process
4054006 ?        S      0:00 nginx: worker process
4054007 ?        S      0:00 nginx: worker process
4060470 pts/3    S+     0:00 rg nginx

corresponding panic:

kong_1            | 2021/03/08 15:51:19 [info] 135#0: *7568 [go:149] panic: send on closed channel, context: ngx.timer
kong_1            | 2021/03/08 15:51:19 [info] 135#0: *7568 [go:149] goroutine 49 [running]:, context: ngx.timer
kong_1            | 2021/03/08 15:51:19 [info] 135#0: *7568 [go:149] github.com/Kong/go-pdk/bridge.PdkBridge.Ask(...), context: ngx.timer
kong_1            | 2021/03/08 15:51:19 [info] 135#0: *7568 [go:149]    /go/pkg/mod/github.com/!kong/[email protected]/bridge/bridge.go:24, context: ngx.timer
kong_1            | 2021/03/08 15:51:19 [info] 135#0: *7568 [go:149] github.com/Kong/go-pdk/bridge.PdkBridge.AskString(0xc000066660, 0x7f9c317e2540, 0x1a, 0xc000184410, 0x1, 0x1, 0xc0001843f0, 0x3, 0x0, 0x0), context: ngx.timer
kong_1            | 2021/03/08 15:51:19 [info] 135#0: *7568 [go:149]    /go/pkg/mod/github.com/!kong/[email protected]/bridge/bridge.go:120 +0xda, context: ngx.timer
kong_1            | 2021/03/08 15:51:19 [info] 135#0: *7568 [go:149] github.com/Kong/go-pdk/request.Request.GetQueryArg(...), context: ngx.timer
kong_1            | 2021/03/08 15:51:19 [info] 135#0: *7568 [go:149]    /go/pkg/mod/github.com/!kong/[email protected]/request/request.go:132, context: ngx.timer
kong_1            | 2021/03/08 15:51:19 [info] 135#0: *7568 [go:149] bm-auth.Config.Access(0x7f9c317dba96, 0x7, 0x7f9c317ddee0, 0xf, 0x7f9c317e8481, 0x28, 0x7f9c317dc14a, 0x8, 0x0, 0xc00005d0e0), context: ngx.timer
kong_1            | 2021/03/08 15:51:19 [info] 135#0: *7568 [go:149]    /go-plugins/bm-authx/auth.go:274 +0xf47, context: ngx.timer
kong_1            | 2021/03/08 15:51:19 [info] 135#0: *7568 [go:149] main.(*PluginServer).HandleEvent.func1(0xc000066660, 0xc00000ec00, 0xc0000146c0, 0xc0000a4050), context: ngx.timer
kong_1            | 2021/03/08 15:51:19 [info] 135#0: *7568 [go:149]    /go/pkg/mod/github.com/!kong/[email protected]/event.go:61 +0x65, context: ngx.timer
kong_1            | 2021/03/08 15:51:19 [info] 135#0: *7568 [go:149] created by main.(*PluginServer).HandleEvent, context: ngx.timer
kong_1            | 2021/03/08 15:51:19 [info] 135#0: *7568 [go:149]    /go/pkg/mod/github.com/!kong/[email protected]/event.go:59 +0x236, context: ngx.timer

Init _ Worker

Is there anyway for me to use method Init_Worker() in Go plugins

Using golangci-lint as linter tool

Lot of Golang project now using golangci-lint as linter tool, would like to check if this tool can be used.

The current golint is kind of limited in feature.

Exposing prometheus metrics from a plugin

Hello,

We're using kong with custom plugins written in go to handle our authentication logic.
We would like to monitor these plugins by exposing metrics in the prometheus plugin (we already have the prometheus plugin in place).
I noticed that the prometheus plugin now exposes the internal prometheus object. Relevant PR here.
I was wondering if there's any way to access this prometheus object from within golang plugins to create our custom metrics.

Thank you.

Unexported struct fields in the Config can lead to crash on startup

In order to reuse an API client, I put it as an unexported field into the Config, like so:

package main

import (
	"github.com/Kong/go-pdk/server"
	management "github.com/rancher/rancher/pkg/client/generated/management/v3"
)

type Config struct {
	rancherClient *management.Client
}

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

func main() {
	server.StartServer(New, "v0.0.1", 0)
}
go run . -dump

Note that the above might crash your computer when run! Be ready to ctrl-c to end the process. For some reason, this particular struct causes the whole memory to fill up pretty quickly. The struct contains lots of fields and nesting levels, but I was unable to reproduce the issue with a more simple struct.

Anyway, a simple fix would be to just ignore unexported fields. My understanding is that unexported fields won't work in the config anyway, since it needs to be Unmarshaled with json.Unmarshal later. I will post a PR with that fix but let me know if you would take a different approach here.

Questions about setting response headers

I'm trying to write a bespoke plugin to perform a transformation on the response body, and it looks like the headers that I'm adding in the Response() phase are not actually being sent to the client. In this case, I'm trying to set the Content-Type and X-Kong-Upstream-Status headers with kong.Response.SetHeader().

err = kong.Response.SetHeader("X-Kong-Upstream-Status", strconv.Itoa(respStatus))
...
kong.Response.SetHeader("Content-Type", conf.OutputType)

Right at the end of the Response() phase, I use this to get all the headers (which I assume are those from the upstream and the two I added):

respHeaders, err := kong.Response.GetHeaders(-1)

I put in some debugging code to sanity check what I got back, and everything I expect to see is there:

kong.Log.Debug("Number of response headers: " + strconv.Itoa(len(respHeaders)))
for header, value := range respHeaders {
   for i := 0; i < len(value); i++ {
      kong.Log.Debug(header + " = " + value[i])
   }
}

2022/01/20 21:13:24 [debug] 2067#0: *170 [kong] pb_rpc.lua:228 Number of response headers: 6
2022/01/20 21:13:24 [debug] 2067#0: *170 [kong] pb_rpc.lua:228 connection = keep-alive
2022/01/20 21:13:24 [debug] 2067#0: *170 [kong] pb_rpc.lua:228 vary = Accept-Encoding
2022/01/20 21:13:24 [debug] 2067#0: *170 [kong] pb_rpc.lua:228 x-kong-upstream-status = 200
2022/01/20 21:13:24 [debug] 2067#0: *170 [kong] pb_rpc.lua:228 access-control-allow-origin = *
2022/01/20 21:13:24 [debug] 2067#0: *170 [kong] pb_rpc.lua:228 content-type = text/html; charset=UTF-8
2022/01/20 21:13:24 [debug] 2067#0: *170 [kong] pb_rpc.lua:228 date = Thu, 20 Jan 2022 21:13:23 GMT

I then exit the Response() phase with:

kong.Response.Exit(respStatus, string(newPayloadStr), respHeaders)

However, when I test this with curl, I'm not seeing the headers I've added:

< HTTP/1.1 200 OK
< Connection: keep-alive
< Content-Length: 1583
< X-Kong-Upstream-Latency: 329
< X-Kong-Proxy-Latency: 3
< Via: kong/2.7.0.0-enterprise-edition

When I read the PDK documentation for Response.SetHeader(), it says this:

This function should be used in the header_filter phase, as Kong is preparing headers to be sent back to the client.

However, I'm not able to figure out how you can target that phase base on this. Am I missing something basic?

.

error : bad argument #1 to 'ipairs' (table expected, got nil)

kong_1 | 2020/03/23 04:29:12 failed to open plugin kong-plugins: plugin.Open("/usr/local/share/lua/5.1/kong/plugins/go-so/kong-plugins.so"): Dynamic loading not supported
kong_1 | 2020/03/23 04:29:12 [error] 1#0: init_by_lua error: /usr/local/share/lua/5.1/kong/db/dao/plugins/go.lua:455: bad argument #1 to 'ipairs' (table expected, got nil)
kong_1 | stack traceback:
kong_1 | [C]: in function 'ipairs'
kong_1 | /usr/local/share/lua/5.1/kong/db/dao/plugins/go.lua:455: in function 'get_plugin'
kong_1 | /usr/local/share/lua/5.1/kong/db/dao/plugins/go.lua:481: in function 'load_plugin'
kong_1 | /usr/local/share/lua/5.1/kong/db/dao/plugins.lua:151: in function 'load_plugin_handler'
kong_1 | /usr/local/share/lua/5.1/kong/db/dao/plugins.lua:227: in function 'load_plugin'
kong_1 | /usr/local/share/lua/5.1/kong/db/dao/plugins.lua:275: in function 'load_plugin_schemas'
kong_1 | /usr/local/share/lua/5.1/kong/init.lua:425: in function 'init'
kong_1 | init_by_lua:3: in main chunk
k

How can i modify the respone body?

Hi~~~
Can we access and modify the response body in Go plugins, for doing something like making a Go version of Response Transformer plugin bundled with Kong?
As Go plugin doesn't support body_filter phase, seems like we can't do it now.

kong.Response.ExitStatus(...) causes go-pluginserver to restart

When using kong.Response.ExitStatus(400) I get the following message in log:

signal 17 (SIGCHLD) received from 5558
[notice] 5503#0: *7 [kong] go.lua:95 go-pluginserver terminated: exit 2, context: ngx.timer
[notice] 5503#0: *7 [kong] go.lua:84 Starting go-pluginserver, context: ngx.timer

The call to kong.Response.ExitStatus() is made in Access phase, and if commented out everything work well.
May be the go-pluginserver restart is intended, but then it should be better explained in documention :-). To exit without this problem use ExitStatus func.

go-plugin can't get ctx set by lua

lua plugin at body_filter phase:

local function status()
        local s = kong.response.get_status()
        kong.ctx.shared.status = s
end

go plugin at log phase:

func (conf Config) Log(kong *pdk.PDK) {

        status, err := kong.Ctx.GetSharedInt("status")
        if err!= nil {
                kong.Log.Err(err.Error())
        }
       ....
}

got error : null response

How can i get the response header?

Hello ~~~
kong.Response.GetHeader can not work in access phase, and go-pdk do not support header_filter and body_filter phase. so, how can i get the response header?

kong.Request.GetRawBody() is empty, if body is buffered into temporary file on behalf of nginx/kong

I want to make a POST request to a plugin whose body size exceeds the default body size (http://nginx.org/en/docs/http/ngx_http_core_module.html#client_body_buffer_size) of nginx.

kong.Request.GetRawBody() return an empty body if it is buffered, e.g.:

a client request body is buffered to a temporary file /usr/local/kong/client_body_temp/0000000001

If I increase the body size of nginx/kong

KONG_NGINX_PROXY_CLIENT_BODY_BUFFER_SIZE: 32k

I get the body via kong.Request.GetRawBody()

Response Function not executed

MY GO Plugin is

package main

import (
	"fmt"

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

type Config struct {
	AppId                 string   `json:"api_id"`
	ApiSecret             string   `json:"api_secret"`
	Discovery             string   `json:"discovery"`
	TrasferClaimsToHeader []string `json:"trasfer_claims_to_header"`
	RequiredRoles         []string `json:"required_roles"`
	SslVerify             string   `json:"ssl_verify"`
}

var Version = "1.1"

var Priority = 1000

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

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

func (c Config) Access(kong *pdk.PDK) {
	fmt.Println("Access------------")
          if true {
	      kong.Response.Exit(http.StatusUnauthorized,"unauthorized !", nil)
		return
	}
}

func (conf Config) Preread(kong *pdk.PDK) {
	fmt.Println("Preread------------")
}

func (conf Config) Response(kong *pdk.PDK) {
	fmt.Println("Response------------")

}

func (conf Config) Rewrite(kong *pdk.PDK) {
	fmt.Println("Rewrite------------")
}

func (conf Config) Certificate(kong *pdk.PDK) {
	fmt.Println("Certificate------------")
}

func (conf Config) Log(kong *pdk.PDK) {
	fmt.Println("Log------------")
}

When I'm ready to deploy Kong 2.7, and run it log:

2021/12/31 18:44:00 [notice] 1108#0: *516 [lua] init.lua:260: purge(): [DB cache] purging (local) cache, client: 127.0.0.1, server: kong_admin, request: "POST /config?check_hash=1 HTTP/1.1", host: "127.0.0.1:8001"
2021/12/31 18:44:00 [notice] 1108#0: *516 [lua] init.lua:260: purge(): [DB cache] purging (local) cache, client: 127.0.0.1, server: kong_admin, request: "POST /config?check_hash=1 HTTP/1.1", host: "127.0.0.1:8001"
2021/12/31 18:44:00 [info] 1108#0: *9 [oidc-token:1112] Access------------, context: ngx.timer
31/Dec/2021:18:44:00 +0800 [401] [0.058] [gw.meda.test] "GET /api/demo?id=0007 HTTP/1.1"
2021/12/31 18:44:00 [info] 1108#0: *9 [oidc-token:1112] Log------------, context: ngx.timer
2021/12/31 18:44:00 [info] 1108#0: *527 client 10.244.0.0 closed keepalive connection
2021/12/31 18:44:00 [info] 1108#0: *516 client 127.0.0.1 closed keepalive connection

Config loading

Im trying to write a config schema which looks like this

type Config struct {
  ApiEndPoint    string
  AuthModules    []Module
}


type Module struct {
  Name   string  
  Routes []string
}

and trying to load the configuration from a declarative config file.

config: 
    apiendpoint: "http://localhost:5050/api/v1/permission/validate"
    authmodules: 
       - 
         name: application_mgmt
         routes: 
           - /api/v1/random

This configuration is resulting in an error

docker-compose up                                                                                                  ๎‚ฒ โœ” ๎‚ฒ 17:19:32
Recreating plugin_kong-demo_1 ... done
Attaching to plugin_kong-demo_1
kong-demo_1  | 2020/03/23 16:19:35 [error] 1#0: init_by_lua error: /usr/local/share/lua/5.1/kong/init.lua:431: error parsing declarative config file /home/kong/config.yml:
kong-demo_1  | in 'services':
kong-demo_1  |   - in entry 1 of 'services':
kong-demo_1  |     in 'plugins':
kong-demo_1  |       - in entry 1 of 'plugins':
kong-demo_1  |         in 'config':
kong-demo_1  |           in 'authmodules': unknown field

I made sure the yaml is valid so there's no issue there. I'm able to parse the other fields though.

Can anyone help me explain what am I doing wrong here or if it is a bug in the go-pdk itself?

At this point I'm stuck and any help would be appreciated. Thanks

discuss: Tracing support

Is it possible to use tracing (OpenTracing, OpenTelemetry or OpenCensus) inside the go plugin?

kong.Ctx.SetShared / GetSharedAny not working in v0.7.1

We've been using kong.Ctx to pass information between the different phases, because some of the information we want to use exists in the Access phase, some in Report phase, and we want to use it in the Log phase.
However, after upgrading to v0.7.1 (which includes the move to protobuf) it stopped working.
From my debugging, it seems like no data is passed.

Can I use the init() method?

Hello there!
In my plugin.go, can I use the init() method? such as:

var cache Cache
func init() {
  cache = doSomething()
  go timedCache()
}

func timedCache() {
  for {
    select {
    case: <- time.After(...):
      cache = doSomething()
    }
  }
}

func (conf Config) Access(...) {
  ...
}

Thank you!

How to persist user data

Ive noticed that the session plugin uses kong.db for storage yet this does not seem to be supported in Golang. Is this supported or is there another mechanism?

Context: we are migrating user credentials out of a monolith so during the migration period, we would like to support both session types and hence might need to write our custom plugin for that.

Test library errors setting headers

Trying the new test library and getting an error trying to set a header

go test ./... -v
=== RUN   TestPlugin
    test.go:571: Access
panic: assignment to entry in nil map

goroutine 19 [running]:
net/textproto.MIMEHeader.Set(...)
        /usr/local/Cellar/go/1.16.4/libexec/src/net/textproto/header.go:22
net/http.Header.Set(...)
        /usr/local/Cellar/go/1.16.4/libexec/src/net/http/header.go:37
github.com/Kong/go-pdk/test.(*TestEnv).Handle(0xc0000f6000, 0xc0000b00d8, 0x18, 0xc00020c1c0, 0xc, 0xc, 0x0, 0x2b2d37, 0x1792440)
        [redacted]/vendor/github.com/Kong/go-pdk/test/test.go:409 +0x209a
github.com/Kong/go-pdk/bridge/bridgetest.MockFunc.func1(0x1529910, 0xc0000da180, 0x15212f8, 0xc0000f6000)
        [redacted]/vendor/github.com/Kong/go-pdk/bridge/bridgetest/bridgetest.go:132 +0xea
created by github.com/Kong/go-pdk/bridge/bridgetest.MockFunc
        [redacted]/vendor/github.com/Kong/go-pdk/bridge/bridgetest/bridgetest.go:117 +0x65
FAIL    plugintest   0.168s
FAIL

here is the code

// main.go
package main

import (
	"log"

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

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

var Version = "0.0.1"
var Priority = 1

type Config struct{}

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

func (conf Config) Access(kong *pdk.PDK) {
	err := kong.Response.SetHeader("foo", "bar")
	if err != nil {
		log.Printf("Error getting setting header: %s", err.Error())
	}
}
// main_test.go
package main

import (
	"testing"

	"github.com/Kong/go-pdk/test"
	"github.com/stretchr/testify/assert"
)
func TestPlugin(t *testing.T) {
	env, err := test.New(t, test.Request{
		Method:  "GET",
		Url:     "http://example.com?q=search&x=9",
	})
	if assert.NoError(t, err) {
		env.DoHttps(&Config{})
		assert.Equal(t, 200, env.ClientRes.Status)
		assert.Equal(t, "bar", env.ClientRes.Headers.Get("foo"))
	}
}
// go.mod
module plugintest

go 1.16

require (
	github.com/Kong/go-pdk v0.7.0-beta1.0.20210526164613-49a95960d27c
	github.com/stretchr/testify v1.5.1
)

go-pluginserver is broken with the last commit

A change was introduced in the pdk.Init interface and the go-pluginserver fails to be built:

#18 35.78 # github.com/Kong/go-pluginserver
#18 35.78 src/github.com/Kong/go-pluginserver/event.go:49:21: cannot use ipc (type chan interface {}) as type net.Conn in argument to pdk.Init:
#18 35.78 	chan interface {} does not implement net.Conn (missing Close method)
------
executor failed running [/bin/sh -c go get github.com/Kong/go-pluginserver]: exit code: 2

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.