go-kong

module
v0.0.0-...-f0135d4 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 5, 2018 License: MIT

README

go-kong

GoDoc Build Status Coverage Status Go Report Card

go-kong is a Go client library for accessing Mashape's Kong API.

Table of Contents

Installation

go get "github.com/nccurry/go-kong/kong"

Usage

Construct a new Kong client, then use the various services on the client to access different parts of the Kong API.

For example:

// Create new client
client, _ := kong.NewClient(nil, "http://localhost:8001/")

// Get cluster information
cluster, _, _ := client.Cluster.Get()

// Get Node information
node , _, _ := client.Node.Get()
status, _, _ := client.Node.GetStatus()

// Get information about the 'backend' api
api, _, _ := client.Apis.Get("backend")

// Create a new api called 'mt'
mtApi := &kong.Api{Name: "mt", RequestPath: "/mt/v0", UpstreamURL: "http://mt.my.org:8080"}
_, err := client.Apis.Post(mtApi)

// Handle 409 error separately
if _, ok := err.(kong.ConflictError); ok {
    log.Printf("Endpoint with name %s already exists.", mtApi.Name)
} else if err != nil {
    log.Fatal(err)
}

// Get all consumer objects
consumers, _, _ := client.Consumers.GetAll(nil)
for _, v := range consumers.Data {
    log.Println(v.Username)
}

// Apply ACL plugin to all apis
aclConfig := &kong.ACLConfig{Whitelist: []string{"users", "admins"}, Blacklist: []string{"blocked"}}
plugin := &kong.Plugin{Name: "acl", Config: kong.ToMap(aclConfig)}
_, err = client.Plugins.Post(plugin)
if err != nil {
    log.Fatal(err)
}

// Add ACL group to consumer
aclConsumerConfig := &kong.ConsumerACLConfig{Group: "users"}
consumerName := "paul.atreides"
_, err = client.Consumers.Plugins.ACL.Post(consumerName, aclConsumerConfig)

// Handle 404 separately
if _, ok := err.(kong.NotFoundError); ok {
    log.Printf("Could not find consumer with name %s", consumerName)
} else if err != nil {
    log.Fatal(err)
}

// Get api ACL plugin information
opt := &kong.PluginsGetAllOptions{Name: "acl"}
aclApiPlugins, _, err := client.Apis.Plugins.GetAll("mt", opt)
if err != nil {
    log.Fatal(err)
}

// Convert plugin configuration from map[string]interface{} to specific ACLConfig type
aclConfig := new(kong.ACLConfig)
if aclApiPlugins > 0 {
    err = kong.FromMap(aclConfig, aclApiPlugins.Data[0].Config)
    if err != nil {
            log.Fatal(err)
    }
}

// Get Consumer JWT plugin information
jwtPlugins, _, err := client.Consumers.Plugins.JWT.Get(consumerName)
if err != nil {
    log.Fatal(err)
}

var jwtPluginSecret, jwtPluginKey string
if jwtPlugins.Total > 0 {
    jwtPluginSecret = jwtPlugins.Data[0].Secret
    jwtPluginKey = jwtPlugins.Data[0].Key
}

Client Objects

Node
// GET /
node, resp, err := client.Node.Get()

// GET /status
status, resp, err := client.Node.GetStatus()
type Node struct {
	Configuration map[string]interface{} `json:"configuration,omitempty"`
	Hostname      string                 `json:"hostname,omitempty"`
	LuaVersion    string                 `json:"lua_version,omitempty"`
	Plugins       struct {
		AvailableOnServer map[string]bool `json:"available_on_server,omitempty"`
		EnabledInCluster  map[string]bool `json:"enabled_in_cluster,omitempty"`
	} `json:"plugins,omitempty"`
	PRNGSeeds map[string]int `json:"prng_seeds,omitempty"`
	Tagline   string         `json:"tagline,omitempty"`
	Timers    map[string]int `json:"timers,omitempty"`
	Version   string         `json:"version,omitempty"`
}

