add http headers to healthcheck

This commit is contained in:
Jesse Haka 2018-04-16 11:40:03 +02:00 committed by Traefiker Bot
parent de3aeb9732
commit 1954a49f37
29 changed files with 337 additions and 64 deletions

View file

@ -167,6 +167,13 @@ var _templatesConsul_catalogTmpl = []byte(`[backends]
path = "{{ $healthCheck.Path }}" path = "{{ $healthCheck.Path }}"
port = {{ $healthCheck.Port }} port = {{ $healthCheck.Port }}
interval = "{{ $healthCheck.Interval }}" interval = "{{ $healthCheck.Interval }}"
hostname = "{{ $healthCheck.Hostname }}"
{{if $healthCheck.Headers }}
[backends."backend-{{ $backendName }}".healthCheck.headers]
{{range $k, $v := $healthCheck.Headers }}
{{$k}} = "{{$v}}"
{{end}}
{{end}}
{{end}} {{end}}
{{ $buffering := getBuffering $service.TraefikLabels }} {{ $buffering := getBuffering $service.TraefikLabels }}
@ -568,6 +575,13 @@ var _templatesDockerTmpl = []byte(`{{$backendServers := .Servers}}
path = "{{ $healthCheck.Path }}" path = "{{ $healthCheck.Path }}"
port = {{ $healthCheck.Port }} port = {{ $healthCheck.Port }}
interval = "{{ $healthCheck.Interval }}" interval = "{{ $healthCheck.Interval }}"
hostname = "{{ $healthCheck.Hostname }}"
{{if $healthCheck.Headers }}
[backends."backend-{{ $backendName }}".healthCheck.headers]
{{range $k, $v := $healthCheck.Headers }}
{{$k}} = "{{$v}}"
{{end}}
{{end}}
{{end}} {{end}}
{{ $buffering := getBuffering $backend.SegmentLabels }} {{ $buffering := getBuffering $backend.SegmentLabels }}
@ -820,6 +834,13 @@ var _templatesEcsTmpl = []byte(`[backends]
path = "{{ $healthCheck.Path }}" path = "{{ $healthCheck.Path }}"
port = {{ $healthCheck.Port }} port = {{ $healthCheck.Port }}
interval = "{{ $healthCheck.Interval }}" interval = "{{ $healthCheck.Interval }}"
hostname = "{{ $healthCheck.Hostname }}"
{{if $healthCheck.Headers }}
[backends."backend-{{ $serviceName }}".healthCheck.headers]
{{range $k, $v := $healthCheck.Headers }}
{{$k}} = "{{$v}}"
{{end}}
{{end}}
{{end}} {{end}}
{{ $buffering := getBuffering $firstInstance.TraefikLabels }} {{ $buffering := getBuffering $firstInstance.TraefikLabels }}
@ -1230,6 +1251,13 @@ var _templatesKvTmpl = []byte(`[backends]
path = "{{ $healthCheck.Path }}" path = "{{ $healthCheck.Path }}"
port = {{ $healthCheck.Port }} port = {{ $healthCheck.Port }}
interval = "{{ $healthCheck.Interval }}" interval = "{{ $healthCheck.Interval }}"
hostname = "{{ $healthCheck.Hostname }}"
{{if $healthCheck.Headers }}
[backends.{{ $backendName }}.healthCheck.headers]
{{range $k, $v := $healthCheck.Headers }}
{{$k}} = "{{$v}}"
{{end}}
{{end}}
{{end}} {{end}}
{{ $buffering := getBuffering $backend }} {{ $buffering := getBuffering $backend }}
@ -1524,6 +1552,13 @@ var _templatesMarathonTmpl = []byte(`{{ $apps := .Applications }}
path = "{{ $healthCheck.Path }}" path = "{{ $healthCheck.Path }}"
port = {{ $healthCheck.Port }} port = {{ $healthCheck.Port }}
interval = "{{ $healthCheck.Interval }}" interval = "{{ $healthCheck.Interval }}"
hostname = "{{ $healthCheck.Hostname }}"
{{if $healthCheck.Headers }}
[backends.{{ $backendName }}.healthCheck.headers]
{{range $k, $v := $healthCheck.Headers }}
{{$k}} = "{{$v}}"
{{end}}
{{end}}
{{end}} {{end}}
{{ $buffering := getBuffering $app.SegmentLabels }} {{ $buffering := getBuffering $app.SegmentLabels }}
@ -1762,6 +1797,13 @@ var _templatesMesosTmpl = []byte(`[backends]
path = "{{ $healthCheck.Path }}" path = "{{ $healthCheck.Path }}"
port = {{ $healthCheck.Port }} port = {{ $healthCheck.Port }}
interval = "{{ $healthCheck.Interval }}" interval = "{{ $healthCheck.Interval }}"
hostname = "{{ $healthCheck.Hostname }}"
{{if $healthCheck.Headers }}
[backends."backend-{{ $backendName }}".healthCheck.headers]
{{range $k, $v := $healthCheck.Headers }}
{{$k}} = "{{$v}}"
{{end}}
{{end}}
{{end}} {{end}}
{{ $buffering := getBuffering $app.TraefikLabels }} {{ $buffering := getBuffering $app.TraefikLabels }}
@ -2054,6 +2096,13 @@ var _templatesRancherTmpl = []byte(`{{ $backendServers := .Backends }}
path = "{{ $healthCheck.Path }}" path = "{{ $healthCheck.Path }}"
port = {{ $healthCheck.Port }} port = {{ $healthCheck.Port }}
interval = "{{ $healthCheck.Interval }}" interval = "{{ $healthCheck.Interval }}"
hostname = "{{ $healthCheck.Hostname }}"
{{if $healthCheck.Headers }}
[backends."backend-{{ $backendName }}".healthCheck.headers]
{{range $k, $v := $healthCheck.Headers }}
{{$k}} = "{{$v}}"
{{end}}
{{end}}
{{end}} {{end}}
{{ $buffering := getBuffering $backend.SegmentLabels }} {{ $buffering := getBuffering $backend.SegmentLabels }}

View file

@ -424,6 +424,20 @@ To use a different port for the healthcheck:
port = 8080 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
Servers are simply defined using a `url`. You can also apply a custom `weight` to each server (this will be used by load-balancing). Servers are simply defined using a `url`. You can also apply a custom `weight` to each server (this will be used by load-balancing).

View file

@ -102,6 +102,8 @@ Additional settings can be defined using Consul Catalog tags.
| `<prefix>.backend.healthcheck.path=/health` | Enable health check for the backend, hitting the container at `path`. | | `<prefix>.backend.healthcheck.path=/health` | Enable health check for the backend, hitting the container at `path`. |
| `<prefix>.backend.healthcheck.port=8080` | Allow to use a different port for the health check. | | `<prefix>.backend.healthcheck.port=8080` | Allow to use a different port for the health check. |
| `<prefix>.backend.healthcheck.interval=1s` | Define the health check interval. | | `<prefix>.backend.healthcheck.interval=1s` | Define the health check interval. |
| `<prefix>.backend.healthcheck.hostname=foobar.com` | Define the health check hostname. |
| `<prefix>.backend.healthcheck.headers=EXPR` | Define the health check request headers <br>Format: <code>HEADER:value&vert;&vert;HEADER2:value2</code> |
| `<prefix>.backend.loadbalancer.method=drr` | Override the default `wrr` load balancer algorithm. | | `<prefix>.backend.loadbalancer.method=drr` | Override the default `wrr` load balancer algorithm. |
| `<prefix>.backend.loadbalancer.stickiness=true` | Enable backend sticky sessions. | | `<prefix>.backend.loadbalancer.stickiness=true` | Enable backend sticky sessions. |
| `<prefix>.backend.loadbalancer.stickiness.cookieName=NAME` | Manually set the cookie name for sticky sessions. | | `<prefix>.backend.loadbalancer.stickiness.cookieName=NAME` | Manually set the cookie name for sticky sessions. |

View file

@ -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.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.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.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 <br>Format: <code>HEADER:value&vert;&vert;HEADER2:value2</code> |
| `traefik.backend.loadbalancer.method=drr` | Override the default `wrr` load balancer algorithm | | `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=true` | Enable backend sticky sessions |
| `traefik.backend.loadbalancer.stickiness.cookieName=NAME` | Manually set the cookie name for sticky sessions | | `traefik.backend.loadbalancer.stickiness.cookieName=NAME` | Manually set the cookie name for sticky sessions |

View file

@ -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.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.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.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 <br>Format: <code>HEADER:value&vert;&vert;HEADER2:value2</code> |
| `traefik.backend.loadbalancer.method=drr` | Override the default `wrr` load balancer algorithm | | `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=true` | Enable backend sticky sessions |
| `traefik.backend.loadbalancer.stickiness.cookieName=NAME` | Manually set the cookie name for sticky sessions | | `traefik.backend.loadbalancer.stickiness.cookieName=NAME` | Manually set the cookie name for sticky sessions |

View file

@ -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.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.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.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 <br>Format: <code>HEADER:value&vert;&vert;HEADER2:value2</code> |
| `traefik.backend.loadbalancer.method=drr` | Override the default `wrr` load balancer algorithm | | `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=true` | Enable backend sticky sessions |
| `traefik.backend.loadbalancer.stickiness.cookieName=NAME` | Manually set the cookie name for sticky sessions | | `traefik.backend.loadbalancer.stickiness.cookieName=NAME` | Manually set the cookie name for sticky sessions |

View file

@ -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.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.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.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 <br>Format: <code>HEADER:value&vert;&vert;HEADER2:value2</code> |
| `traefik.backend.loadbalancer.method=drr` | Override the default `wrr` load balancer algorithm | | `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=true` | Enable backend sticky sessions |
| `traefik.backend.loadbalancer.stickiness.cookieName=NAME` | Manually set the cookie name for sticky sessions | | `traefik.backend.loadbalancer.stickiness.cookieName=NAME` | Manually set the cookie name for sticky sessions |

View file

@ -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.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.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.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 <br>Format: <code>HEADER:value&vert;&vert;HEADER2:value2</code> |
| `traefik.backend.loadbalancer.method=drr` | Override the default `wrr` load balancer algorithm | | `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=true` | Enable backend sticky sessions |
| `traefik.backend.loadbalancer.stickiness.cookieName=NAME` | Manually set the cookie name for sticky sessions | | `traefik.backend.loadbalancer.stickiness.cookieName=NAME` | Manually set the cookie name for sticky sessions |

View file

@ -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.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.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.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 <br>Format: <code>HEADER:value&vert;&vert;HEADER2:value2</code> |
| `traefik.backend.loadbalancer.method=drr` | Override the default `wrr` load balancer algorithm | | `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=true` | Enable backend sticky sessions |
| `traefik.backend.loadbalancer.stickiness.cookieName=NAME` | Manually set the cookie name for sticky sessions | | `traefik.backend.loadbalancer.stickiness.cookieName=NAME` | Manually set the cookie name for sticky sessions |

View file

@ -29,6 +29,8 @@ func GetHealthCheck(metrics metricsRegistry) *HealthCheck {
// Options are the public health check options. // Options are the public health check options.
type Options struct { type Options struct {
Headers map[string]string
Hostname string
Path string Path string
Port int Port int
Transport http.RoundTripper Transport http.RoundTripper
@ -37,7 +39,7 @@ type Options struct {
} }
func (opt Options) String() string { 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 // 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) { func (hc *HealthCheck) SetBackendsConfiguration(parentCtx context.Context, backends map[string]*BackendHealthCheck) {
hc.Backends = backends hc.Backends = backends
if hc.cancel != nil { if hc.cancel != nil {
@ -149,20 +151,31 @@ func (hc *HealthCheck) checkBackend(backend *BackendHealthCheck) {
} }
} }
func (backend *BackendHealthCheck) newRequest(serverURL *url.URL) (*http.Request, error) { func (b *BackendHealthCheck) newRequest(serverURL *url.URL) (*http.Request, error) {
if backend.Port == 0 { if b.Port == 0 {
return http.NewRequest(http.MethodGet, serverURL.String()+backend.Path, nil) return http.NewRequest(http.MethodGet, serverURL.String()+b.Path, nil)
} }
// copy the url and add the port to the host // copy the url and add the port to the host
u := &url.URL{} u := &url.URL{}
*u = *serverURL *u = *serverURL
u.Host = net.JoinHostPort(u.Hostname(), strconv.Itoa(backend.Port)) u.Host = net.JoinHostPort(u.Hostname(), strconv.Itoa(b.Port))
u.Path = u.Path + backend.Path u.Path = u.Path + b.Path
return http.NewRequest(http.MethodGet, u.String(), nil) 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 // 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. // a non-nil error with a meaningful description why the health check failed.
func checkHealth(serverURL *url.URL, backend *BackendHealthCheck) error { func checkHealth(serverURL *url.URL, backend *BackendHealthCheck) error {
@ -174,6 +187,7 @@ func checkHealth(serverURL *url.URL, backend *BackendHealthCheck) error {
if err != nil { if err != nil {
return fmt.Errorf("failed to create HTTP request: %s", err) return fmt.Errorf("failed to create HTTP request: %s", err)
} }
req = backend.addHeadersAndHost(req)
resp, err := client.Do(req) resp, err := client.Do(req)
if err == nil { if err == nil {

View file

@ -10,6 +10,8 @@ import (
"time" "time"
"github.com/containous/traefik/testhelpers" "github.com/containous/traefik/testhelpers"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/vulcand/oxy/roundrobin" "github.com/vulcand/oxy/roundrobin"
) )
@ -21,57 +23,57 @@ type testHandler struct {
} }
func TestSetBackendsConfiguration(t *testing.T) { func TestSetBackendsConfiguration(t *testing.T) {
tests := []struct { testCases := []struct {
desc string desc string
startHealthy bool startHealthy bool
healthSequence []bool healthSequence []bool
wantNumRemovedServers int expectedNumRemovedServers int
wantNumUpsertedServers int expectedNumUpsertedServers int
wantGaugeValue float64 expectedGaugeValue float64
}{ }{
{ {
desc: "healthy server staying healthy", desc: "healthy server staying healthy",
startHealthy: true, startHealthy: true,
healthSequence: []bool{true}, healthSequence: []bool{true},
wantNumRemovedServers: 0, expectedNumRemovedServers: 0,
wantNumUpsertedServers: 0, expectedNumUpsertedServers: 0,
wantGaugeValue: 1, expectedGaugeValue: 1,
}, },
{ {
desc: "healthy server becoming sick", desc: "healthy server becoming sick",
startHealthy: true, startHealthy: true,
healthSequence: []bool{false}, healthSequence: []bool{false},
wantNumRemovedServers: 1, expectedNumRemovedServers: 1,
wantNumUpsertedServers: 0, expectedNumUpsertedServers: 0,
wantGaugeValue: 0, expectedGaugeValue: 0,
}, },
{ {
desc: "sick server becoming healthy", desc: "sick server becoming healthy",
startHealthy: false, startHealthy: false,
healthSequence: []bool{true}, healthSequence: []bool{true},
wantNumRemovedServers: 0, expectedNumRemovedServers: 0,
wantNumUpsertedServers: 1, expectedNumUpsertedServers: 1,
wantGaugeValue: 1, expectedGaugeValue: 1,
}, },
{ {
desc: "sick server staying sick", desc: "sick server staying sick",
startHealthy: false, startHealthy: false,
healthSequence: []bool{false}, healthSequence: []bool{false},
wantNumRemovedServers: 0, expectedNumRemovedServers: 0,
wantNumUpsertedServers: 0, expectedNumUpsertedServers: 0,
wantGaugeValue: 0, expectedGaugeValue: 0,
}, },
{ {
desc: "healthy server toggling to sick and back to healthy", desc: "healthy server toggling to sick and back to healthy",
startHealthy: true, startHealthy: true,
healthSequence: []bool{false, true}, healthSequence: []bool{false, true},
wantNumRemovedServers: 1, expectedNumRemovedServers: 1,
wantNumUpsertedServers: 1, expectedNumUpsertedServers: 1,
wantGaugeValue: 1, expectedGaugeValue: 1,
}, },
} }
for _, test := range tests { for _, test := range testCases {
test := test test := test
t.Run(test.desc, func(t *testing.T) { t.Run(test.desc, func(t *testing.T) {
t.Parallel() t.Parallel()
@ -88,6 +90,7 @@ func TestSetBackendsConfiguration(t *testing.T) {
Interval: healthCheckInterval, Interval: healthCheckInterval,
LB: lb, LB: lb,
}, "backendName") }, "backendName")
serverURL := testhelpers.MustParseURL(ts.URL) serverURL := testhelpers.MustParseURL(ts.URL)
if test.startHealthy { if test.startHealthy {
lb.servers = append(lb.servers, serverURL) lb.servers = append(lb.servers, serverURL)
@ -100,8 +103,10 @@ func TestSetBackendsConfiguration(t *testing.T) {
Backends: make(map[string]*BackendHealthCheck), Backends: make(map[string]*BackendHealthCheck),
metrics: collectingMetrics, metrics: collectingMetrics,
} }
wg := sync.WaitGroup{} wg := sync.WaitGroup{}
wg.Add(1) wg.Add(1)
go func() { go func() {
check.execute(ctx, backend) check.execute(ctx, backend)
wg.Done() wg.Done()
@ -119,23 +124,16 @@ func TestSetBackendsConfiguration(t *testing.T) {
lb.Lock() lb.Lock()
defer lb.Unlock() 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 { assert.Equal(t, test.expectedNumRemovedServers, lb.numRemovedServers, "removed servers")
t.Errorf("got %d upserted servers, wanted %d", lb.numUpsertedServers, test.wantNumUpsertedServers) assert.Equal(t, test.expectedNumUpsertedServers, lb.numUpsertedServers, "upserted servers")
} assert.Equal(t, test.expectedGaugeValue, collectingMetrics.Gauge.GaugeValue, "ServerUp Gauge")
if collectingMetrics.Gauge.GaugeValue != test.wantGaugeValue {
t.Errorf("got %v ServerUp Gauge, want %v", collectingMetrics.Gauge.GaugeValue, test.wantGaugeValue)
}
}) })
} }
} }
func TestNewRequest(t *testing.T) { func TestNewRequest(t *testing.T) {
tests := []struct { testCases := []struct {
desc string desc string
host string host string
port int port int
@ -172,10 +170,11 @@ func TestNewRequest(t *testing.T) {
}, },
} }
for _, test := range tests { for _, test := range testCases {
test := test test := test
t.Run(test.desc, func(t *testing.T) { t.Run(test.desc, func(t *testing.T) {
t.Parallel() t.Parallel()
backend := NewBackendHealthCheck( backend := NewBackendHealthCheck(
Options{ Options{
Path: test.path, Path: test.path,
@ -187,15 +186,94 @@ func TestNewRequest(t *testing.T) {
Host: test.host, 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) req, err := backend.newRequest(u)
if err != nil { if err != nil {
t.Fatalf("failed to create new backend request: %s", err) t.Fatalf("failed to create new backend request: %s", err)
} }
actual := req.URL.String() req = backend.addHeadersAndHost(req)
if actual != test.expected {
t.Fatalf("got %s for healthcheck URL, wanted %s", actual, test.expected) assert.Equal(t, test.expectedHostname, req.Host)
} assert.Equal(t, test.expectedHeader, req.Header.Get("Custom-Header"))
}) })
} }
} }

View file

@ -101,6 +101,8 @@ func TestDockerBuildConfiguration(t *testing.T) {
label.TraefikBackendHealthCheckPath: "/health", label.TraefikBackendHealthCheckPath: "/health",
label.TraefikBackendHealthCheckPort: "880", label.TraefikBackendHealthCheckPort: "880",
label.TraefikBackendHealthCheckInterval: "6", label.TraefikBackendHealthCheckInterval: "6",
label.TraefikBackendHealthCheckHostname: "foo.com",
label.TraefikBackendHealthCheckHeaders: "Foo:bar || Bar:foo",
label.TraefikBackendLoadBalancerMethod: "drr", label.TraefikBackendLoadBalancerMethod: "drr",
label.TraefikBackendLoadBalancerSticky: "true", label.TraefikBackendLoadBalancerSticky: "true",
label.TraefikBackendLoadBalancerStickiness: "true", label.TraefikBackendLoadBalancerStickiness: "true",
@ -293,6 +295,11 @@ func TestDockerBuildConfiguration(t *testing.T) {
Path: "/health", Path: "/health",
Port: 880, Port: 880,
Interval: "6", Interval: "6",
Hostname: "foo.com",
Headers: map[string]string{
"Foo": "bar",
"Bar": "foo",
},
}, },
Buffering: &types.Buffering{ Buffering: &types.Buffering{
MaxResponseBodyBytes: 10485760, MaxResponseBodyBytes: 10485760,

View file

@ -110,6 +110,8 @@ func TestSwarmBuildConfiguration(t *testing.T) {
label.TraefikBackendHealthCheckPath: "/health", label.TraefikBackendHealthCheckPath: "/health",
label.TraefikBackendHealthCheckPort: "880", label.TraefikBackendHealthCheckPort: "880",
label.TraefikBackendHealthCheckInterval: "6", label.TraefikBackendHealthCheckInterval: "6",
label.TraefikBackendHealthCheckHostname: "foo.com",
label.TraefikBackendHealthCheckHeaders: "Foo:bar || Bar:foo",
label.TraefikBackendLoadBalancerMethod: "drr", label.TraefikBackendLoadBalancerMethod: "drr",
label.TraefikBackendLoadBalancerSticky: "true", label.TraefikBackendLoadBalancerSticky: "true",
label.TraefikBackendLoadBalancerStickiness: "true", label.TraefikBackendLoadBalancerStickiness: "true",
@ -299,6 +301,11 @@ func TestSwarmBuildConfiguration(t *testing.T) {
Path: "/health", Path: "/health",
Port: 880, Port: 880,
Interval: "6", Interval: "6",
Hostname: "foo.com",
Headers: map[string]string{
"Foo": "bar",
"Bar": "foo",
},
}, },
Buffering: &types.Buffering{ Buffering: &types.Buffering{
MaxResponseBodyBytes: 10485760, MaxResponseBodyBytes: 10485760,

View file

@ -6,6 +6,8 @@ const (
pathBackendHealthCheckPath = "/healthcheck/path" pathBackendHealthCheckPath = "/healthcheck/path"
pathBackendHealthCheckPort = "/healthcheck/port" pathBackendHealthCheckPort = "/healthcheck/port"
pathBackendHealthCheckInterval = "/healthcheck/interval" pathBackendHealthCheckInterval = "/healthcheck/interval"
pathBackendHealthCheckHostname = "/healthcheck/hostname"
pathBackendHealthCheckHeaders = "/healthcheck/headers/"
pathBackendLoadBalancerMethod = "/loadbalancer/method" pathBackendLoadBalancerMethod = "/loadbalancer/method"
pathBackendLoadBalancerSticky = "/loadbalancer/sticky" pathBackendLoadBalancerSticky = "/loadbalancer/sticky"
pathBackendLoadBalancerStickiness = "/loadbalancer/stickiness" pathBackendLoadBalancerStickiness = "/loadbalancer/stickiness"

View file

@ -302,8 +302,12 @@ func (p *Provider) getHealthCheck(rootPath string) *types.HealthCheck {
port := p.getInt(label.DefaultBackendHealthCheckPort, rootPath, pathBackendHealthCheckPort) port := p.getInt(label.DefaultBackendHealthCheckPort, rootPath, pathBackendHealthCheckPort)
interval := p.get("30s", rootPath, pathBackendHealthCheckInterval) interval := p.get("30s", rootPath, pathBackendHealthCheckInterval)
hostname := p.get("", rootPath, pathBackendHealthCheckHostname)
headers := p.getMap(rootPath, pathBackendHealthCheckHeaders)
return &types.HealthCheck{ return &types.HealthCheck{
Hostname: hostname,
Headers: headers,
Path: path, Path: path,
Port: port, Port: port,
Interval: interval, Interval: interval,

View file

@ -17,6 +17,8 @@ const (
SuffixBackendHealthCheckPath = "backend.healthcheck.path" SuffixBackendHealthCheckPath = "backend.healthcheck.path"
SuffixBackendHealthCheckPort = "backend.healthcheck.port" SuffixBackendHealthCheckPort = "backend.healthcheck.port"
SuffixBackendHealthCheckInterval = "backend.healthcheck.interval" SuffixBackendHealthCheckInterval = "backend.healthcheck.interval"
SuffixBackendHealthCheckHostname = "backend.healthcheck.hostname"
SuffixBackendHealthCheckHeaders = "backend.healthcheck.headers"
SuffixBackendLoadBalancer = "backend.loadbalancer" SuffixBackendLoadBalancer = "backend.loadbalancer"
SuffixBackendLoadBalancerMethod = SuffixBackendLoadBalancer + ".method" SuffixBackendLoadBalancerMethod = SuffixBackendLoadBalancer + ".method"
SuffixBackendLoadBalancerSticky = SuffixBackendLoadBalancer + ".sticky" SuffixBackendLoadBalancerSticky = SuffixBackendLoadBalancer + ".sticky"
@ -83,6 +85,8 @@ const (
TraefikBackendHealthCheckPath = Prefix + SuffixBackendHealthCheckPath TraefikBackendHealthCheckPath = Prefix + SuffixBackendHealthCheckPath
TraefikBackendHealthCheckPort = Prefix + SuffixBackendHealthCheckPort TraefikBackendHealthCheckPort = Prefix + SuffixBackendHealthCheckPort
TraefikBackendHealthCheckInterval = Prefix + SuffixBackendHealthCheckInterval TraefikBackendHealthCheckInterval = Prefix + SuffixBackendHealthCheckInterval
TraefikBackendHealthCheckHostname = Prefix + SuffixBackendHealthCheckHostname
TraefikBackendHealthCheckHeaders = Prefix + SuffixBackendHealthCheckHeaders
TraefikBackendLoadBalancer = Prefix + SuffixBackendLoadBalancer TraefikBackendLoadBalancer = Prefix + SuffixBackendLoadBalancer
TraefikBackendLoadBalancerMethod = Prefix + SuffixBackendLoadBalancerMethod TraefikBackendLoadBalancerMethod = Prefix + SuffixBackendLoadBalancerMethod
TraefikBackendLoadBalancerSticky = Prefix + SuffixBackendLoadBalancerSticky TraefikBackendLoadBalancerSticky = Prefix + SuffixBackendLoadBalancerSticky

View file

@ -236,8 +236,12 @@ func GetHealthCheck(labels map[string]string) *types.HealthCheck {
port := GetIntValue(labels, TraefikBackendHealthCheckPort, DefaultBackendHealthCheckPort) port := GetIntValue(labels, TraefikBackendHealthCheckPort, DefaultBackendHealthCheckPort)
interval := GetStringValue(labels, TraefikBackendHealthCheckInterval, "") interval := GetStringValue(labels, TraefikBackendHealthCheckInterval, "")
hostname := GetStringValue(labels, TraefikBackendHealthCheckHostname, "")
headers := GetMapValue(labels, TraefikBackendHealthCheckHeaders)
return &types.HealthCheck{ return &types.HealthCheck{
Hostname: hostname,
Headers: headers,
Path: path, Path: path,
Port: port, Port: port,
Interval: interval, Interval: interval,

View file

@ -176,6 +176,9 @@ func TestBuildConfiguration(t *testing.T) {
withLabel(label.TraefikBackendHealthCheckPath, "/health"), withLabel(label.TraefikBackendHealthCheckPath, "/health"),
withLabel(label.TraefikBackendHealthCheckPort, "880"), withLabel(label.TraefikBackendHealthCheckPort, "880"),
withLabel(label.TraefikBackendHealthCheckInterval, "6"), withLabel(label.TraefikBackendHealthCheckInterval, "6"),
withLabel(label.TraefikBackendHealthCheckHostname, "foo.com"),
withLabel(label.TraefikBackendHealthCheckHeaders, "Foo:bar || Bar:foo"),
withLabel(label.TraefikBackendLoadBalancerMethod, "drr"), withLabel(label.TraefikBackendLoadBalancerMethod, "drr"),
withLabel(label.TraefikBackendLoadBalancerSticky, "true"), withLabel(label.TraefikBackendLoadBalancerSticky, "true"),
withLabel(label.TraefikBackendLoadBalancerStickiness, "true"), withLabel(label.TraefikBackendLoadBalancerStickiness, "true"),
@ -365,6 +368,11 @@ func TestBuildConfiguration(t *testing.T) {
Path: "/health", Path: "/health",
Port: 880, Port: 880,
Interval: "6", Interval: "6",
Hostname: "foo.com",
Headers: map[string]string{
"Foo": "bar",
"Bar": "foo",
},
}, },
Buffering: &types.Buffering{ Buffering: &types.Buffering{
MaxResponseBodyBytes: 10485760, MaxResponseBodyBytes: 10485760,

View file

@ -126,6 +126,9 @@ func TestBuildConfiguration(t *testing.T) {
withLabel(label.TraefikBackendHealthCheckPath, "/health"), withLabel(label.TraefikBackendHealthCheckPath, "/health"),
withLabel(label.TraefikBackendHealthCheckPort, "880"), withLabel(label.TraefikBackendHealthCheckPort, "880"),
withLabel(label.TraefikBackendHealthCheckInterval, "6"), withLabel(label.TraefikBackendHealthCheckInterval, "6"),
withLabel(label.TraefikBackendHealthCheckHostname, "foo.com"),
withLabel(label.TraefikBackendHealthCheckHeaders, "Foo:bar || Bar:foo"),
withLabel(label.TraefikBackendLoadBalancerMethod, "drr"), withLabel(label.TraefikBackendLoadBalancerMethod, "drr"),
withLabel(label.TraefikBackendLoadBalancerStickiness, "true"), withLabel(label.TraefikBackendLoadBalancerStickiness, "true"),
withLabel(label.TraefikBackendLoadBalancerStickinessCookieName, "chocolate"), withLabel(label.TraefikBackendLoadBalancerStickinessCookieName, "chocolate"),
@ -316,6 +319,11 @@ func TestBuildConfiguration(t *testing.T) {
Path: "/health", Path: "/health",
Port: 880, Port: 880,
Interval: "6", Interval: "6",
Hostname: "foo.com",
Headers: map[string]string{
"Foo": "bar",
"Bar": "foo",
},
}, },
Buffering: &types.Buffering{ Buffering: &types.Buffering{
MaxResponseBodyBytes: 10485760, MaxResponseBodyBytes: 10485760,

View file

@ -44,6 +44,8 @@ func TestProviderBuildConfiguration(t *testing.T) {
label.TraefikBackendHealthCheckPath: "/health", label.TraefikBackendHealthCheckPath: "/health",
label.TraefikBackendHealthCheckPort: "880", label.TraefikBackendHealthCheckPort: "880",
label.TraefikBackendHealthCheckInterval: "6", label.TraefikBackendHealthCheckInterval: "6",
label.TraefikBackendHealthCheckHostname: "foo.com",
label.TraefikBackendHealthCheckHeaders: "Foo:bar || Bar:foo",
label.TraefikBackendLoadBalancerMethod: "drr", label.TraefikBackendLoadBalancerMethod: "drr",
label.TraefikBackendLoadBalancerSticky: "true", label.TraefikBackendLoadBalancerSticky: "true",
label.TraefikBackendLoadBalancerStickiness: "true", label.TraefikBackendLoadBalancerStickiness: "true",
@ -240,6 +242,11 @@ func TestProviderBuildConfiguration(t *testing.T) {
Path: "/health", Path: "/health",
Port: 880, Port: 880,
Interval: "6", Interval: "6",
Hostname: "foo.com",
Headers: map[string]string{
"Foo": "bar",
"Bar": "foo",
},
}, },
Buffering: &types.Buffering{ Buffering: &types.Buffering{
MaxResponseBodyBytes: 10485760, MaxResponseBodyBytes: 10485760,

View file

@ -1386,6 +1386,8 @@ func parseHealthCheckOptions(lb healthcheck.LoadBalancer, backend string, hc *ty
} }
return &healthcheck.Options{ return &healthcheck.Options{
Hostname: hc.Hostname,
Headers: hc.Headers,
Path: hc.Path, Path: hc.Path,
Port: hc.Port, Port: hc.Port,
Interval: interval, Interval: interval,

View file

@ -32,6 +32,13 @@
path = "{{ $healthCheck.Path }}" path = "{{ $healthCheck.Path }}"
port = {{ $healthCheck.Port }} port = {{ $healthCheck.Port }}
interval = "{{ $healthCheck.Interval }}" interval = "{{ $healthCheck.Interval }}"
hostname = "{{ $healthCheck.Hostname }}"
{{if $healthCheck.Headers }}
[backends."backend-{{ $backendName }}".healthCheck.headers]
{{range $k, $v := $healthCheck.Headers }}
{{$k}} = "{{$v}}"
{{end}}
{{end}}
{{end}} {{end}}
{{ $buffering := getBuffering $service.TraefikLabels }} {{ $buffering := getBuffering $service.TraefikLabels }}

View file

@ -33,6 +33,13 @@
path = "{{ $healthCheck.Path }}" path = "{{ $healthCheck.Path }}"
port = {{ $healthCheck.Port }} port = {{ $healthCheck.Port }}
interval = "{{ $healthCheck.Interval }}" interval = "{{ $healthCheck.Interval }}"
hostname = "{{ $healthCheck.Hostname }}"
{{if $healthCheck.Headers }}
[backends."backend-{{ $backendName }}".healthCheck.headers]
{{range $k, $v := $healthCheck.Headers }}
{{$k}} = "{{$v}}"
{{end}}
{{end}}
{{end}} {{end}}
{{ $buffering := getBuffering $backend.SegmentLabels }} {{ $buffering := getBuffering $backend.SegmentLabels }}

View file

@ -32,6 +32,13 @@
path = "{{ $healthCheck.Path }}" path = "{{ $healthCheck.Path }}"
port = {{ $healthCheck.Port }} port = {{ $healthCheck.Port }}
interval = "{{ $healthCheck.Interval }}" interval = "{{ $healthCheck.Interval }}"
hostname = "{{ $healthCheck.Hostname }}"
{{if $healthCheck.Headers }}
[backends."backend-{{ $serviceName }}".healthCheck.headers]
{{range $k, $v := $healthCheck.Headers }}
{{$k}} = "{{$v}}"
{{end}}
{{end}}
{{end}} {{end}}
{{ $buffering := getBuffering $firstInstance.TraefikLabels }} {{ $buffering := getBuffering $firstInstance.TraefikLabels }}

View file

@ -32,6 +32,13 @@
path = "{{ $healthCheck.Path }}" path = "{{ $healthCheck.Path }}"
port = {{ $healthCheck.Port }} port = {{ $healthCheck.Port }}
interval = "{{ $healthCheck.Interval }}" interval = "{{ $healthCheck.Interval }}"
hostname = "{{ $healthCheck.Hostname }}"
{{if $healthCheck.Headers }}
[backends.{{ $backendName }}.healthCheck.headers]
{{range $k, $v := $healthCheck.Headers }}
{{$k}} = "{{$v}}"
{{end}}
{{end}}
{{end}} {{end}}
{{ $buffering := getBuffering $backend }} {{ $buffering := getBuffering $backend }}

View file

@ -35,6 +35,13 @@
path = "{{ $healthCheck.Path }}" path = "{{ $healthCheck.Path }}"
port = {{ $healthCheck.Port }} port = {{ $healthCheck.Port }}
interval = "{{ $healthCheck.Interval }}" interval = "{{ $healthCheck.Interval }}"
hostname = "{{ $healthCheck.Hostname }}"
{{if $healthCheck.Headers }}
[backends.{{ $backendName }}.healthCheck.headers]
{{range $k, $v := $healthCheck.Headers }}
{{$k}} = "{{$v}}"
{{end}}
{{end}}
{{end}} {{end}}
{{ $buffering := getBuffering $app.SegmentLabels }} {{ $buffering := getBuffering $app.SegmentLabels }}

View file

@ -35,6 +35,13 @@
path = "{{ $healthCheck.Path }}" path = "{{ $healthCheck.Path }}"
port = {{ $healthCheck.Port }} port = {{ $healthCheck.Port }}
interval = "{{ $healthCheck.Interval }}" interval = "{{ $healthCheck.Interval }}"
hostname = "{{ $healthCheck.Hostname }}"
{{if $healthCheck.Headers }}
[backends."backend-{{ $backendName }}".healthCheck.headers]
{{range $k, $v := $healthCheck.Headers }}
{{$k}} = "{{$v}}"
{{end}}
{{end}}
{{end}} {{end}}
{{ $buffering := getBuffering $app.TraefikLabels }} {{ $buffering := getBuffering $app.TraefikLabels }}

View file

@ -34,6 +34,13 @@
path = "{{ $healthCheck.Path }}" path = "{{ $healthCheck.Path }}"
port = {{ $healthCheck.Port }} port = {{ $healthCheck.Port }}
interval = "{{ $healthCheck.Interval }}" interval = "{{ $healthCheck.Interval }}"
hostname = "{{ $healthCheck.Hostname }}"
{{if $healthCheck.Headers }}
[backends."backend-{{ $backendName }}".healthCheck.headers]
{{range $k, $v := $healthCheck.Headers }}
{{$k}} = "{{$v}}"
{{end}}
{{end}}
{{end}} {{end}}
{{ $buffering := getBuffering $backend.SegmentLabels }} {{ $buffering := getBuffering $backend.SegmentLabels }}

View file

@ -69,9 +69,11 @@ type WhiteList struct {
// HealthCheck holds HealthCheck configuration // HealthCheck holds HealthCheck configuration
type HealthCheck struct { type HealthCheck struct {
Path string `json:"path,omitempty"` Headers map[string]string `json:"headers,omitempty"`
Port int `json:"port,omitempty"` Hostname string `json:"hostname,omitempty"`
Interval string `json:"interval,omitempty"` Path string `json:"path,omitempty"`
Port int `json:"port,omitempty"`
Interval string `json:"interval,omitempty"`
} }
// Server holds server configuration. // Server holds server configuration.