Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,7 @@ ENV VPN_SERVICE_PROVIDER=pia \
LOG_LEVEL=info \
# Health
HEALTH_SERVER_ADDRESS=127.0.0.1:9999 \
HEALTH_SERVER_DISABLE_LOOP=off \
HEALTH_TARGET_ADDRESS=cloudflare.com:443 \
HEALTH_SUCCESS_WAIT_DURATION=5s \
HEALTH_VPN_DURATION_INITIAL=6s \
Expand Down
13 changes: 10 additions & 3 deletions internal/configuration/settings/health.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ type Health struct {
SuccessWait time.Duration
// VPN has health settings specific to the VPN loop.
VPN HealthyWait
// Disable the healthcheck server loop. Useful for environments like Kubernetes which have their own healthcheck loops.
DisableLoop *bool
}

func (h Health) Validate() (err error) {
Expand All @@ -58,6 +60,7 @@ func (h *Health) copy() (copied Health) {
TargetAddress: h.TargetAddress,
SuccessWait: h.SuccessWait,
VPN: h.VPN.copy(),
DisableLoop: gosettings.CopyPointer(h.DisableLoop),
}
}

Expand All @@ -71,6 +74,7 @@ func (h *Health) OverrideWith(other Health) {
h.TargetAddress = gosettings.OverrideWithComparable(h.TargetAddress, other.TargetAddress)
h.SuccessWait = gosettings.OverrideWithComparable(h.SuccessWait, other.SuccessWait)
h.VPN.overrideWith(other.VPN)
h.DisableLoop = gosettings.OverrideWithPointer(h.DisableLoop, other.DisableLoop)
}

func (h *Health) SetDefaults() {
Expand All @@ -83,6 +87,7 @@ func (h *Health) SetDefaults() {
const defaultSuccessWait = 5 * time.Second
h.SuccessWait = gosettings.DefaultComparable(h.SuccessWait, defaultSuccessWait)
h.VPN.setDefaults()
h.DisableLoop = gosettings.DefaultPointer(h.DisableLoop, false)
}

func (h Health) String() string {
Expand All @@ -97,23 +102,25 @@ func (h Health) toLinesNode() (node *gotree.Node) {
node.Appendf("Read header timeout: %s", h.ReadHeaderTimeout)
node.Appendf("Read timeout: %s", h.ReadTimeout)
node.AppendNode(h.VPN.toLinesNode("VPN"))
node.Appendf("Disable loop: %s", gosettings.BoolToYesNo(h.DisableLoop))
return node
}

func (h *Health) Read(r *reader.Reader) (err error) {
h.ServerAddress = r.String("HEALTH_SERVER_ADDRESS")
h.TargetAddress = r.String("HEALTH_TARGET_ADDRESS",
reader.RetroKeys("HEALTH_ADDRESS_TO_PING"))

h.SuccessWait, err = r.Duration("HEALTH_SUCCESS_WAIT_DURATION")
if err != nil {
return err
}

err = h.VPN.read(r)
if err != nil {
return fmt.Errorf("VPN health settings: %w", err)
}

h.DisableLoop, err = r.BoolPtr("HEALTH_SERVER_DISABLE_LOOP")
if err != nil {
return err
}
return nil
}
8 changes: 6 additions & 2 deletions internal/healthcheck/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,15 @@ func (s *Server) Run(ctx context.Context, done chan<- struct{}) {
defer close(done)

loopDone := make(chan struct{})
go s.runHealthcheckLoop(ctx, loopDone)
if !*s.config.DisableLoop {
go s.runHealthcheckLoop(ctx, loopDone)
} else {
close(done)
}

server := http.Server{
Addr: s.config.ServerAddress,
Handler: s.handler,
Handler: s.mux,
ReadHeaderTimeout: s.config.ReadHeaderTimeout,
ReadTimeout: s.config.ReadTimeout,
}
Expand Down
15 changes: 14 additions & 1 deletion internal/healthcheck/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package healthcheck
import (
"context"
"net"
"net/http"

"github.com/qdm12/gluetun/internal/configuration/settings"
"github.com/qdm12/gluetun/internal/models"
Expand All @@ -14,12 +15,13 @@ type Server struct {
dialer *net.Dialer
config settings.Health
vpn vpnHealth
mux *http.ServeMux
}

func NewServer(config settings.Health,
logger Logger, vpnLoop StatusApplier,
) *Server {
return &Server{
s := &Server{
logger: logger,
handler: newHandler(),
dialer: &net.Dialer{
Expand All @@ -32,7 +34,18 @@ func NewServer(config settings.Health,
loop: vpnLoop,
healthyWait: *config.VPN.Initial,
},
mux: http.NewServeMux(),
}
s.mux.Handle("/", s.handler)
s.mux.HandleFunc("/check/", func(w http.ResponseWriter, r *http.Request) {
err := s.healthCheck(r.Context())
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
w.WriteHeader(http.StatusOK)
})
return s
}

type StatusApplier interface {
Expand Down