From efbbff671c004610f076c5d37817595a0d5cec3c Mon Sep 17 00:00:00 2001 From: Julien Salleyron Date: Mon, 6 Feb 2017 22:59:50 +0100 Subject: [PATCH 1/4] Add healthcheck interval --- healthcheck/healthcheck.go | 7 ++++--- server.go | 20 ++++++++++++++++++-- types/types.go | 3 ++- 3 files changed, 24 insertions(+), 6 deletions(-) diff --git a/healthcheck/healthcheck.go b/healthcheck/healthcheck.go index cbae33cc4..30b4af495 100644 --- a/healthcheck/healthcheck.go +++ b/healthcheck/healthcheck.go @@ -26,6 +26,7 @@ func GetHealthCheck() *HealthCheck { // BackendHealthCheck HealthCheck configuration for a backend type BackendHealthCheck struct { URL string + Interval time.Duration DisabledURLs []*url.URL lb loadBalancer } @@ -49,8 +50,8 @@ func newHealthCheck() *HealthCheck { } // NewBackendHealthCheck Instantiate a new BackendHealthCheck -func NewBackendHealthCheck(URL string, lb loadBalancer) *BackendHealthCheck { - return &BackendHealthCheck{URL, nil, lb} +func NewBackendHealthCheck(URL string, interval time.Duration, lb loadBalancer) *BackendHealthCheck { + return &BackendHealthCheck{URL, interval, nil, lb} } //SetBackendsConfiguration set backends configuration @@ -70,7 +71,7 @@ func (hc *HealthCheck) execute(ctx context.Context) { currentBackendID := backendID safe.Go(func() { for { - ticker := time.NewTicker(time.Second * 30) + ticker := time.NewTicker(currentBackend.Interval) select { case <-ctx.Done(): log.Debugf("Stopping all current Healthcheck goroutines") diff --git a/server.go b/server.go index a2d3a406f..457093eb2 100644 --- a/server.go +++ b/server.go @@ -658,7 +658,15 @@ func (server *Server) loadConfig(configurations configs, globalConfiguration Glo continue frontend } if configuration.Backends[frontend.Backend].HealthCheck != nil { - backendsHealthcheck[frontend.Backend] = healthcheck.NewBackendHealthCheck(configuration.Backends[frontend.Backend].HealthCheck.URL, rebalancer) + var interval time.Duration + if configuration.Backends[frontend.Backend].HealthCheck.Interval != "" { + interval, err = time.ParseDuration(configuration.Backends[frontend.Backend].HealthCheck.Interval) + if err != nil { + log.Errorf("Wrong healthcheck interval: %s", err) + interval = time.Second * 30 + } + } + backendsHealthcheck[frontend.Backend] = healthcheck.NewBackendHealthCheck(configuration.Backends[frontend.Backend].HealthCheck.URL, interval, rebalancer) } } case types.Wrr: @@ -684,7 +692,15 @@ func (server *Server) loadConfig(configurations configs, globalConfiguration Glo } } if configuration.Backends[frontend.Backend].HealthCheck != nil { - backendsHealthcheck[frontend.Backend] = healthcheck.NewBackendHealthCheck(configuration.Backends[frontend.Backend].HealthCheck.URL, rr) + var interval time.Duration + if configuration.Backends[frontend.Backend].HealthCheck.Interval != "" { + interval, err = time.ParseDuration(configuration.Backends[frontend.Backend].HealthCheck.Interval) + if err != nil { + log.Errorf("Wrong healthcheck interval: %s", err) + interval = time.Second * 30 + } + } + backendsHealthcheck[frontend.Backend] = healthcheck.NewBackendHealthCheck(configuration.Backends[frontend.Backend].HealthCheck.URL, interval, rr) } } maxConns := configuration.Backends[frontend.Backend].MaxConn diff --git a/types/types.go b/types/types.go index 630263164..308a71e25 100644 --- a/types/types.go +++ b/types/types.go @@ -39,7 +39,8 @@ type CircuitBreaker struct { // HealthCheck holds HealthCheck configuration type HealthCheck struct { - URL string `json:"url,omitempty"` + URL string `json:"url,omitempty"` + Interval string `json:"interval,omitempty"` } // Server holds server configuration. From 04a25b841f3fdda0c503c029419952f4882e2a14 Mon Sep 17 00:00:00 2001 From: Julien Salleyron Date: Mon, 6 Feb 2017 23:00:54 +0100 Subject: [PATCH 2/4] Add some integration test --- integration/fixtures/healthcheck/simple.toml | 27 ++++++ integration/healthcheck_test.go | 91 +++++++++++++++++++ integration/resources/compose/healthcheck.yml | 5 + 3 files changed, 123 insertions(+) create mode 100644 integration/fixtures/healthcheck/simple.toml create mode 100644 integration/healthcheck_test.go create mode 100644 integration/resources/compose/healthcheck.yml diff --git a/integration/fixtures/healthcheck/simple.toml b/integration/fixtures/healthcheck/simple.toml new file mode 100644 index 000000000..c3ebb33e3 --- /dev/null +++ b/integration/fixtures/healthcheck/simple.toml @@ -0,0 +1,27 @@ +defaultEntryPoints = ["http"] + +logLevel = "DEBUG" + +[entryPoints] + [entryPoints.http] + address = ":8000" + +[web] + address = ":8080" + +[file] +[backends] + [backends.backend1] + [backends.backend1.healthcheck] + url = "/health" + interval = "1s" + [backends.backend1.servers.server1] + url = "http://{{.Server1}}:80" + [backends.backend1.servers.server2] + url = "http://{{.Server2}}:80" + +[frontends] + [frontends.frontend1] + backend = "backend1" + [frontends.frontend1.routes.test_1] + rule = "Host:test.localhost" diff --git a/integration/healthcheck_test.go b/integration/healthcheck_test.go new file mode 100644 index 000000000..532a431bd --- /dev/null +++ b/integration/healthcheck_test.go @@ -0,0 +1,91 @@ +package main + +import ( + "bytes" + "errors" + "io/ioutil" + "net/http" + "os" + "os/exec" + "strings" + "time" + + "github.com/containous/traefik/integration/utils" + "github.com/go-check/check" + + checker "github.com/vdemeester/shakers" +) + +// HealchCheck test suites (using libcompose) +type HealchCheckSuite struct{ BaseSuite } + +func (s *HealchCheckSuite) SetUpSuite(c *check.C) { + s.createComposeProject(c, "healthcheck") + s.composeProject.Start(c) + +} + +func (s *HealchCheckSuite) TestSimpleConfiguration(c *check.C) { + + whoami1Host := s.composeProject.Container(c, "whoami1").NetworkSettings.IPAddress + whoami2Host := s.composeProject.Container(c, "whoami2").NetworkSettings.IPAddress + + file := s.adaptFile(c, "fixtures/healthcheck/simple.toml", struct { + Server1 string + Server2 string + }{whoami1Host, whoami2Host}) + defer os.Remove(file) + cmd := exec.Command(traefikBinary, "--configFile="+file) + + err := cmd.Start() + c.Assert(err, checker.IsNil) + defer cmd.Process.Kill() + + // wait for traefik + err = utils.TryRequest("http://127.0.0.1:8080/api/providers", 60*time.Second, func(res *http.Response) error { + body, err := ioutil.ReadAll(res.Body) + if err != nil { + return err + } + if !strings.Contains(string(body), "Host:test.localhost") { + return errors.New("Incorrect traefik config: " + string(body)) + } + return nil + }) + c.Assert(err, checker.IsNil) + + client := &http.Client{} + req, err := http.NewRequest("GET", "http://127.0.0.1:8000/health", nil) + c.Assert(err, checker.IsNil) + req.Host = "test.localhost" + + resp, err := client.Do(req) + c.Assert(err, checker.IsNil) + c.Assert(resp.StatusCode, checker.Equals, 200) + + resp, err = client.Do(req) + c.Assert(err, checker.IsNil) + c.Assert(resp.StatusCode, checker.Equals, 200) + + healthReq, err := http.NewRequest("POST", "http://"+whoami1Host+"/health", bytes.NewBuffer([]byte("500"))) + c.Assert(err, checker.IsNil) + _, err = client.Do(healthReq) + c.Assert(err, checker.IsNil) + + time.Sleep(time.Second * 3) + + resp, err = client.Do(req) + c.Assert(err, checker.IsNil) + c.Assert(resp.StatusCode, checker.Equals, 200) + + resp, err = client.Do(req) + c.Assert(err, checker.IsNil) + c.Assert(resp.StatusCode, checker.Equals, 200) + + // TODO validate : run on 80 + resp, err = http.Get("http://127.0.0.1:8000/") + + // Expected a 404 as we did not configure anything + c.Assert(err, checker.IsNil) + c.Assert(resp.StatusCode, checker.Equals, 404) +} diff --git a/integration/resources/compose/healthcheck.yml b/integration/resources/compose/healthcheck.yml new file mode 100644 index 000000000..e2b6f7083 --- /dev/null +++ b/integration/resources/compose/healthcheck.yml @@ -0,0 +1,5 @@ +whoami1: + image: juliens/whoami + +whoami2: + image: juliens/whoami From 44fa364cdd40aea874fce3d09435b00efc4dd1b7 Mon Sep 17 00:00:00 2001 From: Julien Salleyron Date: Mon, 6 Feb 2017 23:08:26 +0100 Subject: [PATCH 3/4] Add doc --- docs/basics.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/docs/basics.md b/docs/basics.md index 79042790e..58a74ee51 100644 --- a/docs/basics.md +++ b/docs/basics.md @@ -235,6 +235,20 @@ For example: [backends.backend1.loadbalancer] sticky = true ``` + +Healthcheck URL can be configured with a relative URL for `healthcheck.URL`. +Interval between healthcheck can be configured by using `healthcheck.interval` +(default: 30s) + +For example: +```toml +[backends] + [backends.backend1] + [backends.backend1.healthcheck] + URL = "/health" + interval = "10s" +``` + ## 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). From 05f2449d841d45b7f946a7ecafaf0118c864eff5 Mon Sep 17 00:00:00 2001 From: Julien Salleyron Date: Mon, 6 Feb 2017 23:15:38 +0100 Subject: [PATCH 4/4] Wrong tests docker images --- integration/resources/compose/healthcheck.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/integration/resources/compose/healthcheck.yml b/integration/resources/compose/healthcheck.yml index e2b6f7083..cbe13d553 100644 --- a/integration/resources/compose/healthcheck.yml +++ b/integration/resources/compose/healthcheck.yml @@ -1,5 +1,5 @@ whoami1: - image: juliens/whoami + image: emilevauge/whoami whoami2: - image: juliens/whoami + image: emilevauge/whoami