servicefoundation

package module
v4.2.0+incompatible Latest Latest
Warning

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

Go to latest
Published: Oct 26, 2018 License: MIT Imports: 19 Imported by: 0

README

ServiceFoundation Build Status

Go Report Card Coverage Status license

Create new Web Services using convention-based configuration.

More documentation to be found on GoDoc.

ServiceFoundation enables you to create Web Services containing:

  • 3 access levels (public, readiness and internal)
  • Customizable logging (defaults to go-logger)
  • Customizable metrics collection (defaults to go-metrics)
  • Out-of-the-box middleware for panic handling, no-cache, counters, histograms and CORS.
  • Default and overridable handling of catch-all (root), liveness, health, version and readiness
  • Handling of SIGTERM and SIGINT with a custom shutdown function to properly free your own resources.
  • Customizable server timeouts
  • Request/response logging as middleware
  • Support service warm-up through state customization
  • Standardized metrics (defaults to go-metrics)
  • Standardized log messages in JSON format
  • Adding route-specific meta fields to log messages
  • Default handling of pre-flight requests

To do:

  • De-duplicate CORS elements in slices
  • Automated documentation (GoDocs?)

Package usage

Include this package into your project with:

go get github.com/Travix-International/go-servicefoundation

Although all components can be extended, the easiest way to use ServiceFoundation is to use the boilerplate version:

package main

import (
	"context"
	"net/http"

	sf "github.com/Travix-International/go-servicefoundation"
)

var gitHash, versionNumber, buildDate string

func main() {
	svc := sf.NewService(
		"AppGroup","HelloWorldService",
		[]string{http.MethodGet},
		func(log sf.Logger) {
			log.Info("GracefulShutdown", "Handling graceful shutdown")
		},
		sf.BuildVersion{
			GitHash:       gitHash,
			VersionNumber: versionNumber,
			BuildDate:     buildDate,
		}, make(map[string]string))

	svc.AddRoute(
		"helloworld",
		[]string{"/helloworld"},
		sf.MethodsForGet,
		[]sf.Middleware{sf.PanicTo500, sf.CORS, sf.RequestMetrics},
		func(r *http.Request, _ sf.RouterParams) map[string]string {
        		return make(map[string]string)
        },
		func(w sf.WrappedResponseWriter, _ *http.Request, _ sf.RouterParams) {
			w.JSON(http.StatusOK, "hello world!")
		})

	svc.Run(context.Background()) // blocks execution
}

The following environment variables are used by ServiceFoundation:

Name Used for
CORS_ORIGINS Comma-separated list of CORS origins (default:*)
HTTPPORT Port used for exposing the public endpoint (default: 8080)
LOG_MINFILTER Minimum filter for log writing (default: Warning)
APP_NAME Name of the application (HelloWorldService)
SERVER_NAME Name of the server instance (helloworldservice-1234)
DEPLOY_ENVIRONMENT Name of the deployment environment (default: staging)

Dependencies

Although ServiceFoundation contains interfaces to hide any external dependencies, the default configuration depends on the following packages:

Extending ServiceFoundation

You can use the servicefoundation.CreateService(ServiceOptions) method for extension. The provided ServiceOptions struct contains all the things you can extend. If you want to create your own ReadinessHandler, you can do so as follows:

package main

import (
	"context"
	"net/http"
	"time"

	sf "github.com/Travix-International/go-servicefoundation"
)

var gitHash, versionNumber, buildDate string

type CustomServiceStateReader struct {
	sf.ServiceStateReader
	isWarmedUp bool
}

func (r *CustomServiceStateReader) IsLive() bool {
	return true
}

func (r *CustomServiceStateReader) IsReady() bool {
	return r.isWarmedUp
}

func (r *CustomServiceStateReader) IsHealthy() bool {
	return true
}

