diff --git a/docs/content/operations/ping.md b/docs/content/operations/ping.md index f44cdc691..95b2266f1 100644 --- a/docs/content/operations/ping.md +++ b/docs/content/operations/ping.md @@ -81,3 +81,28 @@ ping: ```bash tab="CLI" --ping.manualrouting=true ``` + +### `terminatingStatusCode` + +_Optional, Default=503_ + +During the period in which Traefik is gracefully shutting down, the ping handler +returns a 503 status code by default. If Traefik is behind e.g. a load-balancer +doing health checks (such as the Kubernetes LivenessProbe), another code might +be expected as the signal for graceful termination. In which case, the +terminatingStatusCode can be used to set the code returned by the ping +handler during termination. + +```toml tab="File (TOML)" +[ping] + terminatingStatusCode = 204 +``` + +```yaml tab="File (YAML)" +ping: + terminatingStatusCode: 204 +``` + +```bash tab="CLI" +--ping.terminatingStatusCode=204 +``` diff --git a/docs/content/reference/static-configuration/cli-ref.md b/docs/content/reference/static-configuration/cli-ref.md index aed610b9a..7afe56d75 100644 --- a/docs/content/reference/static-configuration/cli-ref.md +++ b/docs/content/reference/static-configuration/cli-ref.md @@ -279,6 +279,9 @@ EntryPoint (Default: ```traefik```) `--ping.manualrouting`: Manual routing (Default: ```false```) +`--ping.terminatingstatuscode`: +Terminating status code (Default: ```503```) + `--providers.consul`: Enable Consul backend with default settings. (Default: ```false```) diff --git a/docs/content/reference/static-configuration/env-ref.md b/docs/content/reference/static-configuration/env-ref.md index 2228bec10..bafef49ad 100644 --- a/docs/content/reference/static-configuration/env-ref.md +++ b/docs/content/reference/static-configuration/env-ref.md @@ -279,6 +279,9 @@ EntryPoint (Default: ```traefik```) `TRAEFIK_PING_MANUALROUTING`: Manual routing (Default: ```false```) +`TRAEFIK_PING_TERMINATINGSTATUSCODE`: +Terminating status code (Default: ```503```) + `TRAEFIK_PROVIDERS_CONSUL`: Enable Consul backend with default settings. (Default: ```false```) diff --git a/integration/fixtures/custom_ping_termination_status_code.toml b/integration/fixtures/custom_ping_termination_status_code.toml new file mode 100644 index 000000000..f234963ff --- /dev/null +++ b/integration/fixtures/custom_ping_termination_status_code.toml @@ -0,0 +1,16 @@ +[global] + checkNewVersion = false + sendAnonymousUsage = false + +[log] + level = "DEBUG" + +[entryPoints] + + [entryPoints.traefik] + address = ":8001" + [entryPoints.traefik.transport.lifeCycle] + requestAcceptGraceTimeout = "10s" + +[ping] + terminatingStatusCode = 204 \ No newline at end of file diff --git a/integration/simple_test.go b/integration/simple_test.go index 75f692faf..943ae9843 100644 --- a/integration/simple_test.go +++ b/integration/simple_test.go @@ -161,6 +161,35 @@ func (s *SimpleSuite) TestRequestAcceptGraceTimeout(c *check.C) { } } +func (s *SimpleSuite) TestCustomPingTerminationStatusCode(c *check.C) { + file := s.adaptFile(c, "fixtures/custom_ping_termination_status_code.toml", struct{}{}) + defer os.Remove(file) + cmd, display := s.traefikCmd(withConfigFile(file)) + defer display(c) + + err := cmd.Start() + c.Assert(err, checker.IsNil) + defer cmd.Process.Kill() + + // Wait for Traefik to turn ready. + err = try.GetRequest("http://127.0.0.1:8001/", 2*time.Second, try.StatusCodeIs(http.StatusNotFound)) + c.Assert(err, checker.IsNil) + + // Check that /ping endpoint is responding with 200. + err = try.GetRequest("http://127.0.0.1:8001/ping", 3*time.Second, try.StatusCodeIs(http.StatusOK)) + c.Assert(err, checker.IsNil) + + // Send SIGTERM to Traefik. + proc, err := os.FindProcess(cmd.Process.Pid) + c.Assert(err, checker.IsNil) + err = proc.Signal(syscall.SIGTERM) + c.Assert(err, checker.IsNil) + + // ping endpoint should now return a Service Unavailable. + err = try.GetRequest("http://127.0.0.1:8001/ping", 2*time.Second, try.StatusCodeIs(http.StatusNoContent)) + c.Assert(err, checker.IsNil) +} + func (s *SimpleSuite) TestStatsWithMultipleEntryPoint(c *check.C) { c.Skip("Stats is missing") s.createComposeProject(c, "stats") diff --git a/pkg/ping/ping.go b/pkg/ping/ping.go index 8cb804398..ecf56a0df 100644 --- a/pkg/ping/ping.go +++ b/pkg/ping/ping.go @@ -8,14 +8,16 @@ import ( // Handler expose ping routes. type Handler struct { - EntryPoint string `description:"EntryPoint" export:"true" json:"entryPoint,omitempty" toml:"entryPoint,omitempty" yaml:"entryPoint,omitempty"` - ManualRouting bool `description:"Manual routing" json:"manualRouting,omitempty" toml:"manualRouting,omitempty" yaml:"manualRouting,omitempty"` - terminating bool + EntryPoint string `description:"EntryPoint" export:"true" json:"entryPoint,omitempty" toml:"entryPoint,omitempty" yaml:"entryPoint,omitempty"` + ManualRouting bool `description:"Manual routing" json:"manualRouting,omitempty" toml:"manualRouting,omitempty" yaml:"manualRouting,omitempty"` + TerminatingStatusCode int `description:"Terminating status code" json:"terminatingStatusCode,omitempty" toml:"terminatingStatusCode,omitempty" yaml:"terminatingStatusCode,omitempty"` + terminating bool } // SetDefaults sets the default values. func (h *Handler) SetDefaults() { h.EntryPoint = "traefik" + h.TerminatingStatusCode = http.StatusServiceUnavailable } // WithContext causes the ping endpoint to serve non 200 responses. @@ -29,7 +31,7 @@ func (h *Handler) WithContext(ctx context.Context) { func (h *Handler) ServeHTTP(response http.ResponseWriter, request *http.Request) { statusCode := http.StatusOK if h.terminating { - statusCode = http.StatusServiceUnavailable + statusCode = h.TerminatingStatusCode } response.WriteHeader(statusCode) fmt.Fprint(response, http.StatusText(statusCode))