type Status struct {
	Database map[string]int `json:"database,omitempty"`
	Server   map[string]int `json:"server,omitempty"`
}
Cluster
// GET /cluster
cluster, resp, err := client.Cluster.Get()

// DELETE /cluster
cluster := &kong.Cluster{Name: "clusternode01"}
resp, err := client.Cluster.Delete(cluster)
type Cluster struct {
	Total int             `json:"total,omitemtpy"`
	Data  []ClusterMember `json:"data,omitempty"`
}

type ClusterMember struct {
	Address string `json:"address,omitempty"`
	Name    string `json:"name,omitempty"`
	Status  string `json:"status,omitempty"`
}
Apis
// GET /apis
apis, resp, err := client.Apis.GetAll(nil)

// GET /apis?size=10&mapKey=myapi
opt := &ApisGetAllOptions{Size: 10, Name: "myapi"}
apis, resp, err := client.Apis.GetAll(opt)

// GET /apis/myapi
api, resp, err := client.Apis.Get("myapi")

// POST /apis
api := &Api{Name: "myapi", RequestPath: "/myapi", UpstreamURL: "http:myapi:8080"}
resp, err := client.Apis.Post(api)

// PATCH /apis/myapi
api := &Api{Name: "myapi", RequestPath: "/myapi", UpstreamURL: "http:myapi:8080"}
resp, err := client.Apis.Patch(api)

// DELETE /apis/myapi
resp, err := client.Apis.Delete("myapi")
type Apis struct {
	Data   []Api  `json:"data,omitempty"`
	Total  int    `json:"total,omitempty"`
	Next   string `json:"next,omitempty"`
	Offset string `json:"offset,omitempty"`
}

type Api struct {
	UpstreamURL      string `json:"upstream_url,omitempty"`
	StripRequestPath bool   `json:"strip_request_path,omitempty"`
	RequestPath      string `json:"request_path,omitempty"`
	ID               string `json:"id,omitempty"`
	CreatedAt        int64  `json:"created_at,omitempty"`
	PreserveHost     bool   `json:"preserve_host,omitempty"`
	Name             string `json:"name,omitempty"`
}

type ApisGetAllOptions struct {
	ID          string `url:"id,omitempty"`           
	Name        string `url:"name,omitempty"`         
	RequestHost string `url:"request_host,omitempty"` 
	RequestPath string `url:"request_path,omitempty"`
	UpstreamURL string `url:"upstream_url,omitempty"`
	Size        int    `url:"size,omitempty"`        
	Offset      string `url:"offset,omitempty"`      
}
Consumers
// GET /consumers
consumers, resp, err := client.Consumers.GetAll(nil)

// GET /consumers?size=10&custom_id=nickname
opt := &ConsumersGetAllOptions{Size: 10, CustomID: "nickname"}
consumers, resp, err := client.Consumers.GetAll(opt)

// GET /consumers/admin
consumer, resp, err := client.Consumers.Get("admin")

// POST /consumers
consumer := &Consumer{Username: "admin"}
resp, err := client.Consumers.Post(consumer)

// PATCH /consumers/admin
consumer := &Consumer{CustomID: "superuser"}
resp, err := client.Consumers.Patch(consumer)

// DELETE /consumers/admin
resp, err := client.Consumers.Delete("admin")
type Consumers struct {
	Data  []Consumer `json:"consumer,omitempty"`
	Total int        `json:"total,omitempty"`
	Next  string     `json:"next,omitempty"`
}

type Consumer struct {
	ID        string `json:"id,omitempty"`
	Username  string `json:"username,omitempty"`
	CustomID  string `json:"custom_id,omitempty"`
	CreatedAt int    `json:"created_at,omitempty"`
}

type ConsumersGetAllOptions struct {
	ID       string `url:"id,omitempty"`       
	CustomID string `url:"custom_id,omitempty"`
	Username string `url:"username,omitempty"`  
	Size     int    `url:"size,omitempty"`   
	Offset   string `url:"offset,omitempty"`
}
Plugins