func main() {
	shutdownFn := func(log sf.Logger) {
		log.Info("GracefulShutdown", "Handling graceful shutdown")
	}

	stateReader := &CustomServiceStateReader{}

	go func() {
		// Simulating warm-up time...
		time.Sleep(10 * time.Second)
		stateReader.isWarmedUp = true
	}()

    // Use a global meta for logging additional fields during the service lifecycle
    globalMeta := make(map[string]string)
    globalMeta["hello"] = "world"

	opt := sf.NewServiceOptions(
		"AppGroup", "HelloWorldService",
		[]string{http.MethodGet},
		shutdownFn,
		sf.BuildVersion{
			GitHash:       gitHash,
			VersionNumber: versionNumber,
			BuildDate:     buildDate,
		}, globalMeta)
	opt.ServiceStateReader = stateReader
	opt.SetHandlers() // Required to re-bind the state to the ReadinessHandler

	// Use this in case you want to handle the public root endpoint yourself instead of relying 
	// on the default catch-all handling.
	opt.UsePublicRootHandler = true

	svc := sf.NewCustomService(opt)

	svc.AddRoute(
		"helloworld",
		[]string{"/helloworld"},
		sf.MethodsForGet,
		[]sf.Middleware{sf.PanicTo500, sf.CORS, sf.RequestMetrics},
		func(r *http.Request, _ sf.RouterParams) map[string]string {
            // Use a route-specific meta to log additional fields for handling a route request 
            routeMeta := make(map[string]string)
            routeMeta["hello"] = "route"
			return routeMeta 
        },
		func(w sf.WrappedResponseWriter, _ *http.Request, _ sf.RouterParams) {
			w.JSON(http.StatusOK, "hello world!")
		})

	svc.Run(context.Background()) // blocks execution
}

license

Documentation

Index

Constants

View Source
const (
	// AcceptHeader is the name of the http Accept header.
	AcceptHeader = "Accept"
	// ContentTypeHeader is the name of the http content type header.
	ContentTypeHeader = "Content-Type"
	// ContentTypeJSON is the value of the http content type header for JSON documents.
	ContentTypeJSON = "application/json"
	// ContentTypeXML is the value of the http content type header for XML documents.
	ContentTypeXML = "application/xml"
)

Variables

View Source
var (
	// MethodsForGet contains a slice with the supported http methods for GET.
	MethodsForGet = []string{http.MethodGet}
	// MethodsForPost contains a slice with the supported http methods for POST.
	MethodsForPost = []string{http.MethodPost}
)
View Source
var DefaultMiddlewares = []Middleware{PanicTo500, NoCaching}

DefaultMiddlewares contains the default middleware wrappers for the predefined service endpoints.

Functions

func NewExitFunc

func NewExitFunc(log Logger, shutdownFunc ShutdownFunc) func(int)

NewExitFunc returns a new exit function. It wraps the shutdownFunc and executed an os.exit after the shutdown is completed with a slight delay, giving the quit handler a chance to return a status.

Types

type BuildVersion

type BuildVersion struct {
	VersionNumber string `json:"version"`
	BuildDate     string `json:"buildDate"`
	GitHash       string `json:"gitHash"`
}

BuildVersion contains the version and build information of the application.

type CORSOptions

type CORSOptions struct {
	// AllowedOrigins is a list of origins a cross-domain request can be executed from.
	// If the special "*" value is present in the list, all origins will be allowed.
	// An origin may contain a wildcard (*) to replace 0 or more characters
	// (i.e.: http://*.domain.com). Usage of wildcards implies a small performance penality.
	// Only one wildcard can be used per origin.
	// Default value is ["*"]
	AllowedOrigins []string
	// AllowedMethods is a list of methods the client is allowed to use with
	// cross-domain requests. Default value is simple methods (GET and POST)
	AllowedMethods []string
	// AllowedHeaders is list of non simple headers the client is allowed to use with
	// cross-domain requests.
	// If the special "*" value is present in the list, all headers will be allowed.
	// Default value is [] but "Origin" is always appended to the list.
	AllowedHeaders []string
	// ExposedHeaders indicates which headers are safe to expose to the API of a CORS
	// API specification
	ExposedHeaders []string
	// MaxAge indicates how long (in seconds) the results of a preflight request
	// can be cached
	MaxAge int
}

CORSOptions contains properties used for handling CORS requests.

type ErrorResponse

type ErrorResponse struct {
	Message string
}

ErrorResponse can be used to to send an error response.

type ExitFunc

type ExitFunc func(int)

ExitFunc is the function signature for the exit function used by Service.

type Handle

Handle is a function signature for the ServiceFoundation handlers

type Handlers

type Handlers struct {
	RootHandler      RootHandler
	ReadinessHandler ReadinessHandler
	LivenessHandler  LivenessHandler
	HealthHandler    HealthHandler
	VersionHandler   VersionHandler
	MetricsHandler   MetricsHandler
	QuitHandler      QuitHandler
	PreFlightHandler PreFlightHandler
}

Handlers is a struct containing references to handler implementations.

type HealthHandler

type HealthHandler interface {
	NewHealthHandler() Handle
}

HealthHandler is an interface to instantiate a new health handler.

type HistogramVec

type HistogramVec interface {
	RecordTimeElapsed(start time.Time)
	RecordDuration(start time.Time, unit time.Duration)
}

HistogramVec is a wrapper around the HistogramVec from the go-metrics package.

type LivenessHandler

type LivenessHandler interface {
	NewLivenessHandler() Handle
}

