genapi

package
v0.0.0-...-0f89747 Latest Latest
Warning

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

Go to latest
Published: Sep 11, 2018 License: MIT Imports: 37 Imported by: 4

Documentation

Overview

Package genapi implements a generic skeleton we can use as the basis for an api service. It will set up command line arguments, connections to backend databases, handle test modes which might affect those databases, register itself with skyapi, and more.

Basic definition

To use first initialize a GenAPI instance somewhere accessible by your entire application, and give it an RPC type

package myapi

var GA = genapi.GenAPI{
	Name:      "my-api",
	RedisInfo: &genapi.RedisInfo{}
	Services:  []interface{}{MyAPI{}},
}

type MyAPI struct{}

func (_ MyAPI) Foo(r *http.Request, args *struct{}, res *struct{}) error {
	return GA.Cmd("INCR", "MyKey").Err
}

API Mode

To actually read command-line arguments, set up database connections, listen on a random port, register with skyapi, and start handling requests, simply call APIMode() from your main method:

package main

func main() {
	myapi.GA.APIMode()
}

In APIMode the genapi will also listen for SIGTERM signals, and if it receives one will unregister with skyapi, and exit once all ongoing requests are completed.

Test Mode

When testing your api you can call TestMode from your test's init function, and call RPC to get an instance of an http.Handler you can make calls against:

package myapi // myapi_test.go

import . "testing"

func init() {
	GA.TestMode()
}

func TestSomeThing(t *T) {
	h := GA.RPC()
	// test against h
}

CLI Mode

Finally, there are times when you want a command-line binary which will be made alongside the actual api binary, and which will share resources and possibly database connections. In this case you can use the CLIMode method and then access the GenAPI from your main method as normal:

package main

func main() {
	myapi.GA.CLIMode()
	myapi.GA.Cmd("DECR", "MyKey")
}

Index

Constants

View Source
const (
	APIMode  = "api"
	TestMode = "test"
	CLIMode  = "cli"
)

The different possible Mode values for GenAPI

Variables

View Source
var (
	BuildCommit    string
	BuildDate      string
	BuildGoVersion string
)

Build variables which can be set during the go build command. E.g.: -ldflags "-X 'github.com/levenlabs/golib/genapi.BuildCommit commitHash'" These fields will be used to construct the string printed out when the --version flag is used.

Functions

func AddCORSHeaders

func AddCORSHeaders(handler http.Handler) http.Handler

AddCORSHeaders accepts a http.Handler and returns a http.Handler that adds CORS headers when an Origin is sent. No validation on the origin is done

func ContextApply

func ContextApply(r *http.Request, ctx context.Context)

ContextApply takes the context and adds information from it to the given Request, so that if the Request is sent to another genapi the Context will be propogated

func ContextKV

func ContextKV(ctx context.Context) llog.KV

ContextKV returns the llog.KV associated with the given context, which was presumably returned from RequestContext. If no llog.KV is on the context, and empty llog.KV is returned

func ContextMergeKV

func ContextMergeKV(ctx context.Context, kvs ...llog.KV) context.Context

ContextMergeKV returns a context with the given set of llog.KVs merged into it. Merging has the same behavior as the llog.Merge function

func HTTPDefaultClient

func HTTPDefaultClient() *http.Client

HTTPDefaultClient returns a *http.Client with sane defaults that can be overridden on a case-by-case basis if you can't use http.DefaultClient

func Version

func Version() string

Version compiles the build strings into a string which will be printed out when --version is used in a GenAPI instance, but is exposed so it may be used other places too.

Types

type Caller

type Caller interface {
	Call(ctx context.Context, res interface{}, method string, args interface{}) error
}

Caller provides a way of calling RPC methods against a pre-defined remote endpoint. The Call method is essentially the same as GenAPI's Call method, but doesn't take in a host parameter

func RetryCaller

func RetryCaller(c Caller, attempts int) Caller

RetryCaller returns a Caller which wraps the given one, passing all Calls back to it. If any return any errors they will be retried the given number of times until one doesn't return an error. The most recent error is returned if all attempts fail.

If attempts is -1 then RetryCaller will never give up

type CallerStub

type CallerStub func(method string, args interface{}) (interface{}, error)

CallerStub provides a convenient way to make stubbed endpoints for testing

func (CallerStub) Call

func (cs CallerStub) Call(_ context.Context, res interface{}, method string, args interface{}) error

Call implements the Call method for the Caller interface. It passed method and args to the underlying CallerStub function. The returned interface from that function is assigned to res (if the underlying types for them are compatible). The passed in context is ignored.

type GenAPI

