2017-03-15 21:16:06 +03:00
package server
import (
"fmt"
2017-05-09 23:31:16 +03:00
"net/http"
2017-07-10 13:11:44 +03:00
"net/http/httptest"
2017-03-15 21:16:06 +03:00
"net/url"
"reflect"
"testing"
"time"
2017-03-24 11:36:33 +03:00
"github.com/containous/flaeg"
2017-05-09 23:31:16 +03:00
"github.com/containous/mux"
2017-08-25 17:10:03 +03:00
"github.com/containous/traefik/configuration"
2017-03-15 21:16:06 +03:00
"github.com/containous/traefik/healthcheck"
2017-08-23 21:46:03 +03:00
"github.com/containous/traefik/metrics"
2017-04-18 09:22:06 +03:00
"github.com/containous/traefik/middlewares"
2017-05-09 23:31:16 +03:00
"github.com/containous/traefik/testhelpers"
2017-03-15 21:16:06 +03:00
"github.com/containous/traefik/types"
2017-05-11 01:34:47 +03:00
"github.com/davecgh/go-spew/spew"
2017-04-30 12:22:07 +03:00
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
2017-07-19 13:02:51 +03:00
"github.com/urfave/negroni"
2017-03-15 21:16:06 +03:00
"github.com/vulcand/oxy/roundrobin"
)
type testLoadBalancer struct { }
func ( lb * testLoadBalancer ) RemoveServer ( u * url . URL ) error {
return nil
}
func ( lb * testLoadBalancer ) UpsertServer ( u * url . URL , options ... roundrobin . ServerOption ) error {
return nil
}
func ( lb * testLoadBalancer ) Servers ( ) [ ] * url . URL {
return [ ] * url . URL { }
}
2017-08-18 16:34:04 +03:00
func TestPrepareServerTimeouts ( t * testing . T ) {
tests := [ ] struct {
desc string
2017-08-25 17:10:03 +03:00
globalConfig configuration . GlobalConfiguration
2017-08-18 16:34:04 +03:00
wantIdleTimeout time . Duration
wantReadTimeout time . Duration
wantWriteTimeout time . Duration
} {
{
desc : "full configuration" ,
2017-08-25 17:10:03 +03:00
globalConfig : configuration . GlobalConfiguration {
RespondingTimeouts : & configuration . RespondingTimeouts {
2017-08-18 16:34:04 +03:00
IdleTimeout : flaeg . Duration ( 10 * time . Second ) ,
ReadTimeout : flaeg . Duration ( 12 * time . Second ) ,
WriteTimeout : flaeg . Duration ( 14 * time . Second ) ,
} ,
} ,
wantIdleTimeout : time . Duration ( 10 * time . Second ) ,
wantReadTimeout : time . Duration ( 12 * time . Second ) ,
wantWriteTimeout : time . Duration ( 14 * time . Second ) ,
} ,
{
desc : "using defaults" ,
2017-08-25 17:10:03 +03:00
globalConfig : configuration . GlobalConfiguration { } ,
2017-08-18 16:34:04 +03:00
wantIdleTimeout : time . Duration ( 180 * time . Second ) ,
wantReadTimeout : time . Duration ( 0 * time . Second ) ,
wantWriteTimeout : time . Duration ( 0 * time . Second ) ,
} ,
{
desc : "deprecated IdleTimeout configured" ,
2017-08-25 17:10:03 +03:00
globalConfig : configuration . GlobalConfiguration {
2017-08-18 16:34:04 +03:00
IdleTimeout : flaeg . Duration ( 45 * time . Second ) ,
} ,
wantIdleTimeout : time . Duration ( 45 * time . Second ) ,
wantReadTimeout : time . Duration ( 0 * time . Second ) ,
wantWriteTimeout : time . Duration ( 0 * time . Second ) ,
} ,
{
desc : "deprecated and new IdleTimeout configured" ,
2017-08-25 17:10:03 +03:00
globalConfig : configuration . GlobalConfiguration {
2017-08-18 16:34:04 +03:00
IdleTimeout : flaeg . Duration ( 45 * time . Second ) ,
2017-08-25 17:10:03 +03:00
RespondingTimeouts : & configuration . RespondingTimeouts {
2017-08-18 16:34:04 +03:00
IdleTimeout : flaeg . Duration ( 80 * time . Second ) ,
} ,
} ,
2017-09-20 19:14:03 +03:00
wantIdleTimeout : time . Duration ( 45 * time . Second ) ,
2017-08-18 16:34:04 +03:00
wantReadTimeout : time . Duration ( 0 * time . Second ) ,
wantWriteTimeout : time . Duration ( 0 * time . Second ) ,
} ,
}
for _ , test := range tests {
test := test
t . Run ( test . desc , func ( t * testing . T ) {
t . Parallel ( )
entryPointName := "http"
2017-09-07 21:14:03 +03:00
entryPoint := & configuration . EntryPoint { Address : "localhost:0" }
2017-08-18 16:34:04 +03:00
router := middlewares . NewHandlerSwitcher ( mux . NewRouter ( ) )
srv := NewServer ( test . globalConfig )
2017-08-25 22:32:03 +03:00
httpServer , _ , err := srv . prepareServer ( entryPointName , entryPoint , router )
2017-08-18 16:34:04 +03:00
if err != nil {
t . Fatalf ( "Unexpected error when preparing srv: %s" , err )
}
if httpServer . IdleTimeout != test . wantIdleTimeout {
t . Errorf ( "Got %s as IdleTimeout, want %s" , httpServer . IdleTimeout , test . wantIdleTimeout )
}
if httpServer . ReadTimeout != test . wantReadTimeout {
t . Errorf ( "Got %s as ReadTimeout, want %s" , httpServer . ReadTimeout , test . wantReadTimeout )
}
if httpServer . WriteTimeout != test . wantWriteTimeout {
t . Errorf ( "Got %s as WriteTimeout, want %s" , httpServer . WriteTimeout , test . wantWriteTimeout )
}
} )
}
}
2017-05-09 23:31:16 +03:00
func TestServerMultipleFrontendRules ( t * testing . T ) {
cases := [ ] struct {
expression string
requestURL string
expectedURL string
} {
{
expression : "Host:foo.bar" ,
requestURL : "http://foo.bar" ,
expectedURL : "http://foo.bar" ,
} ,
{
expression : "PathPrefix:/management;ReplacePath:/health" ,
requestURL : "http://foo.bar/management" ,
expectedURL : "http://foo.bar/health" ,
} ,
{
expression : "Host:foo.bar;AddPrefix:/blah" ,
requestURL : "http://foo.bar/baz" ,
expectedURL : "http://foo.bar/blah/baz" ,
} ,
{
expression : "PathPrefixStripRegex:/one/{two}/{three:[0-9]+}" ,
requestURL : "http://foo.bar/one/some/12345/four" ,
expectedURL : "http://foo.bar/four" ,
} ,
{
expression : "PathPrefixStripRegex:/one/{two}/{three:[0-9]+};AddPrefix:/zero" ,
requestURL : "http://foo.bar/one/some/12345/four" ,
expectedURL : "http://foo.bar/zero/four" ,
} ,
{
expression : "AddPrefix:/blah;ReplacePath:/baz" ,
requestURL : "http://foo.bar/hello" ,
expectedURL : "http://foo.bar/baz" ,
} ,
{
expression : "PathPrefixStrip:/management;ReplacePath:/health" ,
requestURL : "http://foo.bar/management" ,
expectedURL : "http://foo.bar/health" ,
} ,
}
for _ , test := range cases {
test := test
t . Run ( test . expression , func ( t * testing . T ) {
t . Parallel ( )
router := mux . NewRouter ( )
route := router . NewRoute ( )
serverRoute := & serverRoute { route : route }
rules := & Rules { route : serverRoute }
expression := test . expression
routeResult , err := rules . Parse ( expression )
if err != nil {
t . Fatalf ( "Error while building route for %s: %+v" , expression , err )
}
request := testhelpers . MustNewRequest ( http . MethodGet , test . requestURL , nil )
routeMatch := routeResult . Match ( request , & mux . RouteMatch { Route : routeResult } )
if ! routeMatch {
t . Fatalf ( "Rule %s doesn't match" , expression )
}
server := new ( Server )
server . wireFrontendBackend ( serverRoute , http . HandlerFunc ( func ( w http . ResponseWriter , r * http . Request ) {
if r . URL . String ( ) != test . expectedURL {
t . Fatalf ( "got URL %s, expected %s" , r . URL . String ( ) , test . expectedURL )
}
} ) )
serverRoute . route . GetHandler ( ) . ServeHTTP ( nil , request )
} )
}
}
2017-03-15 21:16:06 +03:00
func TestServerLoadConfigHealthCheckOptions ( t * testing . T ) {
healthChecks := [ ] * types . HealthCheck {
nil ,
{
Path : "/path" ,
} ,
}
for _ , lbMethod := range [ ] string { "Wrr" , "Drr" } {
for _ , healthCheck := range healthChecks {
t . Run ( fmt . Sprintf ( "%s/hc=%t" , lbMethod , healthCheck != nil ) , func ( t * testing . T ) {
2017-08-25 17:10:03 +03:00
globalConfig := configuration . GlobalConfiguration {
EntryPoints : configuration . EntryPoints {
"http" : & configuration . EntryPoint { } ,
2017-03-15 21:16:06 +03:00
} ,
2017-08-25 17:10:03 +03:00
HealthCheck : & configuration . HealthCheckConfig { Interval : flaeg . Duration ( 5 * time . Second ) } ,
2017-03-15 21:16:06 +03:00
}
2017-08-25 17:10:03 +03:00
dynamicConfigs := types . Configurations {
2017-03-15 21:16:06 +03:00
"config" : & types . Configuration {
Frontends : map [ string ] * types . Frontend {
"frontend" : {
EntryPoints : [ ] string { "http" } ,
Backend : "backend" ,
} ,
} ,
Backends : map [ string ] * types . Backend {
"backend" : {
Servers : map [ string ] types . Server {
"server" : {
URL : "http://localhost" ,
} ,
} ,
LoadBalancer : & types . LoadBalancer {
Method : lbMethod ,
} ,
HealthCheck : healthCheck ,
} ,
} ,
} ,
}
srv := NewServer ( globalConfig )
if _ , err := srv . loadConfig ( dynamicConfigs , globalConfig ) ; err != nil {
t . Fatalf ( "got error: %s" , err )
}
wantNumHealthCheckBackends := 0
if healthCheck != nil {
wantNumHealthCheckBackends = 1
}
gotNumHealthCheckBackends := len ( healthcheck . GetHealthCheck ( ) . Backends )
if gotNumHealthCheckBackends != wantNumHealthCheckBackends {
t . Errorf ( "got %d health check backends, want %d" , gotNumHealthCheckBackends , wantNumHealthCheckBackends )
}
} )
}
}
}
func TestServerParseHealthCheckOptions ( t * testing . T ) {
lb := & testLoadBalancer { }
2017-03-24 11:36:33 +03:00
globalInterval := 15 * time . Second
2017-03-15 21:16:06 +03:00
tests := [ ] struct {
desc string
hc * types . HealthCheck
wantOpts * healthcheck . Options
} {
{
desc : "nil health check" ,
hc : nil ,
wantOpts : nil ,
} ,
{
desc : "empty path" ,
hc : & types . HealthCheck {
Path : "" ,
} ,
wantOpts : nil ,
} ,
{
desc : "unparseable interval" ,
hc : & types . HealthCheck {
Path : "/path" ,
Interval : "unparseable" ,
} ,
wantOpts : & healthcheck . Options {
Path : "/path" ,
2017-03-24 11:36:33 +03:00
Interval : globalInterval ,
2017-03-15 21:16:06 +03:00
LB : lb ,
} ,
} ,
{
desc : "sub-zero interval" ,
hc : & types . HealthCheck {
Path : "/path" ,
2017-03-24 11:36:33 +03:00
Interval : "-42s" ,
2017-03-15 21:16:06 +03:00
} ,
wantOpts : & healthcheck . Options {
Path : "/path" ,
2017-03-24 11:36:33 +03:00
Interval : globalInterval ,
2017-03-15 21:16:06 +03:00
LB : lb ,
} ,
} ,
{
desc : "parseable interval" ,
hc : & types . HealthCheck {
Path : "/path" ,
Interval : "5m" ,
} ,
wantOpts : & healthcheck . Options {
Path : "/path" ,
Interval : 5 * time . Minute ,
LB : lb ,
} ,
} ,
}
for _ , test := range tests {
test := test
t . Run ( test . desc , func ( t * testing . T ) {
t . Parallel ( )
2017-08-25 17:10:03 +03:00
gotOpts := parseHealthCheckOptions ( lb , "backend" , test . hc , & configuration . HealthCheckConfig { Interval : flaeg . Duration ( globalInterval ) } )
2017-03-15 21:16:06 +03:00
if ! reflect . DeepEqual ( gotOpts , test . wantOpts ) {
t . Errorf ( "got health check options %+v, want %+v" , gotOpts , test . wantOpts )
}
} )
}
}
2017-05-15 10:02:32 +03:00
2017-04-30 12:22:07 +03:00
func TestNewServerWithWhitelistSourceRange ( t * testing . T ) {
cases := [ ] struct {
desc string
whitelistStrings [ ] string
middlewareConfigured bool
errMessage string
} {
{
desc : "no whitelists configued" ,
whitelistStrings : nil ,
middlewareConfigured : false ,
errMessage : "" ,
} , {
desc : "whitelists configued" ,
whitelistStrings : [ ] string {
"1.2.3.4/24" ,
"fe80::/16" ,
} ,
middlewareConfigured : true ,
errMessage : "" ,
} , {
desc : "invalid whitelists configued" ,
whitelistStrings : [ ] string {
"foo" ,
} ,
middlewareConfigured : false ,
errMessage : "parsing CIDR whitelist <nil>: invalid CIDR address: foo" ,
} ,
}
for _ , tc := range cases {
tc := tc
t . Run ( tc . desc , func ( t * testing . T ) {
t . Parallel ( )
middleware , err := configureIPWhitelistMiddleware ( tc . whitelistStrings )
if tc . errMessage != "" {
require . EqualError ( t , err , tc . errMessage )
} else {
assert . NoError ( t , err )
if tc . middlewareConfigured {
require . NotNil ( t , middleware , "not expected middleware to be configured" )
} else {
require . Nil ( t , middleware , "expected middleware to be configured" )
}
}
} )
}
}
2017-05-15 10:02:32 +03:00
func TestServerLoadConfigEmptyBasicAuth ( t * testing . T ) {
2017-08-25 17:10:03 +03:00
globalConfig := configuration . GlobalConfiguration {
EntryPoints : configuration . EntryPoints {
"http" : & configuration . EntryPoint { } ,
2017-05-15 10:02:32 +03:00
} ,
}
2017-08-25 17:10:03 +03:00
dynamicConfigs := types . Configurations {
2017-05-15 10:02:32 +03:00
"config" : & types . Configuration {
Frontends : map [ string ] * types . Frontend {
"frontend" : {
EntryPoints : [ ] string { "http" } ,
Backend : "backend" ,
BasicAuth : [ ] string { "" } ,
} ,
} ,
Backends : map [ string ] * types . Backend {
"backend" : {
Servers : map [ string ] types . Server {
"server" : {
URL : "http://localhost" ,
} ,
} ,
LoadBalancer : & types . LoadBalancer {
Method : "Wrr" ,
} ,
} ,
} ,
} ,
}
srv := NewServer ( globalConfig )
if _ , err := srv . loadConfig ( dynamicConfigs , globalConfig ) ; err != nil {
t . Fatalf ( "got error: %s" , err )
}
}
2017-05-11 01:34:47 +03:00
func TestConfigureBackends ( t * testing . T ) {
validMethod := "Drr"
defaultMethod := "wrr"
tests := [ ] struct {
desc string
lb * types . LoadBalancer
wantMethod string
wantSticky bool
} {
{
desc : "valid load balancer method with sticky enabled" ,
lb : & types . LoadBalancer {
Method : validMethod ,
Sticky : true ,
} ,
wantMethod : validMethod ,
wantSticky : true ,
} ,
{
desc : "valid load balancer method with sticky disabled" ,
lb : & types . LoadBalancer {
Method : validMethod ,
Sticky : false ,
} ,
wantMethod : validMethod ,
wantSticky : false ,
} ,
{
desc : "invalid load balancer method with sticky enabled" ,
lb : & types . LoadBalancer {
Method : "Invalid" ,
Sticky : true ,
} ,
wantMethod : defaultMethod ,
wantSticky : true ,
} ,
{
desc : "invalid load balancer method with sticky disabled" ,
lb : & types . LoadBalancer {
Method : "Invalid" ,
Sticky : false ,
} ,
wantMethod : defaultMethod ,
wantSticky : false ,
} ,
{
desc : "missing load balancer" ,
lb : nil ,
wantMethod : defaultMethod ,
wantSticky : false ,
} ,
}
for _ , test := range tests {
test := test
t . Run ( test . desc , func ( t * testing . T ) {
t . Parallel ( )
backend := & types . Backend {
LoadBalancer : test . lb ,
}
2017-05-16 00:53:35 +03:00
srv := Server { }
srv . configureBackends ( map [ string ] * types . Backend {
2017-05-11 01:34:47 +03:00
"backend" : backend ,
} )
wantLB := types . LoadBalancer {
Method : test . wantMethod ,
Sticky : test . wantSticky ,
}
if ! reflect . DeepEqual ( * backend . LoadBalancer , wantLB ) {
t . Errorf ( "got backend load-balancer\n%v\nwant\n%v\n" , spew . Sdump ( backend . LoadBalancer ) , spew . Sdump ( wantLB ) )
}
} )
}
}
2017-04-18 09:22:06 +03:00
2017-07-08 13:21:14 +03:00
func TestServerEntrypointWhitelistConfig ( t * testing . T ) {
tests := [ ] struct {
desc string
2017-08-25 17:10:03 +03:00
entrypoint * configuration . EntryPoint
2017-07-08 13:21:14 +03:00
wantMiddleware bool
} {
{
desc : "no whitelist middleware if no config on entrypoint" ,
2017-08-25 17:10:03 +03:00
entrypoint : & configuration . EntryPoint {
2017-09-07 21:14:03 +03:00
Address : ":0" ,
2017-07-08 13:21:14 +03:00
} ,
wantMiddleware : false ,
} ,
{
desc : "whitelist middleware should be added if configured on entrypoint" ,
2017-08-25 17:10:03 +03:00
entrypoint : & configuration . EntryPoint {
2017-09-07 21:14:03 +03:00
Address : ":0" ,
2017-07-08 13:21:14 +03:00
WhitelistSourceRange : [ ] string {
"127.0.0.1/32" ,
} ,
} ,
wantMiddleware : true ,
} ,
}
for _ , test := range tests {
test := test
t . Run ( test . desc , func ( t * testing . T ) {
t . Parallel ( )
srv := Server {
2017-08-25 17:10:03 +03:00
globalConfiguration : configuration . GlobalConfiguration {
EntryPoints : map [ string ] * configuration . EntryPoint {
2017-07-08 13:21:14 +03:00
"test" : test . entrypoint ,
} ,
} ,
2017-08-23 21:46:03 +03:00
metricsRegistry : metrics . NewVoidRegistry ( ) ,
2017-07-08 13:21:14 +03:00
}
srv . serverEntryPoints = srv . buildEntryPoints ( srv . globalConfiguration )
srvEntryPoint := srv . setupServerEntryPoint ( "test" , srv . serverEntryPoints [ "test" ] )
handler := srvEntryPoint . httpServer . Handler . ( * negroni . Negroni )
found := false
for _ , handler := range handler . Handlers ( ) {
if reflect . TypeOf ( handler ) == reflect . TypeOf ( ( * middlewares . IPWhitelister ) ( nil ) ) {
found = true
}
}
if found && ! test . wantMiddleware {
t . Errorf ( "ip whitelist middleware was installed even though it should not" )
}
if ! found && test . wantMiddleware {
t . Errorf ( "ip whitelist middleware was not installed even though it should have" )
}
} )
}
}
2017-07-10 13:11:44 +03:00
func TestServerResponseEmptyBackend ( t * testing . T ) {
const requestPath = "/path"
const routeRule = "Path:" + requestPath
testCases := [ ] struct {
desc string
dynamicConfig func ( testServerURL string ) * types . Configuration
wantStatusCode int
} {
{
desc : "Ok" ,
dynamicConfig : func ( testServerURL string ) * types . Configuration {
return buildDynamicConfig (
withFrontend ( "frontend" , buildFrontend ( withRoute ( requestPath , routeRule ) ) ) ,
withBackend ( "backend" , buildBackend ( withServer ( "testServer" , testServerURL ) ) ) ,
)
} ,
wantStatusCode : http . StatusOK ,
} ,
{
desc : "No Frontend" ,
dynamicConfig : func ( testServerURL string ) * types . Configuration {
return buildDynamicConfig ( )
} ,
wantStatusCode : http . StatusNotFound ,
} ,
{
desc : "Empty Backend LB-Drr" ,
dynamicConfig : func ( testServerURL string ) * types . Configuration {
return buildDynamicConfig (
withFrontend ( "frontend" , buildFrontend ( withRoute ( requestPath , routeRule ) ) ) ,
withBackend ( "backend" , buildBackend ( withLoadBalancer ( "Drr" , false ) ) ) ,
)
} ,
wantStatusCode : http . StatusServiceUnavailable ,
} ,
{
desc : "Empty Backend LB-Drr Sticky" ,
dynamicConfig : func ( testServerURL string ) * types . Configuration {
return buildDynamicConfig (
withFrontend ( "frontend" , buildFrontend ( withRoute ( requestPath , routeRule ) ) ) ,
withBackend ( "backend" , buildBackend ( withLoadBalancer ( "Drr" , true ) ) ) ,
)
} ,
wantStatusCode : http . StatusServiceUnavailable ,
} ,
{
desc : "Empty Backend LB-Wrr" ,
dynamicConfig : func ( testServerURL string ) * types . Configuration {
return buildDynamicConfig (
withFrontend ( "frontend" , buildFrontend ( withRoute ( requestPath , routeRule ) ) ) ,
withBackend ( "backend" , buildBackend ( withLoadBalancer ( "Wrr" , false ) ) ) ,
)
} ,
wantStatusCode : http . StatusServiceUnavailable ,
} ,
{
desc : "Empty Backend LB-Wrr Sticky" ,
dynamicConfig : func ( testServerURL string ) * types . Configuration {
return buildDynamicConfig (
withFrontend ( "frontend" , buildFrontend ( withRoute ( requestPath , routeRule ) ) ) ,
withBackend ( "backend" , buildBackend ( withLoadBalancer ( "Wrr" , true ) ) ) ,
)
} ,
wantStatusCode : http . StatusServiceUnavailable ,
} ,
}
for _ , test := range testCases {
test := test
t . Run ( test . desc , func ( t * testing . T ) {
t . Parallel ( )
testServer := httptest . NewServer ( http . HandlerFunc ( func ( rw http . ResponseWriter , req * http . Request ) {
rw . WriteHeader ( http . StatusOK )
} ) )
defer testServer . Close ( )
2017-08-25 17:10:03 +03:00
globalConfig := configuration . GlobalConfiguration {
EntryPoints : configuration . EntryPoints {
"http" : & configuration . EntryPoint { } ,
2017-07-10 13:11:44 +03:00
} ,
}
2017-08-25 17:10:03 +03:00
dynamicConfigs := types . Configurations { "config" : test . dynamicConfig ( testServer . URL ) }
2017-07-10 13:11:44 +03:00
srv := NewServer ( globalConfig )
entryPoints , err := srv . loadConfig ( dynamicConfigs , globalConfig )
if err != nil {
t . Fatalf ( "error loading config: %s" , err )
}
responseRecorder := & httptest . ResponseRecorder { }
request := httptest . NewRequest ( http . MethodGet , testServer . URL + requestPath , nil )
entryPoints [ "http" ] . httpRouter . ServeHTTP ( responseRecorder , request )
if responseRecorder . Result ( ) . StatusCode != test . wantStatusCode {
t . Errorf ( "got status code %d, want %d" , responseRecorder . Result ( ) . StatusCode , test . wantStatusCode )
}
} )
}
}
func buildDynamicConfig ( dynamicConfigBuilders ... func ( * types . Configuration ) ) * types . Configuration {
config := & types . Configuration {
Frontends : make ( map [ string ] * types . Frontend ) ,
Backends : make ( map [ string ] * types . Backend ) ,
}
for _ , build := range dynamicConfigBuilders {
build ( config )
}
return config
}
func withFrontend ( frontendName string , frontend * types . Frontend ) func ( * types . Configuration ) {
return func ( config * types . Configuration ) {
config . Frontends [ frontendName ] = frontend
}
}
func withBackend ( backendName string , backend * types . Backend ) func ( * types . Configuration ) {
return func ( config * types . Configuration ) {
config . Backends [ backendName ] = backend
}
}
func buildFrontend ( frontendBuilders ... func ( * types . Frontend ) ) * types . Frontend {
fe := & types . Frontend {
EntryPoints : [ ] string { "http" } ,
Backend : "backend" ,
Routes : make ( map [ string ] types . Route ) ,
}
for _ , build := range frontendBuilders {
build ( fe )
}
return fe
}
func withRoute ( routeName , rule string ) func ( * types . Frontend ) {
return func ( fe * types . Frontend ) {
fe . Routes [ routeName ] = types . Route { Rule : rule }
}
}
func buildBackend ( backendBuilders ... func ( * types . Backend ) ) * types . Backend {
be := & types . Backend {
Servers : make ( map [ string ] types . Server ) ,
LoadBalancer : & types . LoadBalancer { Method : "Wrr" } ,
}
for _ , build := range backendBuilders {
build ( be )
}
return be
}
func withServer ( name , url string ) func ( backend * types . Backend ) {
return func ( be * types . Backend ) {
be . Servers [ name ] = types . Server { URL : url }
}
}
func withLoadBalancer ( method string , sticky bool ) func ( * types . Backend ) {
return func ( be * types . Backend ) {
be . LoadBalancer = & types . LoadBalancer { Method : method , Sticky : sticky }
}
}