Allow overriding port for backend healthchecks

This commit is contained in:
Brian Akins 2017-05-10 14:28:57 -04:00 committed by Ludovic Fernandez
parent c7281df230
commit 13e8a875cf
3 changed files with 114 additions and 13 deletions

View file

@ -296,8 +296,9 @@ A health check can be configured in order to remove a backend from LB rotation
as long as it keeps returning HTTP status codes other than 200 OK to HTTP GET as long as it keeps returning HTTP status codes other than 200 OK to HTTP GET
requests periodically carried out by Traefik. The check is defined by a path requests periodically carried out by Traefik. The check is defined by a path
appended to the backend URL and an interval (given in a format understood by [time.ParseDuration](https://golang.org/pkg/time/#ParseDuration)) specifying how appended to the backend URL and an interval (given in a format understood by [time.ParseDuration](https://golang.org/pkg/time/#ParseDuration)) specifying how
often the health check should be executed (the default being 30 seconds). Each often the health check should be executed (the default being 30 seconds).
backend must respond to the health check within 5 seconds. Each backend must respond to the health check within 5 seconds.
By default, the port of the backend server is used, however, this may be overridden.
A recovering backend returning 200 OK responses again is being returned to the A recovering backend returning 200 OK responses again is being returned to the
LB rotation pool. LB rotation pool.
@ -311,6 +312,16 @@ For example:
interval = "10s" interval = "10s"
``` ```
To use a different port for the healthcheck:
```toml
[backends]
[backends.backend1]
[backends.backend1.healthcheck]
path = "/health"
interval = "10s"
port = 8080
```
## 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).
@ -439,4 +450,3 @@ Note that each command is described at the beginning of the help section:
```bash ```bash
$ traefik --help $ traefik --help
``` ```

View file

@ -3,8 +3,10 @@ package healthcheck
import ( import (
"context" "context"
"fmt" "fmt"
"net"
"net/http" "net/http"
"net/url" "net/url"
"strconv"
"sync" "sync"
"time" "time"
@ -27,6 +29,7 @@ func GetHealthCheck() *HealthCheck {
// Options are the public health check options. // Options are the public health check options.
type Options struct { type Options struct {
Path string Path string
Port int
Interval time.Duration Interval time.Duration
LB LoadBalancer LB LoadBalancer
} }
@ -127,11 +130,32 @@ func checkBackend(currentBackend *BackendHealthCheck) {
} }
} }
func (backend *BackendHealthCheck) newRequest(serverURL *url.URL) (*http.Request, error) {
if backend.Options.Port == 0 {
return http.NewRequest("GET", serverURL.String()+backend.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.Options.Port))
u.Path = u.Path + backend.Path
return http.NewRequest("GET", u.String(), nil)
}
func checkHealth(serverURL *url.URL, backend *BackendHealthCheck) bool { func checkHealth(serverURL *url.URL, backend *BackendHealthCheck) bool {
client := http.Client{ client := http.Client{
Timeout: backend.requestTimeout, Timeout: backend.requestTimeout,
} }
resp, err := client.Get(serverURL.String() + backend.Path) req, err := backend.newRequest(serverURL)
if err != nil {
log.Errorf("Failed to create HTTP request [%s] for healthcheck: %s", serverURL, err)
return false
}
resp, err := client.Do(req)
if err == nil { if err == nil {
defer resp.Body.Close() defer resp.Body.Close()
} }

View file

@ -193,6 +193,73 @@ func TestSetBackendsConfiguration(t *testing.T) {
} }
} }
func TestNewRequest(t *testing.T) {
tests := []struct {
desc string
host string
port int
path string
expected string
}{
{
desc: "no port override",
host: "backend1:80",
port: 0,
path: "/test",
expected: "http://backend1:80/test",
},
{
desc: "port override",
host: "backend2:80",
port: 8080,
path: "/test",
expected: "http://backend2:8080/test",
},
{
desc: "no port override with no port in host",
host: "backend1",
port: 0,
path: "/health",
expected: "http://backend1/health",
},
{
desc: "port override with no port in host",
host: "backend2",
port: 8080,
path: "/health",
expected: "http://backend2:8080/health",
},
}
for _, test := range tests {
test := test
t.Run(test.desc, func(t *testing.T) {
t.Parallel()
backend := NewBackendHealthCheck(
Options{
Path: test.path,
Port: test.port,
})
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)
}
})
}
}
func MustParseURL(rawurl string) *url.URL { func MustParseURL(rawurl string) *url.URL {
u, err := url.Parse(rawurl) u, err := url.Parse(rawurl)
if err != nil { if err != nil {