diff --git a/docs/toml.md b/docs/toml.md index dc66c04fe..5b64c9a72 100644 --- a/docs/toml.md +++ b/docs/toml.md @@ -269,6 +269,27 @@ Supported filters: # attempts = 3 ``` +## Health check configuration +```toml +# Enable custom health check options. +# +# Optional +# +[healthcheck] + +# Set the default health check interval. Will only be effective if health check +# paths are defined. Given provider-specific support, the value may be +# overridden on a per-backend basis. +# Can be provided in a format supported by [time.ParseDuration](https://golang.org/pkg/time/#ParseDuration) or as raw +# values (digits). If no units are provided, the value is parsed assuming +# seconds. +# +# Optional +# Default: "30s" +# +# interval = "30s" +``` + ## ACME (Let's Encrypt) configuration ```toml diff --git a/healthcheck/healthcheck.go b/healthcheck/healthcheck.go index d91ba1ce5..a1282c3b6 100644 --- a/healthcheck/healthcheck.go +++ b/healthcheck/healthcheck.go @@ -2,6 +2,7 @@ package healthcheck import ( "context" + "fmt" "net/http" "net/url" "sync" @@ -12,9 +13,6 @@ import ( "github.com/vulcand/oxy/roundrobin" ) -// DefaultInterval is the default health check interval. -const DefaultInterval = 30 * time.Second - var singleton *HealthCheck var once sync.Once @@ -33,6 +31,10 @@ type Options struct { LB LoadBalancer } +func (opt Options) String() string { + return fmt.Sprintf("[Path: %s Interval: %s]", opt.Path, opt.Interval) +} + // BackendHealthCheck HealthCheck configuration for a backend type BackendHealthCheck struct { Options diff --git a/server/configuration.go b/server/configuration.go index b3d7df0e9..e6226b8fb 100644 --- a/server/configuration.go +++ b/server/configuration.go @@ -27,6 +27,9 @@ import ( "github.com/containous/traefik/types" ) +// DefaultHealthCheckInterval is the default health check interval. +const DefaultHealthCheckInterval = 30 * time.Second + // TraefikConfiguration holds GlobalConfiguration and other stuff type TraefikConfiguration struct { GlobalConfiguration `mapstructure:",squash"` @@ -52,6 +55,7 @@ type GlobalConfiguration struct { IdleTimeout flaeg.Duration `description:"maximum amount of time an idle (keep-alive) connection will remain idle before closing itself."` InsecureSkipVerify bool `description:"Disable SSL certificate verification"` Retry *Retry `description:"Enable retry sending request if network error"` + HealthCheck *HealthCheckConfig `description:"Health check parameters"` Docker *docker.Provider `description:"Enable Docker backend"` File *file.Provider `description:"Enable File backend"` Web *WebProvider `description:"Enable Web backend"` @@ -337,6 +341,11 @@ type Retry struct { Attempts int `description:"Number of attempts"` } +// HealthCheckConfig contains health check configuration parameters. +type HealthCheckConfig struct { + Interval flaeg.Duration `description:"Default periodicity of enabled health checks"` +} + // NewTraefikDefaultPointersConfiguration creates a TraefikConfiguration with pointers default values func NewTraefikDefaultPointersConfiguration() *TraefikConfiguration { //default Docker @@ -461,6 +470,7 @@ func NewTraefikDefaultPointersConfiguration() *TraefikConfiguration { Rancher: &defaultRancher, DynamoDB: &defaultDynamoDB, Retry: &Retry{}, + HealthCheck: &HealthCheckConfig{}, } //default Rancher @@ -485,7 +495,10 @@ func NewTraefikConfiguration() *TraefikConfiguration { ProvidersThrottleDuration: flaeg.Duration(2 * time.Second), MaxIdleConnsPerHost: 200, IdleTimeout: flaeg.Duration(180 * time.Second), - CheckNewVersion: true, + HealthCheck: &HealthCheckConfig{ + Interval: flaeg.Duration(DefaultHealthCheckInterval), + }, + CheckNewVersion: true, }, ConfigFile: "", } diff --git a/server/server.go b/server/server.go index fd2f158eb..0f7374cf7 100644 --- a/server/server.go +++ b/server/server.go @@ -659,8 +659,9 @@ func (server *Server) loadConfig(configurations configs, globalConfiguration Glo log.Errorf("Skipping frontend %s...", frontendName) continue frontend } - hcOpts := parseHealthCheckOptions(rebalancer, frontend.Backend, configuration.Backends[frontend.Backend].HealthCheck) + hcOpts := parseHealthCheckOptions(rebalancer, frontend.Backend, configuration.Backends[frontend.Backend].HealthCheck, *globalConfiguration.HealthCheck) if hcOpts != nil { + log.Debugf("Setting up backend health check %s", *hcOpts) backendsHealthcheck[frontend.Backend] = healthcheck.NewBackendHealthCheck(*hcOpts) } } @@ -685,8 +686,9 @@ func (server *Server) loadConfig(configurations configs, globalConfiguration Glo continue frontend } } - hcOpts := parseHealthCheckOptions(rr, frontend.Backend, configuration.Backends[frontend.Backend].HealthCheck) + hcOpts := parseHealthCheckOptions(rr, frontend.Backend, configuration.Backends[frontend.Backend].HealthCheck, *globalConfiguration.HealthCheck) if hcOpts != nil { + log.Debugf("Setting up backend health check %s", *hcOpts) backendsHealthcheck[frontend.Backend] = healthcheck.NewBackendHealthCheck(*hcOpts) } } @@ -847,12 +849,12 @@ func (server *Server) buildDefaultHTTPRouter() *mux.Router { return router } -func parseHealthCheckOptions(lb healthcheck.LoadBalancer, backend string, hc *types.HealthCheck) *healthcheck.Options { +func parseHealthCheckOptions(lb healthcheck.LoadBalancer, backend string, hc *types.HealthCheck, hcConfig HealthCheckConfig) *healthcheck.Options { if hc == nil || hc.Path == "" { return nil } - interval := healthcheck.DefaultInterval + interval := time.Duration(hcConfig.Interval) if hc.Interval != "" { intervalOverride, err := time.ParseDuration(hc.Interval) switch { diff --git a/server/server_test.go b/server/server_test.go index c53f31d7e..d74949c2c 100644 --- a/server/server_test.go +++ b/server/server_test.go @@ -7,6 +7,7 @@ import ( "testing" "time" + "github.com/containous/flaeg" "github.com/containous/traefik/healthcheck" "github.com/containous/traefik/types" "github.com/vulcand/oxy/roundrobin" @@ -41,6 +42,7 @@ func TestServerLoadConfigHealthCheckOptions(t *testing.T) { EntryPoints: EntryPoints{ "http": &EntryPoint{}, }, + HealthCheck: &HealthCheckConfig{Interval: flaeg.Duration(5 * time.Second)}, } dynamicConfigs := configs{ @@ -87,6 +89,7 @@ func TestServerLoadConfigHealthCheckOptions(t *testing.T) { func TestServerParseHealthCheckOptions(t *testing.T) { lb := &testLoadBalancer{} + globalInterval := 15 * time.Second tests := []struct { desc string @@ -113,7 +116,7 @@ func TestServerParseHealthCheckOptions(t *testing.T) { }, wantOpts: &healthcheck.Options{ Path: "/path", - Interval: healthcheck.DefaultInterval, + Interval: globalInterval, LB: lb, }, }, @@ -121,11 +124,11 @@ func TestServerParseHealthCheckOptions(t *testing.T) { desc: "sub-zero interval", hc: &types.HealthCheck{ Path: "/path", - Interval: "-15s", + Interval: "-42s", }, wantOpts: &healthcheck.Options{ Path: "/path", - Interval: healthcheck.DefaultInterval, + Interval: globalInterval, LB: lb, }, }, @@ -148,7 +151,7 @@ func TestServerParseHealthCheckOptions(t *testing.T) { t.Run(test.desc, func(t *testing.T) { t.Parallel() - gotOpts := parseHealthCheckOptions(lb, "backend", test.hc) + gotOpts := parseHealthCheckOptions(lb, "backend", test.hc, HealthCheckConfig{Interval: flaeg.Duration(globalInterval)}) if !reflect.DeepEqual(gotOpts, test.wantOpts) { t.Errorf("got health check options %+v, want %+v", gotOpts, test.wantOpts) } diff --git a/traefik.sample.toml b/traefik.sample.toml index 928478ce3..e1981d40c 100644 --- a/traefik.sample.toml +++ b/traefik.sample.toml @@ -311,6 +311,24 @@ # # attempts = 3 +# Enable custom health check options. +# +# Optional +# +# [healthcheck] + +# Set the default health check interval. Will only be effective if health check +# paths are defined. Given provider-specific support, the value may be +# overridden on a per-backend basis. +# Can be provided in a format supported by Go's time.ParseDuration function or +# as raw values (digits). If no units are provided, the value is parsed assuming +# seconds. +# +# Optional +# Default: "30s" +# +# interval = "30s" + ################################################################ # Web configuration backend ################################################################