LivenessHandler is an interface to instantiate a new liveness handler.

type LogFactory

type LogFactory interface {
	NewLogger(meta map[string]string) Logger
}

LogFactory can be used to instantiate a new logger

func NewLogFactory

func NewLogFactory(logFilter string, baseMeta map[string]string) LogFactory

NewLogFactory instantiates a new LogFactory implementation.

type LogFormatter

type LogFormatter interface {
	Format(entry *logger.Entry) (string, error)
}

LogFormatter formats the log entries in a LogStash-compatible JSON string

func NewLogFormatter

func NewLogFormatter() LogFormatter

NewLogFormatter instatiates a new log formatter.

type Logger

type Logger interface {
	Debug(event, formatOrMsg string, a ...interface{}) error
	Info(event, formatOrMsg string, a ...interface{}) error
	Warn(event, formatOrMsg string, a ...interface{}) error
	Error(event, formatOrMsg string, a ...interface{}) error
	GetLogger() *logger.Logger
}

Logger is a wrapper around the Logger package and extending it with log level filtering and simplified formatting.

type MetaFunc

type MetaFunc func(*http.Request, RouterParams) map[string]string

MetaFunc is a function that returns a map containing meta data used to enrich log messages.

type Metrics

type Metrics interface {
	Count(subsystem, name, help string)
	SetGauge(value float64, subsystem, name, help string)
	CountLabels(subsystem, name, help string, labels, values []string)
	IncreaseCounter(subsystem, name, help string, increment int)
	AddHistogramVec(subsystem, name, help string, labels, labelValues []string) HistogramVec
	AddSummaryVec(subsystem, name, help string, labels, labelValues []string) SummaryVec
}

Metrics is a wrapper around the Metrics from the go-metrics package.

func NewMetrics

func NewMetrics(namespace string, logger Logger) Metrics

NewMetrics instantiates a new Metrics implementation.

type MetricsHandler

type MetricsHandler interface {
	NewMetricsHandler() Handle
}

MetricsHandler is an interface to instantiate a new metrics handler.

type Middleware

type Middleware int

Middleware is an enumeration to indicate the available middleware wrappers.

const (
	// CORS is a Middleware enumeration for validating cross-domain requests.
	CORS Middleware = 1
	// NoCaching is a middleware enumeration to adding no-caching headers to the response.
	NoCaching Middleware = 2
	// Counter is a middleware enumeration to add counter metrics to the current request/response.
	Counter Middleware = 3
	// Histogram is a middleware enumeration to add histogram metrics to the current request/response.
	Histogram Middleware = 4
	// PanicTo500 is a middleware enumeration to log panics as errors and respond with http status-code 500.
	PanicTo500 Middleware = 5
	// RequestLogging is a middleware enumeration to log the incoming request and response times.
	RequestLogging Middleware = 6
	// RequestMetrics is a middleware enumeration to measure the incoming request and response times.
	RequestMetrics Middleware = 7
)

type MiddlewareWrapper

type MiddlewareWrapper interface {
	Wrap(subsystem, name string, middleware Middleware, handler Handle, metaFunc MetaFunc) Handle
}

MiddlewareWrapper is an interface to wrap an existing handler with the specified middleware.

func NewMiddlewareWrapper

func NewMiddlewareWrapper(logFactory LogFactory, metrics Metrics, corsOptions *CORSOptions, globals ServiceGlobals) MiddlewareWrapper

NewMiddlewareWrapper instantiates a new MiddlewareWrapper implementation.

type PreFlightHandler

type PreFlightHandler interface {
	NewPreFlightHandler() Handle
}

QuitHandler is an interface to instantiate a new quit handler.

type QuitHandler

type QuitHandler interface {
	NewQuitHandler() Handle
}

QuitHandler is an interface to instantiate a new quit handler.

type ReadinessHandler

type ReadinessHandler interface {
	NewReadinessHandler() Handle
}

ReadinessHandler is an interface to instantiate a new readiness handler.

type RootHandler

type RootHandler interface {
	NewRootHandler() Handle
}

RootHandler is an interface to instantiate a new root handler.

type Router

type Router struct {
	Router *httprouter.Router
}

Router is a struct that wraps httprouter.Router

type RouterFactory

type RouterFactory interface {
	NewRouter() *Router
}

RouterFactory is an interface to create a new Router.

func NewRouterFactory

func NewRouterFactory() RouterFactory

NewRouterFactory instantiates a new RouterFactory implementation.

type RouterParams

type RouterParams struct {
	Params httprouter.Params
}

RouterParams is a struct that wraps httprouter.Params

type Service