Because of the generic nature of Kong's plugin object, the Config field of the Plugin struct is defined as follows:

type Plugin struct {
    ...
    Config map[string]interface{} `json:"config,omitempty"`
    ...
}

All of the various Kong plugin configurations will eventually be defined. Two helper functions have been written to help marshal plugin configurations to/from more specific configuration structs to the more generic map[string]interface{}

For example:

// Convert struct to map[string]interface{} expected by kong.Plugin
aclConfig := &kong.ACLConfig{Whitelist: []string{"users", "admins"}, Blacklist: []string{"blocked"}}
plugin := &kong.Plugin{Name: "acl", Config: kong.ToMap(aclConfig)}

// Convert map[string]interface{} returned by client.Plugins.Get to struct
plugin, _, _ := client.Plugins.Get(id)
aclConfig := new(ACLConfig)
err := kong.FromMap(aclConfig, plugin.Config)
// GET /plugins
plugins, resp, err := client.Plugins.GetAll(nil)

// GET /plugins?size=15&consumer_id=4def15f5-0697-4956-a2b0-9ae079b686bb
opt := &kong.PluginsGetAllOptions{Size: 15, ConsumerID: "4def15f5-0697-4956-a2b0-9ae079b686bb"}
plugins, resp, err := client.Plugins.GetAll(opt)

// GET /plugins/4def15f5-0697-4956-a2b0-9ae079b686bb
plugin, resp, err := client.Plugins.Get("4def15f5-0697-4956-a2b0-9ae079b686bb")

// GET /plugins/enabled
enabled, resp, err := client.Plugins.GetEnabled()

// GET /plugins/schema/acl
schema, resp, err := client.Plugins.GetSchema("acl")

// POST /plugins
aclConfig := &kong.ACLConfig{Whitelist: []string{"users", "admins"}, Blacklist: []string{"blocked"}}
plugin := &kong.Plugin{Name: "acl", Config: kong.ToMap(aclConfig)}
resp, err := client.Plugins.Post(plugin)

// PATCH /plugins
aclConfig := &kong.ACLConfig{Whitelist: []string{"users", "admins"}, Blacklist: []string{"blocked"}}
plugin := &kong.Plugin{Name: "acl", Config: kong.ToMap(aclConfig)}
resp, err := client.Plugins.Patch(plugin)

// DELETE /plugins/4def15f5-0697-4956-a2b0-9ae079b686bb
resp, err := client.Plugins.Delete("4def15f5-0697-4956-a2b0-9ae079b686bb")
type Plugins struct {
	Data  []Plugin `json:"data,omitempty"`
	Total int      `json:"total,omitempty"`
	Next  string   `json:"next,omitempty"`
}

type Plugin struct {
	ID         string                 `json:"id,omitempty"`
	Name       string                 `json:"name,omitempty"`
	CreatedAt  int                    `json:"created_at,omitempty"`
	Enabled    bool                   `json:"enabled,omitempty"`
	ApiID      string                 `json:"api_id,omitempty"`
	ConsumerID string                 `json:"consumer_id,omitempty"`
	Config     map[string]interface{} `json:"config,omitempty"`
}

type PluginsGetAllOptions struct {
	ID         string `url:"id,omitempty"`          // A filter on the list based on the id field.
	Name       string `url:"name,omitempty"`        // A filter on the list based on the name field.
	ApiID      string `url:"api_id,omitempty"`      // A filter on the list based on the api_id field.
	ConsumerID string `url:"consumer_id,omitempty"` // A filter on the list based on the consumer_id field.
	Size       int    `url:"size,omitempty"`        // A limit on the number of objects to be returned.
	Offset     string `url:"offset,omitempty"`      // A cursor used for pagination. offset is an object identifier that defines a place in the list.

}

In addition to the generic plugin struct definitions, there are many more structures defined for each plugin configuration in plugins.go

Consumers Plugins

This section of the codebase is very much in progress. At the moment only a few plugins can be configured. When I have time I plan to add the rest.

