@marcel91200 / caddy Mirror · read-only / commits / b2492f85
Fork 0

Fast and extensible multi-platform HTTP/1-2-3 web server with automatic HTTPS

Mirrored from https://github.com/Slazee/caddy.git

Synced Nov 12, 10:45 UTC

reverseproxy: add health_upstream subdirective (#6451)

* Add health_upstream Signed-off-by: Dylan Schultz <9121234+dylanschultzie@users.noreply.github.com> * Add health_upstream to caddyfile parsing * Add Active Upstream case for health checks * Update ignore health port comment Signed-off-by: Dylan Schultz <9121234+dylanschultzie@users.noreply.github.com> * Update Upstream json doc Signed-off-by: Dylan Schultz <9121234+dylanschultzie@users.noreply.github.com> * Update modules/caddyhttp/reverseproxy/healthchecks.go Co-authored-by: Francis Lavoie <lavofr@gmail.com> * Use error rather than log for health_port override Signed-off-by: Dylan Schultz <9121234+dylanschultzie@users.noreply.github.com> * Add comment about port being ignore if using upstream Signed-off-by: Dylan Schultz <9121234+dylanschultzie@users.noreply.github.com> --------- Signed-off-by: Dylan Schultz <9121234+dylanschultzie@users.noreply.github.com> Co-authored-by: Francis Lavoie <lavofr@gmail.com>

schultzie committed Jul 15, 2024 at 10:00 UTC b2492f8567069c223eab2156d18904c03f9dc4f3
3 files changed +52 -9
modules/caddyhttp/reverseproxy/caddyfile.go
+24
@@ -16,6 +16,7 @@ package reverseproxy
16
17 import (
18 "fmt"
19 + "net"
20 "net/http"
21 "reflect"
22 "strconv"
@@ -354,6 +355,26 @@ func (h *Handler) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
355 h.HealthChecks.Active.Path = d.Val()
356 caddy.Log().Named("config.adapter.caddyfile").Warn("the 'health_path' subdirective is deprecated, please use 'health_uri' instead!")
357
358 + case "health_upstream":
359 + if !d.NextArg() {
360 + return d.ArgErr()
361 + }
362 + if h.HealthChecks == nil {
363 + h.HealthChecks = new(HealthChecks)
364 + }
365 + if h.HealthChecks.Active == nil {
366 + h.HealthChecks.Active = new(ActiveHealthChecks)
367 + }
368 + _, port, err := net.SplitHostPort(d.Val())
369 + if err != nil {
370 + return d.Errf("health_upstream is malformed '%s': %v", d.Val(), err)
371 + }
372 + _, err = strconv.Atoi(port)
373 + if err != nil {
374 + return d.Errf("bad port number '%s': %v", d.Val(), err)
375 + }
376 + h.HealthChecks.Active.Upstream = d.Val()
377 +
378 case "health_port":
379 if !d.NextArg() {
380 return d.ArgErr()
@@ -364,6 +385,9 @@ func (h *Handler) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
385 if h.HealthChecks.Active == nil {
386 h.HealthChecks.Active = new(ActiveHealthChecks)
387 }
388 + if h.HealthChecks.Active.Upstream != "" {
389 + return d.Errf("the 'health_port' subdirective is ignored if 'health_upstream' is used!")
390 + }
391 portNum, err := strconv.Atoi(d.Val())
392 if err != nil {
393 return d.Errf("bad port number '%s': %v", d.Val(), err)
modules/caddyhttp/reverseproxy/healthchecks.go
+23 -5
@@ -75,8 +75,16 @@ type ActiveHealthChecks struct {
75 // The URI (path and query) to use for health checks
76 URI string `json:"uri,omitempty"`
77
78 + // The host:port to use (if different from the upstream's dial address)
79 + // for health checks. This should be used in tandem with `health_header` and
80 + // `{http.reverse_proxy.active.target_upstream}`. This can be helpful when
81 + // creating an intermediate service to do a more thorough health check.
82 + // If upstream is set, the active health check port is ignored.
83 + Upstream string `json:"upstream,omitempty"`
84 +
85 // The port to use (if different from the upstream's dial
79 - // address) for health checks.
86 + // address) for health checks. If active upstream is set,
87 + // this value is ignored.
88 Port int `json:"port,omitempty"`
89
90 // HTTP headers to set on health check requests.
@@ -173,9 +181,14 @@ func (a *ActiveHealthChecks) Provision(ctx caddy.Context, h *Handler) error {
181 }
182
183 for _, upstream := range h.Upstreams {
176 - // if there's an alternative port for health-check provided in the config,
177 - // then use it, otherwise use the port of upstream.
178 - if a.Port != 0 {
184 + // if there's an alternative upstream for health-check provided in the config,
185 + // then use it, otherwise use the upstream's dial address. if upstream is used,
186 + // then the port is ignored.
187 + if a.Upstream != "" {
188 + upstream.activeHealthCheckUpstream = a.Upstream
189 + } else if a.Port != 0 {
190 + // if there's an alternative port for health-check provided in the config,
191 + // then use it, otherwise use the port of upstream.
192 upstream.activeHealthCheckPort = a.Port
193 }
194 }
@@ -350,7 +363,12 @@ func (h *Handler) doActiveHealthCheck(dialInfo DialInfo, hostAddr string, networ
363 if err != nil {
364 host = hostAddr
365 }
353 - if h.HealthChecks.Active.Port != 0 {
366 +
367 + // ignore active health check port if active upstream is provided as the
368 + // active upstream already contains the replacement port
369 + if h.HealthChecks.Active.Upstream != "" {
370 + u.Host = h.HealthChecks.Active.Upstream
371 + } else if h.HealthChecks.Active.Port != 0 {
372 port := strconv.Itoa(h.HealthChecks.Active.Port)
373 u.Host = net.JoinHostPort(host, port)
374 }
modules/caddyhttp/reverseproxy/hosts.go
+5 -4
@@ -57,10 +57,11 @@ type Upstream struct {
57 // HeaderAffinity string
58 // IPAffinity string
59
60 - activeHealthCheckPort int
61 - healthCheckPolicy *PassiveHealthChecks
62 - cb CircuitBreaker
63 - unhealthy int32 // accessed atomically; status from active health checker
60 + activeHealthCheckPort int
61 + activeHealthCheckUpstream string
62 + healthCheckPolicy *PassiveHealthChecks
63 + cb CircuitBreaker
64 + unhealthy int32 // accessed atomically; status from active health checker
65 }
66
67 // (pointer receiver necessary to avoid a race condition, since