From af749f1864947f39e5f9fca8342d807d7f5f1ff5 Mon Sep 17 00:00:00 2001 From: Douglas De Toni Machado Date: Mon, 8 Aug 2022 10:22:07 -0300 Subject: [PATCH] Add a method option to the service Health Check --- .../dynamic-configuration/docker-labels.yml | 2 +- .../reference/dynamic-configuration/file.toml | 1 + .../reference/dynamic-configuration/file.yaml | 1 + .../reference/dynamic-configuration/kv-ref.md | 1 + .../marathon-labels.json | 2 +- .../routing/providers/consul-catalog.md | 8 ++++++++ docs/content/routing/providers/docker.md | 8 ++++++++ docs/content/routing/providers/ecs.md | 8 ++++++++ docs/content/routing/providers/kv.md | 8 ++++++++ docs/content/routing/providers/marathon.md | 8 ++++++++ docs/content/routing/providers/rancher.md | 8 ++++++++ docs/content/routing/services/index.md | 1 + pkg/config/dynamic/http_config.go | 1 + pkg/config/label/label_test.go | 8 ++++++++ pkg/healthcheck/healthcheck.go | 15 ++++++++++---- pkg/healthcheck/healthcheck_test.go | 20 +++++++++++++++++-- pkg/server/service/service.go | 1 + 17 files changed, 93 insertions(+), 8 deletions(-) diff --git a/docs/content/reference/dynamic-configuration/docker-labels.yml b/docs/content/reference/dynamic-configuration/docker-labels.yml index 8ae452c895..6716ef8e52 100644 --- a/docs/content/reference/dynamic-configuration/docker-labels.yml +++ b/docs/content/reference/dynamic-configuration/docker-labels.yml @@ -151,10 +151,10 @@ - "traefik.http.services.service01.loadbalancer.healthcheck.hostname=foobar" - "traefik.http.services.service01.loadbalancer.healthcheck.interval=foobar" - "traefik.http.services.service01.loadbalancer.healthcheck.path=foobar" +- "traefik.http.services.service01.loadbalancer.healthcheck.method=foobar" - "traefik.http.services.service01.loadbalancer.healthcheck.port=42" - "traefik.http.services.service01.loadbalancer.healthcheck.scheme=foobar" - "traefik.http.services.service01.loadbalancer.healthcheck.timeout=foobar" -- "traefik.http.services.service01.loadbalancer.healthcheck.followredirects=true" - "traefik.http.services.service01.loadbalancer.passhostheader=true" - "traefik.http.services.service01.loadbalancer.responseforwarding.flushinterval=foobar" - "traefik.http.services.service01.loadbalancer.serverstransport=foobar" diff --git a/docs/content/reference/dynamic-configuration/file.toml b/docs/content/reference/dynamic-configuration/file.toml index 0b640713eb..a7e23da179 100644 --- a/docs/content/reference/dynamic-configuration/file.toml +++ b/docs/content/reference/dynamic-configuration/file.toml @@ -54,6 +54,7 @@ [http.services.Service01.loadBalancer.healthCheck] scheme = "foobar" path = "foobar" + method = "foobar" port = 42 interval = "foobar" timeout = "foobar" diff --git a/docs/content/reference/dynamic-configuration/file.yaml b/docs/content/reference/dynamic-configuration/file.yaml index ddbade43ef..7d95a4e8f2 100644 --- a/docs/content/reference/dynamic-configuration/file.yaml +++ b/docs/content/reference/dynamic-configuration/file.yaml @@ -59,6 +59,7 @@ http: healthCheck: scheme: foobar path: foobar + method: foobar port: 42 interval: foobar timeout: foobar diff --git a/docs/content/reference/dynamic-configuration/kv-ref.md b/docs/content/reference/dynamic-configuration/kv-ref.md index df3169d223..7927f2687e 100644 --- a/docs/content/reference/dynamic-configuration/kv-ref.md +++ b/docs/content/reference/dynamic-configuration/kv-ref.md @@ -207,6 +207,7 @@ | `traefik/http/services/Service01/loadBalancer/healthCheck/headers/name1` | `foobar` | | `traefik/http/services/Service01/loadBalancer/healthCheck/hostname` | `foobar` | | `traefik/http/services/Service01/loadBalancer/healthCheck/interval` | `foobar` | +| `traefik/http/services/Service01/loadBalancer/healthCheck/method` | `foobar` | | `traefik/http/services/Service01/loadBalancer/healthCheck/path` | `foobar` | | `traefik/http/services/Service01/loadBalancer/healthCheck/port` | `42` | | `traefik/http/services/Service01/loadBalancer/healthCheck/scheme` | `foobar` | diff --git a/docs/content/reference/dynamic-configuration/marathon-labels.json b/docs/content/reference/dynamic-configuration/marathon-labels.json index b78ca57b50..7eba469894 100644 --- a/docs/content/reference/dynamic-configuration/marathon-labels.json +++ b/docs/content/reference/dynamic-configuration/marathon-labels.json @@ -151,10 +151,10 @@ "traefik.http.services.service01.loadbalancer.healthcheck.hostname": "foobar", "traefik.http.services.service01.loadbalancer.healthcheck.interval": "foobar", "traefik.http.services.service01.loadbalancer.healthcheck.path": "foobar", +"traefik.http.services.service01.loadbalancer.healthcheck.method": "foobar", "traefik.http.services.service01.loadbalancer.healthcheck.port": "42", "traefik.http.services.service01.loadbalancer.healthcheck.scheme": "foobar", "traefik.http.services.service01.loadbalancer.healthcheck.timeout": "foobar", -"traefik.http.services.service01.loadbalancer.healthcheck.followredirects": "true", "traefik.http.services.service01.loadbalancer.passhostheader": "true", "traefik.http.services.service01.loadbalancer.responseforwarding.flushinterval": "foobar", "traefik.http.services.service01.loadbalancer.serverstransport": "foobar", diff --git a/docs/content/routing/providers/consul-catalog.md b/docs/content/routing/providers/consul-catalog.md index e1d5b1c65e..16e9f531b5 100644 --- a/docs/content/routing/providers/consul-catalog.md +++ b/docs/content/routing/providers/consul-catalog.md @@ -185,6 +185,14 @@ you'd add the tag `traefik.http.services.{name-of-your-choice}.loadbalancer.pass traefik.http.services.myservice.loadbalancer.healthcheck.path=/foo ``` +??? info "`traefik.http.services..loadbalancer.healthcheck.method`" + + See [health check](../services/index.md#health-check) for more information. + + ```yaml + traefik.http.services.myservice.loadbalancer.healthcheck.method=foobar + ``` + ??? info "`traefik.http.services..loadbalancer.healthcheck.port`" See [health check](../services/index.md#health-check) for more information. diff --git a/docs/content/routing/providers/docker.md b/docs/content/routing/providers/docker.md index 5b53ba9245..942a2db250 100644 --- a/docs/content/routing/providers/docker.md +++ b/docs/content/routing/providers/docker.md @@ -339,6 +339,14 @@ you'd add the label `traefik.http.services..loadbalancer.pa - "traefik.http.services.myservice.loadbalancer.healthcheck.path=/foo" ``` +??? info "`traefik.http.services..loadbalancer.healthcheck.method`" + + See [health check](../services/index.md#health-check) for more information. + + ```yaml + - "traefik.http.services.myservice.loadbalancer.healthcheck.method=foobar" + ``` + ??? info "`traefik.http.services..loadbalancer.healthcheck.port`" See [health check](../services/index.md#health-check) for more information. diff --git a/docs/content/routing/providers/ecs.md b/docs/content/routing/providers/ecs.md index ac547fedd5..0dad427814 100644 --- a/docs/content/routing/providers/ecs.md +++ b/docs/content/routing/providers/ecs.md @@ -187,6 +187,14 @@ you'd add the label `traefik.http.services.{name-of-your-choice}.loadbalancer.pa traefik.http.services.myservice.loadbalancer.healthcheck.path=/foo ``` +??? info "`traefik.http.services..loadbalancer.healthcheck.method`" + + See [health check](../services/index.md#health-check) for more information. + + ```yaml + traefik.http.services.myservice.loadbalancer.healthcheck.method=foobar + ``` + ??? info "`traefik.http.services..loadbalancer.healthcheck.port`" See [health check](../services/index.md#health-check) for more information. diff --git a/docs/content/routing/providers/kv.md b/docs/content/routing/providers/kv.md index ccdd4a44e1..8771c4cdb1 100644 --- a/docs/content/routing/providers/kv.md +++ b/docs/content/routing/providers/kv.md @@ -164,6 +164,14 @@ A Story of key & values |-----------------------------------------------------------------|--------| | `traefik/http/services/myservice/loadbalancer/healthcheck/path` | `/foo` | +??? info "`traefik/http/services//loadbalancer/healthcheck/method`" + + See [health check](../services/index.md#health-check) for more information. + + | Key (Path) | Value | + |-------------------------------------------------------------------|----------| + | `traefik/http/services/myservice/loadbalancer/healthcheck/method` | `foobar` | + ??? info "`traefik/http/services//loadbalancer/healthcheck/port`" See [health check](../services/index.md#health-check) for more information. diff --git a/docs/content/routing/providers/marathon.md b/docs/content/routing/providers/marathon.md index f201cd750c..7c215f1b67 100644 --- a/docs/content/routing/providers/marathon.md +++ b/docs/content/routing/providers/marathon.md @@ -214,6 +214,14 @@ For example, to change the passHostHeader behavior, you'd add the label `"traefi "traefik.http.services.myservice.loadbalancer.healthcheck.path": "/foo" ``` +??? info "`traefik.http.services..loadbalancer.healthcheck.method`" + + See [health check](../services/index.md#health-check) for more information. + + ```json + "traefik.http.services.myservice.loadbalancer.healthcheck.method": "foobar" + ``` + ??? info "`traefik.http.services..loadbalancer.healthcheck.port`" See [health check](../services/index.md#health-check) for more information. diff --git a/docs/content/routing/providers/rancher.md b/docs/content/routing/providers/rancher.md index 6a07398076..fef132c4b4 100644 --- a/docs/content/routing/providers/rancher.md +++ b/docs/content/routing/providers/rancher.md @@ -220,6 +220,14 @@ you'd add the label `traefik.http.services.{name-of-your-choice}.loadbalancer.pa - "traefik.http.services.myservice.loadbalancer.healthcheck.path=/foo" ``` +??? info "`traefik.http.services..loadbalancer.healthcheck.method`" + + See [health check](../services/index.md#health-check) for more information. + + ```yaml + - "traefik.http.services.myservice.loadbalancer.healthcheck.method=foobar" + ``` + ??? info "`traefik.http.services..loadbalancer.healthcheck.port`" See [health check](../services/index.md#health-check) for more information. diff --git a/docs/content/routing/services/index.md b/docs/content/routing/services/index.md index ca86b048c9..316d509996 100644 --- a/docs/content/routing/services/index.md +++ b/docs/content/routing/services/index.md @@ -330,6 +330,7 @@ Below are the available options for the health check mechanism: - `timeout` (default: 5s), defines the maximum duration Traefik will wait for a health check request before considering the server unhealthy. - `headers` (optional), defines custom headers to be sent to the health check endpoint. - `followRedirects` (default: true), defines whether redirects should be followed during the health check calls. +- `method` (default: GET), defines the HTTP method that will be used while connecting to the endpoint. !!! info "Interval & Timeout Format" diff --git a/pkg/config/dynamic/http_config.go b/pkg/config/dynamic/http_config.go index 7cf4f64ff0..21e1ced7f0 100644 --- a/pkg/config/dynamic/http_config.go +++ b/pkg/config/dynamic/http_config.go @@ -214,6 +214,7 @@ func (s *Server) SetDefaults() { type ServerHealthCheck struct { Scheme string `json:"scheme,omitempty" toml:"scheme,omitempty" yaml:"scheme,omitempty" export:"true"` Path string `json:"path,omitempty" toml:"path,omitempty" yaml:"path,omitempty" export:"true"` + Method string `json:"method,omitempty" toml:"method,omitempty" yaml:"method,omitempty" export:"true"` Port int `json:"port,omitempty" toml:"port,omitempty,omitzero" yaml:"port,omitempty" export:"true"` // FIXME change string to ptypes.Duration Interval string `json:"interval,omitempty" toml:"interval,omitempty" yaml:"interval,omitempty" export:"true"` diff --git a/pkg/config/label/label_test.go b/pkg/config/label/label_test.go index 4cda9c1777..04bce464ee 100644 --- a/pkg/config/label/label_test.go +++ b/pkg/config/label/label_test.go @@ -150,6 +150,7 @@ func TestDecodeConfiguration(t *testing.T) { "traefik.http.services.Service0.loadbalancer.healthcheck.hostname": "foobar", "traefik.http.services.Service0.loadbalancer.healthcheck.interval": "foobar", "traefik.http.services.Service0.loadbalancer.healthcheck.path": "foobar", + "traefik.http.services.Service0.loadbalancer.healthcheck.method": "foobar", "traefik.http.services.Service0.loadbalancer.healthcheck.port": "42", "traefik.http.services.Service0.loadbalancer.healthcheck.scheme": "foobar", "traefik.http.services.Service0.loadbalancer.healthcheck.timeout": "foobar", @@ -165,6 +166,7 @@ func TestDecodeConfiguration(t *testing.T) { "traefik.http.services.Service1.loadbalancer.healthcheck.hostname": "foobar", "traefik.http.services.Service1.loadbalancer.healthcheck.interval": "foobar", "traefik.http.services.Service1.loadbalancer.healthcheck.path": "foobar", + "traefik.http.services.Service1.loadbalancer.healthcheck.method": "foobar", "traefik.http.services.Service1.loadbalancer.healthcheck.port": "42", "traefik.http.services.Service1.loadbalancer.healthcheck.scheme": "foobar", "traefik.http.services.Service1.loadbalancer.healthcheck.timeout": "foobar", @@ -649,6 +651,7 @@ func TestDecodeConfiguration(t *testing.T) { HealthCheck: &dynamic.ServerHealthCheck{ Scheme: "foobar", Path: "foobar", + Method: "foobar", Port: 42, Interval: "foobar", Timeout: "foobar", @@ -676,6 +679,7 @@ func TestDecodeConfiguration(t *testing.T) { HealthCheck: &dynamic.ServerHealthCheck{ Scheme: "foobar", Path: "foobar", + Method: "foobar", Port: 42, Interval: "foobar", Timeout: "foobar", @@ -1138,6 +1142,7 @@ func TestEncodeConfiguration(t *testing.T) { HealthCheck: &dynamic.ServerHealthCheck{ Scheme: "foobar", Path: "foobar", + Method: "foobar", Port: 42, Interval: "foobar", Timeout: "foobar", @@ -1164,6 +1169,7 @@ func TestEncodeConfiguration(t *testing.T) { HealthCheck: &dynamic.ServerHealthCheck{ Scheme: "foobar", Path: "foobar", + Method: "foobar", Port: 42, Interval: "foobar", Timeout: "foobar", @@ -1324,6 +1330,7 @@ func TestEncodeConfiguration(t *testing.T) { "traefik.HTTP.Services.Service0.LoadBalancer.HealthCheck.Hostname": "foobar", "traefik.HTTP.Services.Service0.LoadBalancer.HealthCheck.Interval": "foobar", "traefik.HTTP.Services.Service0.LoadBalancer.HealthCheck.Path": "foobar", + "traefik.HTTP.Services.Service0.LoadBalancer.HealthCheck.Method": "foobar", "traefik.HTTP.Services.Service0.LoadBalancer.HealthCheck.Port": "42", "traefik.HTTP.Services.Service0.LoadBalancer.HealthCheck.Scheme": "foobar", "traefik.HTTP.Services.Service0.LoadBalancer.HealthCheck.Timeout": "foobar", @@ -1339,6 +1346,7 @@ func TestEncodeConfiguration(t *testing.T) { "traefik.HTTP.Services.Service1.LoadBalancer.HealthCheck.Hostname": "foobar", "traefik.HTTP.Services.Service1.LoadBalancer.HealthCheck.Interval": "foobar", "traefik.HTTP.Services.Service1.LoadBalancer.HealthCheck.Path": "foobar", + "traefik.HTTP.Services.Service1.LoadBalancer.HealthCheck.Method": "foobar", "traefik.HTTP.Services.Service1.LoadBalancer.HealthCheck.Port": "42", "traefik.HTTP.Services.Service1.LoadBalancer.HealthCheck.Scheme": "foobar", "traefik.HTTP.Services.Service1.LoadBalancer.HealthCheck.Timeout": "foobar", diff --git a/pkg/healthcheck/healthcheck.go b/pkg/healthcheck/healthcheck.go index 97a4264c9f..77a7743bb5 100644 --- a/pkg/healthcheck/healthcheck.go +++ b/pkg/healthcheck/healthcheck.go @@ -8,6 +8,7 @@ import ( "net/http" "net/url" "strconv" + "strings" "sync" "time" @@ -60,6 +61,7 @@ type Options struct { Hostname string Scheme string Path string + Method string Port int FollowRedirects bool Transport http.RoundTripper @@ -69,7 +71,7 @@ type Options struct { } func (opt Options) String() string { - return fmt.Sprintf("[Hostname: %s Headers: %v Path: %s Port: %d Interval: %s Timeout: %s FollowRedirects: %v]", opt.Hostname, opt.Headers, opt.Path, opt.Port, opt.Interval, opt.Timeout, opt.FollowRedirects) + return fmt.Sprintf("[Hostname: %s Headers: %v Path: %s Method: %s Port: %d Interval: %s Timeout: %s FollowRedirects: %v]", opt.Hostname, opt.Headers, opt.Path, opt.Method, opt.Port, opt.Interval, opt.Timeout, opt.FollowRedirects) } type backendURL struct { @@ -101,8 +103,8 @@ func (b *BackendConfig) newRequest(serverURL *url.URL) (*http.Request, error) { return http.NewRequest(http.MethodGet, u.String(), http.NoBody) } -// this function adds additional http headers and hostname to http.request. -func (b *BackendConfig) addHeadersAndHost(req *http.Request) *http.Request { +// setRequestOptions sets all request options present on the BackendConfig. +func (b *BackendConfig) setRequestOptions(req *http.Request) *http.Request { if b.Options.Hostname != "" { req.Host = b.Options.Hostname } @@ -110,6 +112,11 @@ func (b *BackendConfig) addHeadersAndHost(req *http.Request) *http.Request { for k, v := range b.Options.Headers { req.Header.Set(k, v) } + + if b.Options.Method != "" { + req.Method = strings.ToUpper(b.Options.Method) + } + return req } @@ -246,7 +253,7 @@ func checkHealth(serverURL *url.URL, backend *BackendConfig) error { return fmt.Errorf("failed to create HTTP request: %w", err) } - req = backend.addHeadersAndHost(req) + req = backend.setRequestOptions(req) client := http.Client{ Timeout: backend.Options.Timeout, diff --git a/pkg/healthcheck/healthcheck_test.go b/pkg/healthcheck/healthcheck_test.go index fc71eafddf..914f48291f 100644 --- a/pkg/healthcheck/healthcheck_test.go +++ b/pkg/healthcheck/healthcheck_test.go @@ -288,13 +288,14 @@ func TestNewRequest(t *testing.T) { } } -func TestAddHeadersAndHost(t *testing.T) { +func TestRequestOptions(t *testing.T) { testCases := []struct { desc string serverURL string options Options expectedHostname string expectedHeader string + expectedMethod string }{ { desc: "override hostname", @@ -305,6 +306,7 @@ func TestAddHeadersAndHost(t *testing.T) { }, expectedHostname: "myhost", expectedHeader: "", + expectedMethod: http.MethodGet, }, { desc: "not override hostname", @@ -315,6 +317,7 @@ func TestAddHeadersAndHost(t *testing.T) { }, expectedHostname: "backend1:80", expectedHeader: "", + expectedMethod: http.MethodGet, }, { desc: "custom header", @@ -326,6 +329,7 @@ func TestAddHeadersAndHost(t *testing.T) { }, expectedHostname: "backend1:80", expectedHeader: "foo", + expectedMethod: http.MethodGet, }, { desc: "custom header with hostname override", @@ -337,6 +341,17 @@ func TestAddHeadersAndHost(t *testing.T) { }, expectedHostname: "myhost", expectedHeader: "foo", + expectedMethod: http.MethodGet, + }, + { + desc: "custom method", + serverURL: "http://backend1:80", + options: Options{ + Path: "/", + Method: http.MethodHead, + }, + expectedHostname: "backend1:80", + expectedMethod: http.MethodHead, }, } @@ -353,11 +368,12 @@ func TestAddHeadersAndHost(t *testing.T) { req, err := backend.newRequest(u) require.NoError(t, err, "failed to create new backend request") - req = backend.addHeadersAndHost(req) + req = backend.setRequestOptions(req) assert.Equal(t, "http://backend1:80/", req.URL.String()) assert.Equal(t, test.expectedHostname, req.Host) assert.Equal(t, test.expectedHeader, req.Header.Get("Custom-Header")) + assert.Equal(t, test.expectedMethod, req.Method) }) } } diff --git a/pkg/server/service/service.go b/pkg/server/service/service.go index 375e9f64ef..3ff89eaa01 100644 --- a/pkg/server/service/service.go +++ b/pkg/server/service/service.go @@ -360,6 +360,7 @@ func buildHealthCheckOptions(ctx context.Context, lb healthcheck.Balancer, backe return &healthcheck.Options{ Scheme: hc.Scheme, Path: hc.Path, + Method: hc.Method, Port: hc.Port, Interval: interval, Timeout: timeout,