type GenAPI struct {
	// Required. Name is the name of the api, as it will be identified on the
	// command-line and in skyapi
	Name string

	// The set of rpc service structs which this API will host. Must have at
	// least one service in APIMode
	Services []interface{}

	// Like Services, but these will not be registered with the underlying
	// gateway library, and therefore will not show up in calls to
	// "RPC.GetMethods"
	HiddenServices []interface{}

	// The mux which the rpc services will be added to. If not set a new one
	// will be created and used. This can be used to provide extra functionality
	// in conjunction with the RPC server, or completely in place of it.
	//
	// It is important that RPCEndpoint does *not* have a handler set in this
	// mux, as GenAPI will be setting it itself.
	Mux *http.ServeMux

	// The http endpoint that the RPC handler for Services and HiddenServices
	// will be attached to. Defaults to "/". If you set this to "_", no rpc
	// listener will be set up and its up to you to add the handler from RPC()
	// to the mux for whatever path you need.
	RPCEndpoint string

	// Additional lever.Param structs which can be included in the lever parsing
	LeverParams []lever.Param

	// If mongo is intended to be used as a backend, this should be filled in
	*MongoInfo

	// If redis is intended to be used, this should be filled in.
	*RedisInfo

	// If okq is intended to be used, this should be filled in.
	*OkqInfo

	// If TLS is intended to be used, this should be filled in. The Certs field
	// of TLSInfo may be filled in during the Init function for convenience, but
	// the struct itself must be initialized before any of the Mode methods are
	// called
	*TLSInfo

	// A function to run just after initializing connections to backing
	// database. Meant for performing any initialization needed by the app.
	// This is called before any AppendInit functions
	Init InitFunc

	// May be set if a codec with different parameters is required.
	// If not set an rpcutil.LLCodec with default options will be used.
	Codec rpc.Codec

	// Do not set. This will be automatically filled in with whatever address
	// is being listened on once APIMode is called.
	ListenAddr string

	// Do not set. This will be automatically filled in when any of the run
	// modes are called, and may be used after that point to retrieve parameter
	// values.
	*lever.Lever

	// Do not set. This will be automatically filled in when any of the run
	// modes are called. Indicates which mode the GenAPI is currently in, and
	// may be used after that point to know which run mode GenAPI is in.
	Mode string

	// When initialized, this channel will be closed at the end of the init
	// phase of running. If in APIMode it will be closed just before the call to
	// ListenAndServe. This is useful so you can call APIMode in a separate
	// go-routine and know when it's started listening, if there's other steps
	// you want to take after initialization has been done.
	InitDoneCh chan bool

	// When initialized, this channel will be closed when in APIMode and cleanup
	// has been completed after a kill signal. This is useful if you have other
	// cleanup you want to run after GenAPI is done.
	DoneCh chan bool

	// Optional set of remote APIs (presumably GenAPIs, but that's not actually
	// required) that this one will be calling. The key should be the name of
	// the remote api, and the value should be the default address for it. Each
	// one will have a configuration option added for its address (e.g. if
	// "other-api" is in this list, then "--other-api-addr" will be a config
	// option). Each key can be used as an argument to RemoteAPICaller to obtain
	// a convenient function for communicating with other apis.
	RemoteAPIs map[string]string

	// Optional set of Healthers which should be checked during a /health-check.
	// These will be checked sequentially, and if any return an error that will
	// be logged and the health check will return false. The key in the map is a
	// name for the Healther which can be logged
	Healthers map[string]Healther

	// SRVClient which will be used by GenAPI when resolving requests, and which
	// can also be used by other processes as well. This should only be modified
	// during the init function
	*srvclient.SRVClient
	// contains filtered or unexported fields
}

GenAPI is a type used to handle most of the generic logic we always implement when making an RPC API endpoint.

The struct is initialized with whatever parameters are appropriate, and then has either APIMode(), TestMode(), or CLIMode() called on it depending on the intent. Fields are optional unless otherwise marked in the comment.

func (*GenAPI) APIMode

func (g *GenAPI) APIMode()

APIMode puts the GenAPI into APIMode, wherein it listens for any incoming rpc requests and tries to serve them against one of its Services. This method will block indefinitely

func (*GenAPI) AddHealther

func (g *GenAPI) AddHealther(key string, healther Healther)

AddHealther adds a healther to Healthers under the specified key

func (*GenAPI) AppendInit

func (g *GenAPI) AppendInit(f InitFunc)

AppendInit adds a function to be called when GenAPI is initialized. It will be called after GenAPI's Init() and after any previous functions that were appended

func (*GenAPI) CLIMode

func (g *GenAPI) CLIMode()

CLIMode puts the GenAPI into CLIMode, wherein it is then prepared to be used by a command-line utility

func (*GenAPI) Call

func (g *GenAPI) Call(ctx context.Context, res interface{}, host, method string, args interface{}) error

Call makes an rpc call, presumably to another genapi server but really it only has to be a JSONRPC2 server. If it is another genapi server, however, the given context will be propagated to it, as well as being used here as a timeout if deadline is set on it. See rpcutil for more on how the rest of the arguments work.