// GET /consumers/paul.atredies/acl
acls, resp, err := client.Consumers.Plugins.ACL.GetAll("paul.atredies")

// DELETE /consumers/paul.atredies/acl/4def15f5-0697-4956-a2b0-9ae079b686bb
resp, err := client.Consumers.Plugins.ACL.Delete("paul.atredies", "4def15f5-0697-4956-a2b0-9ae079b686bb")

// POST /consumers/paul.atredies/acl
aclConfig := &kong.ConsumerACLConfig{Group: "kwisatz.haderach"}
resp, err := client.Consumers.Plugins.ACL.Post("paul.atredies", aclConfig)

Handling Errors

Every client method returns either (*http.Response, error) or (*kong.Object, *http.Response, error)

The *http.Response object can be used by the caller to inspect the actual response object returned by kong.

In cases where Kong returns a 404 or 409 the returned error will have one of the associated kong.ErrorResponse types.

type ErrorResponse struct {
	Response    *http.Response // HTTP response that caused this error
	KongMessage string         `json:"message,omitempty"`
	KongError   string         `json:"error,omitempty"`
}

type ConflictError ErrorResponse //409
type NotFoundError ErrorResponse //404

You can explicitly check for these cases by using type assertions on the returned error value

aclConsumerConfig := &kong.ConsumerACLConfig{Group: "kwisatz.haderach"}
_, err = client.Consumers.Plugins.ACL.Post("paul.atredies", aclConsumerConfig)
if _, ok := err.(*kong.NotFoundError); ok {
    log.Fatal("Could not find consumer paul.atredies")
} else if err != nil {
    log.Fatal(err)
}

Filtering with Query Parameters

When executing GET requests that return multiple objects the results can be filtered by supplying one of the following objects where appropriate.
The supplied struct fields will be used as query parameters.

type ApisGetAllOptions struct {
	ID          string `url:"id,omitempty"`           // A filter on the list based on the apis id field.
	Name        string `url:"name,omitempty"`         // A filter on the list based on the apis name field.
	RequestHost string `url:"request_host,omitempty"` // A filter on the list based on the apis request_host field.
	RequestPath string `url:"request_path,omitempty"` // A filter on the list based on the apis request_path field.
	UpstreamURL string `url:"upstream_url,omitempty"` // A filter on the list based on the apis upstream_url field.
	Size        int    `url:"size,omitempty"`         // A limit on the number of objects to be returned.
	Offset      string `url:"offset,omitempty"`       // A cursor used for pagination. offset is an object identifier that defines a place in the list.
}

type ConsumersGetAllOptions struct {
	ID       string `url:"id,omitempty"`        // A filter on the list based on the consumer id field.
	CustomID string `url:"custom_id,omitempty"` // A filter on the list based on the consumer custom_id field.
	Username string `url:"username,omitempty"`  // A filter on the list based on the consumer username field.
	Size     int    `url:"size,omitempty"`      // A limit on the number of objects to be returned.
	Offset   string `url:"offset,omitempty"`    // A cursor used for pagination. offset is an object identifier that defines a place in the list.
}

type PluginsGetAllOptions struct {
	ID         string `url:"id,omitempty"`          // A filter on the list based on the id field.
	Name       string `url:"name,omitempty"`        // A filter on the list based on the name field.
	ApiID      string `url:"api_id,omitempty"`      // A filter on the list based on the api_id field.
	ConsumerID string `url:"consumer_id,omitempty"` // A filter on the list based on the consumer_id field.
	Size       int    `url:"size,omitempty"`        // A limit on the number of objects to be returned.
	Offset     string `url:"offset,omitempty"`      // A cursor used for pagination. offset is an object identifier that defines a place in the list.
}

For example:

// GET /consumers?username=admin&size=10
opt := &kong.ConsumersGetAllOptions{Username: "admin", Size: 10}
consumers, _, _ := client.Consumers.GetAll(nil)

To-Do

  • Finish the README.md
  • Fuller Unit-testing
  • Represent all plugin object configs via structs
  • Represent all consumer plugin configs via structs

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL