├── .gitignore ├── Dockerfile ├── LICENSE ├── Makefile ├── README.md ├── Tiltfile ├── docker-compose.yml ├── example-plugin ├── go.mod └── plugin.go ├── k8s-krakend.yaml ├── krakend.json ├── proxy-plugin ├── go.mod └── plugin.go └── router-plugin ├── go.mod └── plugin.go /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | 8 | # Test binary, built with `go test -c` 9 | *.test 10 | 11 | # Output of the go coverage tool, specifically when used with LiteIDE 12 | *.out 13 | 14 | # Dependency directories (remove the comment below to include it) 15 | # vendor/ 16 | 17 | krakend 18 | .dccache -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:1.16.4 as builder 2 | 3 | COPY ./example-plugin/ /example-plugin 4 | RUN cd /example-plugin && go build -buildmode=plugin -o example-plugin.so . 5 | 6 | COPY ./router-plugin/ /router-plugin 7 | RUN cd /router-plugin && go build -buildmode=plugin -o router-plugin.so . 8 | 9 | COPY ./proxy-plugin/ /proxy-plugin 10 | RUN cd /proxy-plugin && go build -buildmode=plugin -o proxy-plugin.so . 11 | 12 | FROM devopsfaith/krakend:1.4.1 13 | 14 | EXPOSE 8080 8090 15 | ENV GIN_MODE=release 16 | CMD ["run", "-d", "-c", "/etc/krakend/krakend.json"] 17 | 18 | COPY --from=builder /example-plugin/example-plugin.so /etc/krakend/plugins/ 19 | COPY --from=builder /router-plugin/router-plugin.so /etc/krakend/plugins/ 20 | COPY --from=builder /proxy-plugin/proxy-plugin.so /etc/krakend/plugins/ 21 | 22 | COPY krakend.json /etc/krakend/krakend.json 23 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 M.-Leander Reimer 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: plugins docker run 2 | 3 | all: plugins docker 4 | 5 | plugins: 6 | cd example-plugin && go build -buildmode=plugin -o ../plugins/example-plugin.so . 7 | cd router-plugin && go build -buildmode=plugin -o ../plugins/router-plugin.so . 8 | cd proxy-plugin && go build -buildmode=plugin -o ../plugins/proxy-plugin.so . 9 | 10 | docker: 11 | docker build -t hands-on-krakend . 12 | 13 | run: docker 14 | docker run -p 8080:8080 -p 8090:8090 hands-on-krakend 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Hands on KrakenD 2 | 3 | Demo repository for KrakenD API Gateway plugin showcase. 4 | 5 | ## Usage 6 | 7 | When compiling the plugins, make sure to use the same Go version which Krakend has 8 | been compiled with. Best to use a Dockerized build. 9 | 10 | ```bash 11 | make docker 12 | ``` 13 | 14 | For quick and easy local development, you can use Tilt to continuously build and 15 | deploy Krakend to a local Kubernetes cluster. 16 | 17 | ```bash 18 | $ tilt up 19 | ``` 20 | 21 | ## Maintainer 22 | 23 | M.-Leander Reimer (@lreimer), 24 | 25 | ## License 26 | 27 | This software is provided under the MIT open source license, read the `LICENSE` 28 | file for details. 29 | -------------------------------------------------------------------------------- /Tiltfile: -------------------------------------------------------------------------------- 1 | 2 | docker_build('hands-on-krakend', '.', 3 | only=['./krakend.json', "./example-plugin/", "./proxy-plugin/", "./router-plugin/"]) 4 | 5 | k8s_yaml(['k8s-krakend.yaml']) 6 | k8s_resource(workload='krakend', port_forwards=[ 7 | port_forward(18080, 8080, 'Weather API', '/weather')]) 8 | k8s_resource(workload='krakend', port_forwards=[ 9 | port_forward(18090, 8090, 'Stats', '/__stats')]) -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "2.3" 2 | 3 | services: 4 | hands-on-krakend: 5 | build: 6 | context: . 7 | image: hands-on-krakend 8 | mem_limit: 128m 9 | ports: 10 | - "8080:8080" 11 | - "8090:8090" 12 | networks: 13 | - krakend 14 | 15 | networks: 16 | krakend: 17 | driver: bridge 18 | -------------------------------------------------------------------------------- /example-plugin/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/lreimer/hands-on-krakend/example-plugin 2 | 3 | go 1.16 4 | -------------------------------------------------------------------------------- /example-plugin/plugin.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "fmt" 7 | "net/http" 8 | ) 9 | 10 | // HandlerRegisterer is the symbol the plugin loader will try to load. It must implement the Registerer interface 11 | var HandlerRegisterer = registerer("example-plugin") 12 | 13 | type registerer string 14 | 15 | func (r registerer) RegisterHandlers(f func( 16 | name string, 17 | handler func(context.Context, map[string]interface{}, http.Handler) (http.Handler, error), 18 | )) { 19 | f(string(r), r.registerHandlers) 20 | } 21 | 22 | func (r registerer) registerHandlers(ctx context.Context, extra map[string]interface{}, handler http.Handler) (http.Handler, error) { 23 | // check the passed configuration and initialize the plugin 24 | name, ok := extra["name"].([]interface{}) 25 | if !ok { 26 | return nil, errors.New("wrong config") 27 | } 28 | if name[1] != string(r) { 29 | return nil, fmt.Errorf("unknown register %s", name) 30 | } 31 | 32 | // return the actual handler wrapping or your custom logic so it can be used as a replacement for the default http handler 33 | return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { 34 | fmt.Println("example-plugin called") 35 | handler.ServeHTTP(w, req) 36 | }), nil 37 | } 38 | 39 | func init() { 40 | fmt.Println("example-plugin handler loaded!!!") 41 | } 42 | 43 | func main() {} 44 | -------------------------------------------------------------------------------- /k8s-krakend.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Service 4 | metadata: 5 | name: krakend 6 | spec: 7 | type: NodePort 8 | sessionAffinity: None 9 | selector: 10 | app: krakend 11 | ports: 12 | - name: http 13 | protocol: TCP 14 | port: 8080 15 | - name: stats 16 | protocol: TCP 17 | port: 8090 18 | --- 19 | apiVersion: apps/v1 20 | kind: Deployment 21 | metadata: 22 | name: krakend 23 | spec: 24 | selector: 25 | matchLabels: 26 | app: krakend 27 | replicas: 1 28 | template: 29 | metadata: 30 | labels: 31 | app: krakend 32 | spec: 33 | containers: 34 | - name: krakend 35 | image: hands-on-krakend 36 | ports: 37 | - containerPort: 8080 38 | - containerPort: 8090 39 | resources: 40 | limits: 41 | cpu: "500m" 42 | memory: "128Mi" 43 | requests: 44 | cpu: "500m" 45 | memory: "128Mi" -------------------------------------------------------------------------------- /krakend.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 2, 3 | "name": "Hands on KrakenD", 4 | "cache_ttl": "3600s", 5 | "timeout": "3s", 6 | "plugin": { 7 | "folder": "./plugins/", 8 | "pattern": ".so" 9 | }, 10 | "extra_config": { 11 | "github_com/devopsfaith/krakend-logstash": { 12 | "enabled": true 13 | }, 14 | "github_com/devopsfaith/krakend-gologging": { 15 | "level": "INFO", 16 | "prefix": "[KRAKEND]", 17 | "syslog": false, 18 | "stdout": true, 19 | "format": "logstash" 20 | }, 21 | "github_com/devopsfaith/krakend-metrics": { 22 | "collection_time": "10s", 23 | "proxy_disabled": false, 24 | "router_disabled": false, 25 | "backend_disabled": false, 26 | "endpoint_disabled": false, 27 | "listen_address": ":8090" 28 | }, 29 | "github_com/devopsfaith/krakend/transport/http/server/handler": { 30 | "name": ["router-plugin", "example-plugin"] 31 | } 32 | }, 33 | "endpoints": [ 34 | { 35 | "endpoint": "/health", 36 | "extra_config": { 37 | "github.com/devopsfaith/krakend/proxy": { 38 | "static": { 39 | "data": { 40 | "status": "NOK" 41 | }, 42 | "strategy": "errored" 43 | } 44 | } 45 | }, 46 | "backend": [ 47 | { 48 | "url_pattern": "/__debug/health", 49 | "host": [ 50 | "http://localhost:8080" 51 | ] 52 | } 53 | ] 54 | }, 55 | { 56 | "endpoint": "/stats", 57 | "extra_config": { 58 | "github.com/devopsfaith/krakend/proxy": { 59 | "static": { 60 | "data": { 61 | "status": "NOK" 62 | }, 63 | "strategy": "errored" 64 | } 65 | } 66 | }, 67 | "backend": [ 68 | { 69 | "url_pattern": "/__stats", 70 | "host": [ 71 | "http://localhost:8090" 72 | ] 73 | } 74 | ] 75 | }, 76 | { 77 | "endpoint": "/weather", 78 | "querystring_params": [ 79 | "q", 80 | "appid" 81 | ], 82 | "method": "GET", 83 | "output_encoding": "json", 84 | "backend": [ 85 | { 86 | "url_pattern": "/data/2.5/weather", 87 | "encoding": "json", 88 | "host": [ 89 | "https://api.openweathermap.org" 90 | ], 91 | "extra_config": { 92 | "github.com/devopsfaith/krakend/transport/http/client/executor": { 93 | "name": "proxy-plugin" 94 | } 95 | } 96 | } 97 | ] 98 | } 99 | ] 100 | } 101 | -------------------------------------------------------------------------------- /proxy-plugin/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/lreimer/hands-on-krakend/proxy-plugin 2 | 3 | go 1.16 4 | -------------------------------------------------------------------------------- /proxy-plugin/plugin.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "fmt" 7 | "html" 8 | "net/http" 9 | ) 10 | 11 | // ClientRegisterer is the symbol the plugin loader will try to load. It must implement the RegisterClient interface 12 | var ClientRegisterer = registerer("proxy-plugin") 13 | 14 | type registerer string 15 | 16 | func (r registerer) RegisterClients(f func( 17 | name string, 18 | handler func(context.Context, map[string]interface{}) (http.Handler, error), 19 | )) { 20 | f(string(r), r.registerClients) 21 | } 22 | 23 | func (r registerer) registerClients(ctx context.Context, extra map[string]interface{}) (http.Handler, error) { 24 | // check the passed configuration and initialize the plugin 25 | name, ok := extra["name"].(string) 26 | if !ok { 27 | return nil, errors.New("wrong config") 28 | } 29 | if name != string(r) { 30 | return nil, fmt.Errorf("unknown register %s", name) 31 | } 32 | // return the actual handler wrapping or your custom logic so it can be used as a replacement for the default http client 33 | return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { 34 | fmt.Println("proxy-plugin called") 35 | fmt.Fprintf(w, "{\"message\": \"Hello, %s\"}", html.EscapeString(req.URL.Path)) 36 | }), nil 37 | } 38 | 39 | func init() { 40 | fmt.Println("proxy-plugin client plugin loaded!!!") 41 | } 42 | 43 | func main() {} 44 | -------------------------------------------------------------------------------- /router-plugin/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/lreimer/hands-on-krakend/router-plugin 2 | 3 | go 1.16 4 | -------------------------------------------------------------------------------- /router-plugin/plugin.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "fmt" 7 | "net/http" 8 | ) 9 | 10 | // HandlerRegisterer is the symbol the plugin loader will try to load. It must implement the Registerer interface 11 | var HandlerRegisterer = registerer("router-plugin") 12 | 13 | type registerer string 14 | 15 | func (r registerer) RegisterHandlers(f func( 16 | name string, 17 | handler func(context.Context, map[string]interface{}, http.Handler) (http.Handler, error), 18 | )) { 19 | f(string(r), r.registerHandlers) 20 | } 21 | 22 | func (r registerer) registerHandlers(ctx context.Context, extra map[string]interface{}, handler http.Handler) (http.Handler, error) { 23 | // check the passed configuration and initialize the plugin 24 | name, ok := extra["name"].([]interface{}) 25 | if !ok { 26 | return nil, errors.New("wrong config") 27 | } 28 | if name[0] != string(r) { 29 | return nil, fmt.Errorf("unknown register %s", name) 30 | } 31 | // return the actual handler wrapping or your custom logic so it can be used as a replacement for the default http handler 32 | return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { 33 | fmt.Println("router-plugin called") 34 | handler.ServeHTTP(w, req) 35 | }), nil 36 | } 37 | 38 | func init() { 39 | fmt.Println("router-plugin handler loaded!!!") 40 | } 41 | 42 | func main() {} 43 | --------------------------------------------------------------------------------