Note that host can be a hostname, and address (host:port), or a url (http[s]://host[:port])

func (*GenAPI) NewCaller

func (g *GenAPI) NewCaller(addr string) Caller

NewCaller returns an instance of a Caller which will make RPC requests against the given address, after doing a SRV request on it before each request

func (*GenAPI) RPC

func (g *GenAPI) RPC() http.Handler

RPC returns an http.Handler which will handle the RPC calls made against it for the GenAPI's Services

func (*GenAPI) RPCListen

func (g *GenAPI) RPCListen()

RPCListen sets up listeners for the GenAPI listen and starts them up. This may only be called after TestMode or CLIMode has been called, it is automatically done for APIMode.

func (*GenAPI) ReloadListeners

func (g *GenAPI) ReloadListeners() error

ReloadListeners reloads the listener configurations of all existing listeners. This doesn't actually close the listen sockets, just hot reloads the configuration. Goes through each listener sequentially and returns the first error it encounters.

func (*GenAPI) RemoteAPIAddr

func (g *GenAPI) RemoteAPIAddr(remoteAPI string) string

RemoteAPIAddr returns an address to use for the given remoteAPI (which must be defined in RemoteAPIs). The address will have had SRV called on it already. A Fatal will be thrown if no address has been provided for the remote API

func (*GenAPI) RemoteAPICaller

func (g *GenAPI) RemoteAPICaller(remoteAPI string) Caller

RemoteAPICaller takes in the name of a remote API instance defined in the RemoteAPIs field, and returns a function which can be used to make RPC calls against it. The arguments to the returned function are essentially the same as those to the Call method, sans the host argument. A Fatal will be thrown if no address has been provided for the remote API

func (*GenAPI) RequestContext

func (g *GenAPI) RequestContext(r *http.Request) context.Context

RequestContext returns a context for the given request. The context will be cancelled if the request is closed, and may possibly have a deadline on it as well

func (*GenAPI) TestMode

func (g *GenAPI) TestMode()

TestMode puts the GenAPI into TestMode, wherein it is then prepared to be used for during go tests

type Healther

type Healther interface {
	Healthy() error
}

Healther is an interface that any entity can implement which will report back whether or not that entity is "healthy". An unhealthy entity is, in effect, saying that it could potentially do it's job but at the moment is should not be relied on to do so

type InitFunc

type InitFunc func(*GenAPI)

InitFunc is just a helper for a function that accepts a GenAPI pointer

type MongoInfo

type MongoInfo struct {
	// If you want to make mongo optional, set this and if --mongo-addr isn't
	// sent, then WithDB, WithColl will call fn with nil and SessionHelper's
	// session will be nil.
	Optional bool

	// The name of the mongo database this app should use. In TestMode this will
	// always be overwritten to "test_<DBName>"
	DBName string
	// contains filtered or unexported fields
}

MongoInfo contains information needed by the api to interact with a mongo backend, and also houses the connection to that backend (which can be interacted with through its methods)

func (*MongoInfo) CollSH

func (m *MongoInfo) CollSH(collName string) mgoutil.SessionHelper

CollSH returns an mgoutil.SessionHelper for a collection of the given name The SessionHelper's Session might be nil if you made mongo Optional.

func (*MongoInfo) WithColl

func (m *MongoInfo) WithColl(collName string, fn func(*mgo.Collection))

WithColl is similar to mgoutil.SessionHelper's WithColl, see those docs for more details

func (*MongoInfo) WithDB

func (m *MongoInfo) WithDB(fn func(*mgo.Database))

WithDB is similar to mgoutil.SessionHelper's WithDB, see those docs for more details

type OkqInfo

type OkqInfo struct {
	// If you want to make okq optional, set this and if --okq-addr isn't sent,
	// Client will return nil
	Optional bool

	// Read/Write timeout for redis connection and the NotifyTimeout for Client.
	// Defaults to 30 seconds
	// Do not change after initializing GenAPI
	Timeout time.Duration

	*okq.Client
}

OkqInfo is used to tell the api to interact with a set of okq instances.

type RedisInfo

type RedisInfo struct {
	// If you want to make redis optional, set this and if --redis-addr isn't
	// sent, Cmder will be nil.
	Optional bool

	// Populated by the api once a connection to redis is made, and can be used
	// as such. Do not set manually.
	util.Cmder
}

RedisInfo is used to tell the api to interact with a redis backend, and also houses the connection to that backend. If the redis backend is a cluster instance that whole cluster will be connected to

type TLSInfo

type TLSInfo struct {
	// If set to true then the config options for passing in cert files on the
	// command-line will not be used, and instead the Certs field will be
	// expected to be filled in manually during the Init function
	FillCertsManually bool

	// One or more certificates to use for TLS. Will be filled automatically if
	// FillCertsManually is false
	Certs []tls.Certificate

	// SessionTicketKey is used by TLS servers to provide session
	// resumption. If multiple servers are terminating connections for the same
	// host they should all have the same SessionTicketKey. If the
	// SessionTicketKey leaks, previously recorded and future TLS connections
	// using that key are compromised.
	SessionTicketKey [32]byte

	// If set then this config will be used for all tls listeners, regardless of
	// all other factors.
	//
	// NOTE: remember that you probably need to call BuildNameToCertificate on
	// the Config you make
	// NOTE2: you'll need to handle creation of SessionTicketKey yourself as well
	ForceTLSConfig *tls.Config
}

TLSInfo is used to tell the api to use TLS (e.g. https/ssl) when listening for incoming requests

Jump to

Keyboard shortcuts

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