From 1954a49f375f7e85129a3fd491fc5516d0795e08 Mon Sep 17 00:00:00 2001 From: Jesse Haka Date: Mon, 16 Apr 2018 11:40:03 +0200 Subject: [PATCH] add http headers to healthcheck --- autogen/gentemplates/gen.go | 49 +++++ docs/basics.md | 14 ++ docs/configuration/backends/consulcatalog.md | 2 + docs/configuration/backends/docker.md | 2 + docs/configuration/backends/ecs.md | 2 + docs/configuration/backends/marathon.md | 2 + docs/configuration/backends/mesos.md | 2 + docs/configuration/backends/rancher.md | 2 + docs/configuration/backends/servicefabric.md | 2 + healthcheck/healthcheck.go | 28 ++- healthcheck/healthcheck_test.go | 186 +++++++++++++----- .../docker/config_container_docker_test.go | 7 + .../docker/config_container_swarm_test.go | 7 + provider/kv/keynames.go | 2 + provider/kv/kv_config.go | 4 + provider/label/names.go | 4 + provider/label/partial.go | 4 + provider/marathon/config_test.go | 8 + provider/mesos/config_test.go | 8 + provider/rancher/config_test.go | 7 + server/server.go | 2 + templates/consul_catalog.tmpl | 7 + templates/docker.tmpl | 7 + templates/ecs.tmpl | 7 + templates/kv.tmpl | 7 + templates/marathon.tmpl | 7 + templates/mesos.tmpl | 7 + templates/rancher.tmpl | 7 + types/types.go | 8 +- 29 files changed, 337 insertions(+), 64 deletions(-) diff --git a/autogen/gentemplates/gen.go b/autogen/gentemplates/gen.go index 1c6f6129e..ed97b4aa2 100644 --- a/autogen/gentemplates/gen.go +++ b/autogen/gentemplates/gen.go @@ -167,6 +167,13 @@ var _templatesConsul_catalogTmpl = []byte(`[backends] path = "{{ $healthCheck.Path }}" port = {{ $healthCheck.Port }} interval = "{{ $healthCheck.Interval }}" + hostname = "{{ $healthCheck.Hostname }}" + {{if $healthCheck.Headers }} + [backends."backend-{{ $backendName }}".healthCheck.headers] + {{range $k, $v := $healthCheck.Headers }} + {{$k}} = "{{$v}}" + {{end}} + {{end}} {{end}} {{ $buffering := getBuffering $service.TraefikLabels }} @@ -568,6 +575,13 @@ var _templatesDockerTmpl = []byte(`{{$backendServers := .Servers}} path = "{{ $healthCheck.Path }}" port = {{ $healthCheck.Port }} interval = "{{ $healthCheck.Interval }}" + hostname = "{{ $healthCheck.Hostname }}" + {{if $healthCheck.Headers }} + [backends."backend-{{ $backendName }}".healthCheck.headers] + {{range $k, $v := $healthCheck.Headers }} + {{$k}} = "{{$v}}" + {{end}} + {{end}} {{end}} {{ $buffering := getBuffering $backend.SegmentLabels }} @@ -820,6 +834,13 @@ var _templatesEcsTmpl = []byte(`[backends] path = "{{ $healthCheck.Path }}" port = {{ $healthCheck.Port }} interval = "{{ $healthCheck.Interval }}" + hostname = "{{ $healthCheck.Hostname }}" + {{if $healthCheck.Headers }} + [backends."backend-{{ $serviceName }}".healthCheck.headers] + {{range $k, $v := $healthCheck.Headers }} + {{$k}} = "{{$v}}" + {{end}} + {{end}} {{end}} {{ $buffering := getBuffering $firstInstance.TraefikLabels }} @@ -1230,6 +1251,13 @@ var _templatesKvTmpl = []byte(`[backends] path = "{{ $healthCheck.Path }}" port = {{ $healthCheck.Port }} interval = "{{ $healthCheck.Interval }}" + hostname = "{{ $healthCheck.Hostname }}" + {{if $healthCheck.Headers }} + [backends.{{ $backendName }}.healthCheck.headers] + {{range $k, $v := $healthCheck.Headers }} + {{$k}} = "{{$v}}" + {{end}} + {{end}} {{end}} {{ $buffering := getBuffering $backend }} @@ -1524,6 +1552,13 @@ var _templatesMarathonTmpl = []byte(`{{ $apps := .Applications }} path = "{{ $healthCheck.Path }}" port = {{ $healthCheck.Port }} interval = "{{ $healthCheck.Interval }}" + hostname = "{{ $healthCheck.Hostname }}" + {{if $healthCheck.Headers }} + [backends.{{ $backendName }}.healthCheck.headers] + {{range $k, $v := $healthCheck.Headers }} + {{$k}} = "{{$v}}" + {{end}} + {{end}} {{end}} {{ $buffering := getBuffering $app.SegmentLabels }} @@ -1762,6 +1797,13 @@ var _templatesMesosTmpl = []byte(`[backends] path = "{{ $healthCheck.Path }}" port = {{ $healthCheck.Port }} interval = "{{ $healthCheck.Interval }}" + hostname = "{{ $healthCheck.Hostname }}" + {{if $healthCheck.Headers }} + [backends."backend-{{ $backendName }}".healthCheck.headers] + {{range $k, $v := $healthCheck.Headers }} + {{$k}} = "{{$v}}" + {{end}} + {{end}} {{end}} {{ $buffering := getBuffering $app.TraefikLabels }} @@ -2054,6 +2096,13 @@ var _templatesRancherTmpl = []byte(`{{ $backendServers := .Backends }} path = "{{ $healthCheck.Path }}" port = {{ $healthCheck.Port }} interval = "{{ $healthCheck.Interval }}" + hostname = "{{ $healthCheck.Hostname }}" + {{if $healthCheck.Headers }} + [backends."backend-{{ $backendName }}".healthCheck.headers] + {{range $k, $v := $healthCheck.Headers }} + {{$k}} = "{{$v}}" + {{end}} + {{end}} {{end}} {{ $buffering := getBuffering $backend.SegmentLabels }} diff --git a/docs/basics.md b/docs/basics.md index 5a8b98d85..b7ba02fbf 100644 --- a/docs/basics.md +++ b/docs/basics.md @@ -424,6 +424,20 @@ To use a different port for the healthcheck: port = 8080 ``` +Additional http headers and hostname to healthcheck request can be specified, for instance: +```toml +[backends] + [backends.backend1] + [backends.backend1.healthcheck] + path = "/health" + interval = "10s" + hostname = "myhost.com" + port = 8080 + [backends.backend1.healthcheck.headers] + mycustomheader = "foo" + myheader2 = "bar" +``` + ### Servers Servers are simply defined using a `url`. You can also apply a custom `weight` to each server (this will be used by load-balancing). diff --git a/docs/configuration/backends/consulcatalog.md b/docs/configuration/backends/consulcatalog.md index 4712bc6e1..e70dc9a16 100644 --- a/docs/configuration/backends/consulcatalog.md +++ b/docs/configuration/backends/consulcatalog.md @@ -102,6 +102,8 @@ Additional settings can be defined using Consul Catalog tags. | `.backend.healthcheck.path=/health` | Enable health check for the backend, hitting the container at `path`. | | `.backend.healthcheck.port=8080` | Allow to use a different port for the health check. | | `.backend.healthcheck.interval=1s` | Define the health check interval. | +| `.backend.healthcheck.hostname=foobar.com` | Define the health check hostname. | +| `.backend.healthcheck.headers=EXPR` | Define the health check request headers
Format: HEADER:value||HEADER2:value2 | | `.backend.loadbalancer.method=drr` | Override the default `wrr` load balancer algorithm. | | `.backend.loadbalancer.stickiness=true` | Enable backend sticky sessions. | | `.backend.loadbalancer.stickiness.cookieName=NAME` | Manually set the cookie name for sticky sessions. | diff --git a/docs/configuration/backends/docker.md b/docs/configuration/backends/docker.md index eeba988a5..3f0571f81 100644 --- a/docs/configuration/backends/docker.md +++ b/docs/configuration/backends/docker.md @@ -210,6 +210,8 @@ Labels can be used on containers to override default behavior. | `traefik.backend.healthcheck.path=/health` | Enable health check for the backend, hitting the container at `path`. | | `traefik.backend.healthcheck.port=8080` | Allow to use a different port for the health check. | | `traefik.backend.healthcheck.interval=1s` | Define the health check interval. | +| `traefik.backend.healthcheck.hostname=foobar.com` | Define the health check hostname. | +| `traefik.backend.healthcheck.headers=EXPR` | Define the health check request headers
Format: HEADER:value||HEADER2:value2 | | `traefik.backend.loadbalancer.method=drr` | Override the default `wrr` load balancer algorithm | | `traefik.backend.loadbalancer.stickiness=true` | Enable backend sticky sessions | | `traefik.backend.loadbalancer.stickiness.cookieName=NAME` | Manually set the cookie name for sticky sessions | diff --git a/docs/configuration/backends/ecs.md b/docs/configuration/backends/ecs.md index 12c718892..2570c2871 100644 --- a/docs/configuration/backends/ecs.md +++ b/docs/configuration/backends/ecs.md @@ -149,6 +149,8 @@ Labels can be used on task containers to override default behaviour: | `traefik.backend.healthcheck.path=/health` | Enable health check for the backend, hitting the container at `path`. | | `traefik.backend.healthcheck.port=8080` | Allow to use a different port for the health check. | | `traefik.backend.healthcheck.interval=1s` | Define the health check interval. (Default: 30s) | +| `traefik.backend.healthcheck.hostname=foobar.com` | Define the health check hostname. | +| `traefik.backend.healthcheck.headers=EXPR` | Define the health check request headers
Format: HEADER:value||HEADER2:value2 | | `traefik.backend.loadbalancer.method=drr` | Override the default `wrr` load balancer algorithm | | `traefik.backend.loadbalancer.stickiness=true` | Enable backend sticky sessions | | `traefik.backend.loadbalancer.stickiness.cookieName=NAME` | Manually set the cookie name for sticky sessions | diff --git a/docs/configuration/backends/marathon.md b/docs/configuration/backends/marathon.md index ac84293d3..d9b7a87e8 100644 --- a/docs/configuration/backends/marathon.md +++ b/docs/configuration/backends/marathon.md @@ -186,6 +186,8 @@ The following labels can be defined on Marathon applications. They adjust the be | `traefik.backend.healthcheck.path=/health` | Enable health check for the backend, hitting the container at `path`. | | `traefik.backend.healthcheck.port=8080` | Allow to use a different port for the health check. | | `traefik.backend.healthcheck.interval=1s` | Define the health check interval. (Default: 30s) | +| `traefik.backend.healthcheck.hostname=foobar.com` | Define the health check hostname. | +| `traefik.backend.healthcheck.headers=EXPR` | Define the health check request headers
Format: HEADER:value||HEADER2:value2 | | `traefik.backend.loadbalancer.method=drr` | Override the default `wrr` load balancer algorithm | | `traefik.backend.loadbalancer.stickiness=true` | Enable backend sticky sessions | | `traefik.backend.loadbalancer.stickiness.cookieName=NAME` | Manually set the cookie name for sticky sessions | diff --git a/docs/configuration/backends/mesos.md b/docs/configuration/backends/mesos.md index eef67246b..2abad8c01 100644 --- a/docs/configuration/backends/mesos.md +++ b/docs/configuration/backends/mesos.md @@ -123,6 +123,8 @@ The following labels can be defined on Mesos tasks. They adjust the behavior for | `traefik.backend.healthcheck.path=/health` | Enable health check for the backend, hitting the container at `path`. | | `traefik.backend.healthcheck.port=8080` | Allow to use a different port for the health check. | | `traefik.backend.healthcheck.interval=1s` | Define the health check interval. (Default: 30s) | +| `traefik.backend.healthcheck.hostname=foobar.com` | Define the health check hostname. | +| `traefik.backend.healthcheck.headers=EXPR` | Define the health check request headers
Format: HEADER:value||HEADER2:value2 | | `traefik.backend.loadbalancer.method=drr` | Override the default `wrr` load balancer algorithm | | `traefik.backend.loadbalancer.stickiness=true` | Enable backend sticky sessions | | `traefik.backend.loadbalancer.stickiness.cookieName=NAME` | Manually set the cookie name for sticky sessions | diff --git a/docs/configuration/backends/rancher.md b/docs/configuration/backends/rancher.md index 2cec99c26..84a8a80f4 100644 --- a/docs/configuration/backends/rancher.md +++ b/docs/configuration/backends/rancher.md @@ -154,6 +154,8 @@ Labels can be used on task containers to override default behavior: | `traefik.backend.healthcheck.path=/health` | Enable health check for the backend, hitting the container at `path`. | | `traefik.backend.healthcheck.port=8080` | Allow to use a different port for the health check. | | `traefik.backend.healthcheck.interval=1s` | Define the health check interval. | +| `traefik.backend.healthcheck.hostname=foobar.com` | Define the health check hostname. | +| `traefik.backend.healthcheck.headers=EXPR` | Define the health check request headers
Format: HEADER:value||HEADER2:value2 | | `traefik.backend.loadbalancer.method=drr` | Override the default `wrr` load balancer algorithm | | `traefik.backend.loadbalancer.stickiness=true` | Enable backend sticky sessions | | `traefik.backend.loadbalancer.stickiness.cookieName=NAME` | Manually set the cookie name for sticky sessions | diff --git a/docs/configuration/backends/servicefabric.md b/docs/configuration/backends/servicefabric.md index 8cb12f4ae..064816ea5 100644 --- a/docs/configuration/backends/servicefabric.md +++ b/docs/configuration/backends/servicefabric.md @@ -103,6 +103,8 @@ Labels, set through extensions or the property manager, can be used on services | `traefik.backend.healthcheck.path=/health` | Enable health check for the backend, hitting the container at `path`. | | `traefik.backend.healthcheck.port=8080` | Allow to use a different port for the health check. | | `traefik.backend.healthcheck.interval=1s` | Define the health check interval. | +| `traefik.backend.healthcheck.hostname=foobar.com` | Define the health check hostname. | +| `traefik.backend.healthcheck.headers=EXPR` | Define the health check request headers
Format: HEADER:value||HEADER2:value2 | | `traefik.backend.loadbalancer.method=drr` | Override the default `wrr` load balancer algorithm | | `traefik.backend.loadbalancer.stickiness=true` | Enable backend sticky sessions | | `traefik.backend.loadbalancer.stickiness.cookieName=NAME` | Manually set the cookie name for sticky sessions | diff --git a/healthcheck/healthcheck.go b/healthcheck/healthcheck.go index 295f2afe5..94779d6a2 100644 --- a/healthcheck/healthcheck.go +++ b/healthcheck/healthcheck.go @@ -29,6 +29,8 @@ func GetHealthCheck(metrics metricsRegistry) *HealthCheck { // Options are the public health check options. type Options struct { + Headers map[string]string + Hostname string Path string Port int Transport http.RoundTripper @@ -37,7 +39,7 @@ type Options struct { } func (opt Options) String() string { - return fmt.Sprintf("[Path: %s Port: %d Interval: %s]", opt.Path, opt.Port, opt.Interval) + return fmt.Sprintf("[Hostname: %s Headers: %v Path: %s Port: %d Interval: %s]", opt.Hostname, opt.Headers, opt.Path, opt.Port, opt.Interval) } // BackendHealthCheck HealthCheck configuration for a backend @@ -84,7 +86,7 @@ func NewBackendHealthCheck(options Options, backendName string) *BackendHealthCh } } -//SetBackendsConfiguration set backends configuration +// SetBackendsConfiguration set backends configuration func (hc *HealthCheck) SetBackendsConfiguration(parentCtx context.Context, backends map[string]*BackendHealthCheck) { hc.Backends = backends if hc.cancel != nil { @@ -149,20 +151,31 @@ func (hc *HealthCheck) checkBackend(backend *BackendHealthCheck) { } } -func (backend *BackendHealthCheck) newRequest(serverURL *url.URL) (*http.Request, error) { - if backend.Port == 0 { - return http.NewRequest(http.MethodGet, serverURL.String()+backend.Path, nil) +func (b *BackendHealthCheck) newRequest(serverURL *url.URL) (*http.Request, error) { + if b.Port == 0 { + return http.NewRequest(http.MethodGet, serverURL.String()+b.Path, nil) } // copy the url and add the port to the host u := &url.URL{} *u = *serverURL - u.Host = net.JoinHostPort(u.Hostname(), strconv.Itoa(backend.Port)) - u.Path = u.Path + backend.Path + u.Host = net.JoinHostPort(u.Hostname(), strconv.Itoa(b.Port)) + u.Path = u.Path + b.Path return http.NewRequest(http.MethodGet, u.String(), nil) } +// this function adds additional http headers and hostname to http.request +func (b *BackendHealthCheck) addHeadersAndHost(req *http.Request) *http.Request { + if b.Options.Hostname != "" { + req.Host = b.Options.Hostname + } + for k, v := range b.Options.Headers { + req.Header.Set(k, v) + } + return req +} + // checkHealth returns a nil error in case it was successful and otherwise // a non-nil error with a meaningful description why the health check failed. func checkHealth(serverURL *url.URL, backend *BackendHealthCheck) error { @@ -174,6 +187,7 @@ func checkHealth(serverURL *url.URL, backend *BackendHealthCheck) error { if err != nil { return fmt.Errorf("failed to create HTTP request: %s", err) } + req = backend.addHeadersAndHost(req) resp, err := client.Do(req) if err == nil { diff --git a/healthcheck/healthcheck_test.go b/healthcheck/healthcheck_test.go index 5a8481db4..5ef75504a 100644 --- a/healthcheck/healthcheck_test.go +++ b/healthcheck/healthcheck_test.go @@ -10,6 +10,8 @@ import ( "time" "github.com/containous/traefik/testhelpers" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "github.com/vulcand/oxy/roundrobin" ) @@ -21,57 +23,57 @@ type testHandler struct { } func TestSetBackendsConfiguration(t *testing.T) { - tests := []struct { - desc string - startHealthy bool - healthSequence []bool - wantNumRemovedServers int - wantNumUpsertedServers int - wantGaugeValue float64 + testCases := []struct { + desc string + startHealthy bool + healthSequence []bool + expectedNumRemovedServers int + expectedNumUpsertedServers int + expectedGaugeValue float64 }{ { - desc: "healthy server staying healthy", - startHealthy: true, - healthSequence: []bool{true}, - wantNumRemovedServers: 0, - wantNumUpsertedServers: 0, - wantGaugeValue: 1, + desc: "healthy server staying healthy", + startHealthy: true, + healthSequence: []bool{true}, + expectedNumRemovedServers: 0, + expectedNumUpsertedServers: 0, + expectedGaugeValue: 1, }, { - desc: "healthy server becoming sick", - startHealthy: true, - healthSequence: []bool{false}, - wantNumRemovedServers: 1, - wantNumUpsertedServers: 0, - wantGaugeValue: 0, + desc: "healthy server becoming sick", + startHealthy: true, + healthSequence: []bool{false}, + expectedNumRemovedServers: 1, + expectedNumUpsertedServers: 0, + expectedGaugeValue: 0, }, { - desc: "sick server becoming healthy", - startHealthy: false, - healthSequence: []bool{true}, - wantNumRemovedServers: 0, - wantNumUpsertedServers: 1, - wantGaugeValue: 1, + desc: "sick server becoming healthy", + startHealthy: false, + healthSequence: []bool{true}, + expectedNumRemovedServers: 0, + expectedNumUpsertedServers: 1, + expectedGaugeValue: 1, }, { - desc: "sick server staying sick", - startHealthy: false, - healthSequence: []bool{false}, - wantNumRemovedServers: 0, - wantNumUpsertedServers: 0, - wantGaugeValue: 0, + desc: "sick server staying sick", + startHealthy: false, + healthSequence: []bool{false}, + expectedNumRemovedServers: 0, + expectedNumUpsertedServers: 0, + expectedGaugeValue: 0, }, { - desc: "healthy server toggling to sick and back to healthy", - startHealthy: true, - healthSequence: []bool{false, true}, - wantNumRemovedServers: 1, - wantNumUpsertedServers: 1, - wantGaugeValue: 1, + desc: "healthy server toggling to sick and back to healthy", + startHealthy: true, + healthSequence: []bool{false, true}, + expectedNumRemovedServers: 1, + expectedNumUpsertedServers: 1, + expectedGaugeValue: 1, }, } - for _, test := range tests { + for _, test := range testCases { test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() @@ -88,6 +90,7 @@ func TestSetBackendsConfiguration(t *testing.T) { Interval: healthCheckInterval, LB: lb, }, "backendName") + serverURL := testhelpers.MustParseURL(ts.URL) if test.startHealthy { lb.servers = append(lb.servers, serverURL) @@ -100,8 +103,10 @@ func TestSetBackendsConfiguration(t *testing.T) { Backends: make(map[string]*BackendHealthCheck), metrics: collectingMetrics, } + wg := sync.WaitGroup{} wg.Add(1) + go func() { check.execute(ctx, backend) wg.Done() @@ -119,23 +124,16 @@ func TestSetBackendsConfiguration(t *testing.T) { lb.Lock() defer lb.Unlock() - if lb.numRemovedServers != test.wantNumRemovedServers { - t.Errorf("got %d removed servers, wanted %d", lb.numRemovedServers, test.wantNumRemovedServers) - } - if lb.numUpsertedServers != test.wantNumUpsertedServers { - t.Errorf("got %d upserted servers, wanted %d", lb.numUpsertedServers, test.wantNumUpsertedServers) - } - - if collectingMetrics.Gauge.GaugeValue != test.wantGaugeValue { - t.Errorf("got %v ServerUp Gauge, want %v", collectingMetrics.Gauge.GaugeValue, test.wantGaugeValue) - } + assert.Equal(t, test.expectedNumRemovedServers, lb.numRemovedServers, "removed servers") + assert.Equal(t, test.expectedNumUpsertedServers, lb.numUpsertedServers, "upserted servers") + assert.Equal(t, test.expectedGaugeValue, collectingMetrics.Gauge.GaugeValue, "ServerUp Gauge") }) } } func TestNewRequest(t *testing.T) { - tests := []struct { + testCases := []struct { desc string host string port int @@ -172,10 +170,11 @@ func TestNewRequest(t *testing.T) { }, } - for _, test := range tests { + for _, test := range testCases { test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() + backend := NewBackendHealthCheck( Options{ Path: test.path, @@ -187,15 +186,94 @@ func TestNewRequest(t *testing.T) { Host: test.host, } + req, err := backend.newRequest(u) + require.NoError(t, err, "failed to create new backend request") + + assert.Equal(t, test.expected, req.URL.String()) + }) + } +} + +func TestNewRequestWithAddHeaders(t *testing.T) { + testCases := []struct { + desc string + host string + headers map[string]string + hostname string + port int + path string + expectedHostname string + expectedHeader string + }{ + { + desc: "override hostname", + host: "backend1:80", + headers: map[string]string{}, + hostname: "myhost", + port: 0, + path: "/", + expectedHostname: "myhost", + expectedHeader: "", + }, + { + desc: "not override hostname", + host: "backend1:80", + headers: map[string]string{}, + hostname: "", + port: 0, + path: "/", + expectedHostname: "backend1:80", + expectedHeader: "", + }, + { + desc: "custom header", + host: "backend1:80", + headers: map[string]string{"Custom-Header": "foo"}, + hostname: "", + port: 0, + path: "/", + expectedHostname: "backend1:80", + expectedHeader: "foo", + }, + { + desc: "custom header with host override", + host: "backend1:80", + headers: map[string]string{"Custom-Header": "foo"}, + hostname: "myhost", + port: 0, + path: "/", + expectedHostname: "myhost", + expectedHeader: "foo", + }, + } + + for _, test := range testCases { + test := test + t.Run(test.desc, func(t *testing.T) { + t.Parallel() + + backend := NewBackendHealthCheck( + Options{ + Hostname: test.hostname, + Path: test.path, + Port: test.port, + Headers: test.headers, + }, "backendName") + + u := &url.URL{ + Scheme: "http", + Host: test.host, + } + req, err := backend.newRequest(u) if err != nil { t.Fatalf("failed to create new backend request: %s", err) } - actual := req.URL.String() - if actual != test.expected { - t.Fatalf("got %s for healthcheck URL, wanted %s", actual, test.expected) - } + req = backend.addHeadersAndHost(req) + + assert.Equal(t, test.expectedHostname, req.Host) + assert.Equal(t, test.expectedHeader, req.Header.Get("Custom-Header")) }) } } diff --git a/provider/docker/config_container_docker_test.go b/provider/docker/config_container_docker_test.go index d05497235..be8e8a67a 100644 --- a/provider/docker/config_container_docker_test.go +++ b/provider/docker/config_container_docker_test.go @@ -101,6 +101,8 @@ func TestDockerBuildConfiguration(t *testing.T) { label.TraefikBackendHealthCheckPath: "/health", label.TraefikBackendHealthCheckPort: "880", label.TraefikBackendHealthCheckInterval: "6", + label.TraefikBackendHealthCheckHostname: "foo.com", + label.TraefikBackendHealthCheckHeaders: "Foo:bar || Bar:foo", label.TraefikBackendLoadBalancerMethod: "drr", label.TraefikBackendLoadBalancerSticky: "true", label.TraefikBackendLoadBalancerStickiness: "true", @@ -293,6 +295,11 @@ func TestDockerBuildConfiguration(t *testing.T) { Path: "/health", Port: 880, Interval: "6", + Hostname: "foo.com", + Headers: map[string]string{ + "Foo": "bar", + "Bar": "foo", + }, }, Buffering: &types.Buffering{ MaxResponseBodyBytes: 10485760, diff --git a/provider/docker/config_container_swarm_test.go b/provider/docker/config_container_swarm_test.go index 056f47549..8b37b95c3 100644 --- a/provider/docker/config_container_swarm_test.go +++ b/provider/docker/config_container_swarm_test.go @@ -110,6 +110,8 @@ func TestSwarmBuildConfiguration(t *testing.T) { label.TraefikBackendHealthCheckPath: "/health", label.TraefikBackendHealthCheckPort: "880", label.TraefikBackendHealthCheckInterval: "6", + label.TraefikBackendHealthCheckHostname: "foo.com", + label.TraefikBackendHealthCheckHeaders: "Foo:bar || Bar:foo", label.TraefikBackendLoadBalancerMethod: "drr", label.TraefikBackendLoadBalancerSticky: "true", label.TraefikBackendLoadBalancerStickiness: "true", @@ -299,6 +301,11 @@ func TestSwarmBuildConfiguration(t *testing.T) { Path: "/health", Port: 880, Interval: "6", + Hostname: "foo.com", + Headers: map[string]string{ + "Foo": "bar", + "Bar": "foo", + }, }, Buffering: &types.Buffering{ MaxResponseBodyBytes: 10485760, diff --git a/provider/kv/keynames.go b/provider/kv/keynames.go index 5d9268c46..01a2d67e1 100644 --- a/provider/kv/keynames.go +++ b/provider/kv/keynames.go @@ -6,6 +6,8 @@ const ( pathBackendHealthCheckPath = "/healthcheck/path" pathBackendHealthCheckPort = "/healthcheck/port" pathBackendHealthCheckInterval = "/healthcheck/interval" + pathBackendHealthCheckHostname = "/healthcheck/hostname" + pathBackendHealthCheckHeaders = "/healthcheck/headers/" pathBackendLoadBalancerMethod = "/loadbalancer/method" pathBackendLoadBalancerSticky = "/loadbalancer/sticky" pathBackendLoadBalancerStickiness = "/loadbalancer/stickiness" diff --git a/provider/kv/kv_config.go b/provider/kv/kv_config.go index 10e657a8a..ee962e515 100644 --- a/provider/kv/kv_config.go +++ b/provider/kv/kv_config.go @@ -302,8 +302,12 @@ func (p *Provider) getHealthCheck(rootPath string) *types.HealthCheck { port := p.getInt(label.DefaultBackendHealthCheckPort, rootPath, pathBackendHealthCheckPort) interval := p.get("30s", rootPath, pathBackendHealthCheckInterval) + hostname := p.get("", rootPath, pathBackendHealthCheckHostname) + headers := p.getMap(rootPath, pathBackendHealthCheckHeaders) return &types.HealthCheck{ + Hostname: hostname, + Headers: headers, Path: path, Port: port, Interval: interval, diff --git a/provider/label/names.go b/provider/label/names.go index ae0aaaede..e34e6d5d8 100644 --- a/provider/label/names.go +++ b/provider/label/names.go @@ -17,6 +17,8 @@ const ( SuffixBackendHealthCheckPath = "backend.healthcheck.path" SuffixBackendHealthCheckPort = "backend.healthcheck.port" SuffixBackendHealthCheckInterval = "backend.healthcheck.interval" + SuffixBackendHealthCheckHostname = "backend.healthcheck.hostname" + SuffixBackendHealthCheckHeaders = "backend.healthcheck.headers" SuffixBackendLoadBalancer = "backend.loadbalancer" SuffixBackendLoadBalancerMethod = SuffixBackendLoadBalancer + ".method" SuffixBackendLoadBalancerSticky = SuffixBackendLoadBalancer + ".sticky" @@ -83,6 +85,8 @@ const ( TraefikBackendHealthCheckPath = Prefix + SuffixBackendHealthCheckPath TraefikBackendHealthCheckPort = Prefix + SuffixBackendHealthCheckPort TraefikBackendHealthCheckInterval = Prefix + SuffixBackendHealthCheckInterval + TraefikBackendHealthCheckHostname = Prefix + SuffixBackendHealthCheckHostname + TraefikBackendHealthCheckHeaders = Prefix + SuffixBackendHealthCheckHeaders TraefikBackendLoadBalancer = Prefix + SuffixBackendLoadBalancer TraefikBackendLoadBalancerMethod = Prefix + SuffixBackendLoadBalancerMethod TraefikBackendLoadBalancerSticky = Prefix + SuffixBackendLoadBalancerSticky diff --git a/provider/label/partial.go b/provider/label/partial.go index 5ccd9ba3c..043f13652 100644 --- a/provider/label/partial.go +++ b/provider/label/partial.go @@ -236,8 +236,12 @@ func GetHealthCheck(labels map[string]string) *types.HealthCheck { port := GetIntValue(labels, TraefikBackendHealthCheckPort, DefaultBackendHealthCheckPort) interval := GetStringValue(labels, TraefikBackendHealthCheckInterval, "") + hostname := GetStringValue(labels, TraefikBackendHealthCheckHostname, "") + headers := GetMapValue(labels, TraefikBackendHealthCheckHeaders) return &types.HealthCheck{ + Hostname: hostname, + Headers: headers, Path: path, Port: port, Interval: interval, diff --git a/provider/marathon/config_test.go b/provider/marathon/config_test.go index 709dd13b8..f063c5670 100644 --- a/provider/marathon/config_test.go +++ b/provider/marathon/config_test.go @@ -176,6 +176,9 @@ func TestBuildConfiguration(t *testing.T) { withLabel(label.TraefikBackendHealthCheckPath, "/health"), withLabel(label.TraefikBackendHealthCheckPort, "880"), withLabel(label.TraefikBackendHealthCheckInterval, "6"), + withLabel(label.TraefikBackendHealthCheckHostname, "foo.com"), + withLabel(label.TraefikBackendHealthCheckHeaders, "Foo:bar || Bar:foo"), + withLabel(label.TraefikBackendLoadBalancerMethod, "drr"), withLabel(label.TraefikBackendLoadBalancerSticky, "true"), withLabel(label.TraefikBackendLoadBalancerStickiness, "true"), @@ -365,6 +368,11 @@ func TestBuildConfiguration(t *testing.T) { Path: "/health", Port: 880, Interval: "6", + Hostname: "foo.com", + Headers: map[string]string{ + "Foo": "bar", + "Bar": "foo", + }, }, Buffering: &types.Buffering{ MaxResponseBodyBytes: 10485760, diff --git a/provider/mesos/config_test.go b/provider/mesos/config_test.go index 2fd96a0e4..e686c847d 100644 --- a/provider/mesos/config_test.go +++ b/provider/mesos/config_test.go @@ -126,6 +126,9 @@ func TestBuildConfiguration(t *testing.T) { withLabel(label.TraefikBackendHealthCheckPath, "/health"), withLabel(label.TraefikBackendHealthCheckPort, "880"), withLabel(label.TraefikBackendHealthCheckInterval, "6"), + withLabel(label.TraefikBackendHealthCheckHostname, "foo.com"), + withLabel(label.TraefikBackendHealthCheckHeaders, "Foo:bar || Bar:foo"), + withLabel(label.TraefikBackendLoadBalancerMethod, "drr"), withLabel(label.TraefikBackendLoadBalancerStickiness, "true"), withLabel(label.TraefikBackendLoadBalancerStickinessCookieName, "chocolate"), @@ -316,6 +319,11 @@ func TestBuildConfiguration(t *testing.T) { Path: "/health", Port: 880, Interval: "6", + Hostname: "foo.com", + Headers: map[string]string{ + "Foo": "bar", + "Bar": "foo", + }, }, Buffering: &types.Buffering{ MaxResponseBodyBytes: 10485760, diff --git a/provider/rancher/config_test.go b/provider/rancher/config_test.go index 9be47c337..f0cf55fd2 100644 --- a/provider/rancher/config_test.go +++ b/provider/rancher/config_test.go @@ -44,6 +44,8 @@ func TestProviderBuildConfiguration(t *testing.T) { label.TraefikBackendHealthCheckPath: "/health", label.TraefikBackendHealthCheckPort: "880", label.TraefikBackendHealthCheckInterval: "6", + label.TraefikBackendHealthCheckHostname: "foo.com", + label.TraefikBackendHealthCheckHeaders: "Foo:bar || Bar:foo", label.TraefikBackendLoadBalancerMethod: "drr", label.TraefikBackendLoadBalancerSticky: "true", label.TraefikBackendLoadBalancerStickiness: "true", @@ -240,6 +242,11 @@ func TestProviderBuildConfiguration(t *testing.T) { Path: "/health", Port: 880, Interval: "6", + Hostname: "foo.com", + Headers: map[string]string{ + "Foo": "bar", + "Bar": "foo", + }, }, Buffering: &types.Buffering{ MaxResponseBodyBytes: 10485760, diff --git a/server/server.go b/server/server.go index 4893ad725..ebbcc1051 100644 --- a/server/server.go +++ b/server/server.go @@ -1386,6 +1386,8 @@ func parseHealthCheckOptions(lb healthcheck.LoadBalancer, backend string, hc *ty } return &healthcheck.Options{ + Hostname: hc.Hostname, + Headers: hc.Headers, Path: hc.Path, Port: hc.Port, Interval: interval, diff --git a/templates/consul_catalog.tmpl b/templates/consul_catalog.tmpl index ed30f0582..49f5d1d49 100644 --- a/templates/consul_catalog.tmpl +++ b/templates/consul_catalog.tmpl @@ -32,6 +32,13 @@ path = "{{ $healthCheck.Path }}" port = {{ $healthCheck.Port }} interval = "{{ $healthCheck.Interval }}" + hostname = "{{ $healthCheck.Hostname }}" + {{if $healthCheck.Headers }} + [backends."backend-{{ $backendName }}".healthCheck.headers] + {{range $k, $v := $healthCheck.Headers }} + {{$k}} = "{{$v}}" + {{end}} + {{end}} {{end}} {{ $buffering := getBuffering $service.TraefikLabels }} diff --git a/templates/docker.tmpl b/templates/docker.tmpl index 72387dbef..ab9eb3ba0 100644 --- a/templates/docker.tmpl +++ b/templates/docker.tmpl @@ -33,6 +33,13 @@ path = "{{ $healthCheck.Path }}" port = {{ $healthCheck.Port }} interval = "{{ $healthCheck.Interval }}" + hostname = "{{ $healthCheck.Hostname }}" + {{if $healthCheck.Headers }} + [backends."backend-{{ $backendName }}".healthCheck.headers] + {{range $k, $v := $healthCheck.Headers }} + {{$k}} = "{{$v}}" + {{end}} + {{end}} {{end}} {{ $buffering := getBuffering $backend.SegmentLabels }} diff --git a/templates/ecs.tmpl b/templates/ecs.tmpl index 5b59889f5..8c5ba15f6 100644 --- a/templates/ecs.tmpl +++ b/templates/ecs.tmpl @@ -32,6 +32,13 @@ path = "{{ $healthCheck.Path }}" port = {{ $healthCheck.Port }} interval = "{{ $healthCheck.Interval }}" + hostname = "{{ $healthCheck.Hostname }}" + {{if $healthCheck.Headers }} + [backends."backend-{{ $serviceName }}".healthCheck.headers] + {{range $k, $v := $healthCheck.Headers }} + {{$k}} = "{{$v}}" + {{end}} + {{end}} {{end}} {{ $buffering := getBuffering $firstInstance.TraefikLabels }} diff --git a/templates/kv.tmpl b/templates/kv.tmpl index 81e278266..3faf1bcf3 100644 --- a/templates/kv.tmpl +++ b/templates/kv.tmpl @@ -32,6 +32,13 @@ path = "{{ $healthCheck.Path }}" port = {{ $healthCheck.Port }} interval = "{{ $healthCheck.Interval }}" + hostname = "{{ $healthCheck.Hostname }}" + {{if $healthCheck.Headers }} + [backends.{{ $backendName }}.healthCheck.headers] + {{range $k, $v := $healthCheck.Headers }} + {{$k}} = "{{$v}}" + {{end}} + {{end}} {{end}} {{ $buffering := getBuffering $backend }} diff --git a/templates/marathon.tmpl b/templates/marathon.tmpl index d9dfc3d6a..51f393c92 100644 --- a/templates/marathon.tmpl +++ b/templates/marathon.tmpl @@ -35,6 +35,13 @@ path = "{{ $healthCheck.Path }}" port = {{ $healthCheck.Port }} interval = "{{ $healthCheck.Interval }}" + hostname = "{{ $healthCheck.Hostname }}" + {{if $healthCheck.Headers }} + [backends.{{ $backendName }}.healthCheck.headers] + {{range $k, $v := $healthCheck.Headers }} + {{$k}} = "{{$v}}" + {{end}} + {{end}} {{end}} {{ $buffering := getBuffering $app.SegmentLabels }} diff --git a/templates/mesos.tmpl b/templates/mesos.tmpl index 30e3cac4e..7a7f0d486 100644 --- a/templates/mesos.tmpl +++ b/templates/mesos.tmpl @@ -35,6 +35,13 @@ path = "{{ $healthCheck.Path }}" port = {{ $healthCheck.Port }} interval = "{{ $healthCheck.Interval }}" + hostname = "{{ $healthCheck.Hostname }}" + {{if $healthCheck.Headers }} + [backends."backend-{{ $backendName }}".healthCheck.headers] + {{range $k, $v := $healthCheck.Headers }} + {{$k}} = "{{$v}}" + {{end}} + {{end}} {{end}} {{ $buffering := getBuffering $app.TraefikLabels }} diff --git a/templates/rancher.tmpl b/templates/rancher.tmpl index f275fe940..aaa611f65 100644 --- a/templates/rancher.tmpl +++ b/templates/rancher.tmpl @@ -34,6 +34,13 @@ path = "{{ $healthCheck.Path }}" port = {{ $healthCheck.Port }} interval = "{{ $healthCheck.Interval }}" + hostname = "{{ $healthCheck.Hostname }}" + {{if $healthCheck.Headers }} + [backends."backend-{{ $backendName }}".healthCheck.headers] + {{range $k, $v := $healthCheck.Headers }} + {{$k}} = "{{$v}}" + {{end}} + {{end}} {{end}} {{ $buffering := getBuffering $backend.SegmentLabels }} diff --git a/types/types.go b/types/types.go index 518d5a365..e9c554830 100644 --- a/types/types.go +++ b/types/types.go @@ -69,9 +69,11 @@ type WhiteList struct { // HealthCheck holds HealthCheck configuration type HealthCheck struct { - Path string `json:"path,omitempty"` - Port int `json:"port,omitempty"` - Interval string `json:"interval,omitempty"` + Headers map[string]string `json:"headers,omitempty"` + Hostname string `json:"hostname,omitempty"` + Path string `json:"path,omitempty"` + Port int `json:"port,omitempty"` + Interval string `json:"interval,omitempty"` } // Server holds server configuration.