type Service interface {
	Run(ctx context.Context)
	AddRoute(name string, routes []string, methods []string, middlewares []Middleware, metaFunc MetaFunc, handler Handle)
}

Service is the main interface for ServiceFoundation and is used to define routing and running the service.

func NewCustomService

func NewCustomService(options ServiceOptions) Service

NewCustomService allows you to customize ServiceFoundation using your own implementations of factories.

func NewService

func NewService(group, name string, allowedMethods []string, shutdownFunc ShutdownFunc, version BuildVersion,
	meta map[string]string) Service

NewService creates and returns a Service that uses environment variables for default configuration.

type ServiceGlobals

type ServiceGlobals struct {
	AppName           string
	GroupName         string
	ServerName        string
	DeployEnvironment string
	VersionNumber     string
}

ServiceGlobals contains basic service properties, like name, deployment environment and version number.

type ServiceHandlerFactory

type ServiceHandlerFactory interface {
	NewHandlers() *Handlers
	WrapHandler
}

ServiceHandlerFactory is an interface to get access to implemented handlers.

func NewServiceHandlerFactory

func NewServiceHandlerFactory(middlewareWrapper MiddlewareWrapper, versionBuilder VersionBuilder,
	stateReader ServiceStateReader, exitFunc ExitFunc) ServiceHandlerFactory

NewServiceHandlerFactory creates a new factory with handler implementations.

type ServiceOptions

type ServiceOptions struct {
	Globals              ServiceGlobals
	Port                 int
	ReadinessPort        int
	InternalPort         int
	LogFactory           LogFactory
	Metrics              Metrics
	RouterFactory        RouterFactory
	MiddlewareWrapper    MiddlewareWrapper
	Handlers             *Handlers
	WrapHandler          WrapHandler
	VersionBuilder       VersionBuilder
	ServiceStateReader   ServiceStateReader
	ShutdownFunc         ShutdownFunc
	ExitFunc             ExitFunc
	ServerTimeout        time.Duration
	IdleTimeout          time.Duration
	UsePublicRootHandler bool
}

ServiceOptions contains value and references used by the Service implementation. The contents of ServiceOptions can be used to customize or extend ServiceFoundation.

func NewServiceOptions

func NewServiceOptions(group, name string, allowedMethods []string, shutdownFunc ShutdownFunc, version BuildVersion,
	meta map[string]string) ServiceOptions

NewServiceOptions creates and returns ServiceOptions that use environment variables for default configuration.

func (*ServiceOptions) SetHandlers

func (o *ServiceOptions) SetHandlers()

SetHandlers is used to update the handler references in ServiceOptions to use the correct middleware and state.

type ServiceStateReader

type ServiceStateReader interface {
	IsLive() bool
	IsReady() bool
	IsHealthy() bool
}

ServiceStateReader contains state methods used by the service's handler implementations.

func NewServiceStateReader

func NewServiceStateReader() ServiceStateReader

NewServiceStateReader instantiates a new basic ServiceStateReader implementation, which always returns true for it's state methods.

type ShutdownFunc

type ShutdownFunc func(log Logger)

ShutdownFunc is a function signature for the shutdown function.

type SummaryVec

type SummaryVec interface {
	RecordTimeElapsed(start time.Time)
	RecordDuration(start time.Time, unit time.Duration)
}

SummaryVec is a wrapper around the v from the go-metrics package.

type VersionBuilder

type VersionBuilder interface {
	ToString() string
	ToMap() map[string]string
}

VersionBuilder contains methods to output version information in string format.

func NewVersionBuilder

func NewVersionBuilder(version BuildVersion) VersionBuilder

NewVersionBuilder creates and returns a VersionBuilder for the given BuildVersion.

type VersionHandler

type VersionHandler interface {
	NewVersionHandler() Handle
}

VersionHandler is an interface to instantiate a new version handler.

type WrapHandler

type WrapHandler interface {
	Wrap(string, string, []Middleware, Handle, MetaFunc) httprouter.Handle
}

WrapHandler is an interface for wrapping a Handle with middleware.

type WrappedResponseWriter

type WrappedResponseWriter interface {
	http.ResponseWriter
	JSON(statusCode int, content interface{})
	XML(statusCode int, content interface{})
	AcceptsXML(r *http.Request) bool
	WriteResponse(r *http.Request, statusCode int, content interface{})
	SetCaching(maxAge int)
	Status() int
}

WrappedResponseWriter is a wrapper around the http.ResponseWriter and extending it with commonly used writing methods.

func NewWrappedResponseWriter

func NewWrappedResponseWriter(w http.ResponseWriter) WrappedResponseWriter

NewWrappedResponseWriter instantiates a new WrappedResponseWriter implementation.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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