mirror of
https://github.com/qdm12/gluetun.git
synced 2026-07-30 17:03:22 +02:00
fix(healthcheck): prevent race condition on the healthchecker (#3400)
Co-authored-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com> Co-authored-by: Quentin McGaw <quentin.mcgaw@gmail.com> Thanks also to @arturhoo for also trying to resolve this issue in #3410
This commit is contained in:
committed by
Quentin McGaw
parent
cccd7c4c1b
commit
c0073c9567
@@ -8,7 +8,6 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/qdm12/gluetun/internal/healthcheck/dns"
|
"github.com/qdm12/gluetun/internal/healthcheck/dns"
|
||||||
@@ -24,7 +23,6 @@ type Checker struct {
|
|||||||
icmpTargetIPs []netip.Addr
|
icmpTargetIPs []netip.Addr
|
||||||
smallCheckType string
|
smallCheckType string
|
||||||
startupOnFail bool
|
startupOnFail bool
|
||||||
configMutex sync.Mutex
|
|
||||||
|
|
||||||
icmpNotPermitted *bool
|
icmpNotPermitted *bool
|
||||||
|
|
||||||
@@ -55,8 +53,6 @@ func NewChecker(logger Logger) *Checker {
|
|||||||
func (c *Checker) SetConfig(tlsDialAddrs []string, icmpTargets []netip.Addr,
|
func (c *Checker) SetConfig(tlsDialAddrs []string, icmpTargets []netip.Addr,
|
||||||
smallCheckType string, startupOnFail bool,
|
smallCheckType string, startupOnFail bool,
|
||||||
) {
|
) {
|
||||||
c.configMutex.Lock()
|
|
||||||
defer c.configMutex.Unlock()
|
|
||||||
c.tlsDialAddrs = tlsDialAddrs
|
c.tlsDialAddrs = tlsDialAddrs
|
||||||
c.icmpTargetIPs = icmpTargets
|
c.icmpTargetIPs = icmpTargets
|
||||||
c.smallCheckType = smallCheckType
|
c.smallCheckType = smallCheckType
|
||||||
@@ -166,10 +162,8 @@ func (c *Checker) Stop() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Checker) smallPeriodicCheck(ctx context.Context) error {
|
func (c *Checker) smallPeriodicCheck(ctx context.Context) error {
|
||||||
c.configMutex.Lock()
|
|
||||||
icmpTargetIPs := make([]netip.Addr, len(c.icmpTargetIPs))
|
icmpTargetIPs := make([]netip.Addr, len(c.icmpTargetIPs))
|
||||||
copy(icmpTargetIPs, c.icmpTargetIPs)
|
copy(icmpTargetIPs, c.icmpTargetIPs)
|
||||||
c.configMutex.Unlock()
|
|
||||||
tryTimeouts := []time.Duration{
|
tryTimeouts := []time.Duration{
|
||||||
5 * time.Second,
|
5 * time.Second,
|
||||||
5 * time.Second,
|
5 * time.Second,
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ type Loop struct {
|
|||||||
start <-chan struct{}
|
start <-chan struct{}
|
||||||
running chan<- models.LoopStatus
|
running chan<- models.LoopStatus
|
||||||
userTrigger bool
|
userTrigger bool
|
||||||
|
healthDone <-chan struct{}
|
||||||
// Internal constant values
|
// Internal constant values
|
||||||
backoffTime time.Duration
|
backoffTime time.Duration
|
||||||
}
|
}
|
||||||
@@ -68,6 +69,11 @@ func NewLoop(vpnSettings settings.VPN, ipv6Supported bool, vpnInputPorts []uint1
|
|||||||
statusManager := loopstate.New(constants.Stopped, start, running, stop, stopped)
|
statusManager := loopstate.New(constants.Stopped, start, running, stop, stopped)
|
||||||
state := state.New(statusManager, vpnSettings)
|
state := state.New(statusManager, vpnSettings)
|
||||||
|
|
||||||
|
// Initialize healthDone channel to a closed channel so that the first time
|
||||||
|
// we call <-l.healthDone it does not block
|
||||||
|
healthDone := make(chan struct{})
|
||||||
|
close(healthDone)
|
||||||
|
|
||||||
return &Loop{
|
return &Loop{
|
||||||
statusManager: statusManager,
|
statusManager: statusManager,
|
||||||
state: state,
|
state: state,
|
||||||
@@ -95,6 +101,7 @@ func NewLoop(vpnSettings settings.VPN, ipv6Supported bool, vpnInputPorts []uint1
|
|||||||
stop: stop,
|
stop: stop,
|
||||||
stopped: stopped,
|
stopped: stopped,
|
||||||
userTrigger: true,
|
userTrigger: true,
|
||||||
|
healthDone: healthDone,
|
||||||
backoffTime: defaultBackoffTime,
|
backoffTime: defaultBackoffTime,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ func (l *Loop) onTunnelUp(ctx, loopCtx context.Context, data tunnelUpData) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
<-l.healthDone // make sure the health checker is stopped before restarting it
|
||||||
icmpTargetIPs := l.healthSettings.ICMPTargetIPs
|
icmpTargetIPs := l.healthSettings.ICMPTargetIPs
|
||||||
if len(icmpTargetIPs) == 1 && icmpTargetIPs[0].IsUnspecified() {
|
if len(icmpTargetIPs) == 1 && icmpTargetIPs[0].IsUnspecified() {
|
||||||
icmpTargetIPs = []netip.Addr{data.serverIP}
|
icmpTargetIPs = []netip.Addr{data.serverIP}
|
||||||
@@ -54,7 +55,12 @@ func (l *Loop) onTunnelUp(ctx, loopCtx context.Context, data tunnelUpData) {
|
|||||||
// Start collecting health errors asynchronously, since
|
// Start collecting health errors asynchronously, since
|
||||||
// we should not wait for the code below to complete
|
// we should not wait for the code below to complete
|
||||||
// to start monitoring health and auto-healing.
|
// to start monitoring health and auto-healing.
|
||||||
go l.collectHealthErrors(ctx, loopCtx, healthErrCh)
|
// We keep track of when this goroutine is done with the healthDone
|
||||||
|
// channel to avoid a race condition where the health checker is stopped
|
||||||
|
// after being reconfigured and restarted, instead of before.
|
||||||
|
healthDone := make(chan struct{})
|
||||||
|
l.healthDone = healthDone
|
||||||
|
go l.collectHealthErrors(ctx, loopCtx, healthDone, healthErrCh)
|
||||||
|
|
||||||
if *l.dnsLooper.GetSettings().ServerEnabled {
|
if *l.dnsLooper.GetSettings().ServerEnabled {
|
||||||
_, _ = l.dnsLooper.ApplyStatus(ctx, constants.Running)
|
_, _ = l.dnsLooper.ApplyStatus(ctx, constants.Running)
|
||||||
@@ -86,7 +92,10 @@ func (l *Loop) onTunnelUp(ctx, loopCtx context.Context, data tunnelUpData) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *Loop) collectHealthErrors(ctx, loopCtx context.Context, healthErrCh <-chan error) {
|
func (l *Loop) collectHealthErrors(ctx, loopCtx context.Context,
|
||||||
|
done chan<- struct{}, healthErrCh <-chan error,
|
||||||
|
) {
|
||||||
|
defer close(done)
|
||||||
var previousHealthErr error
|
var previousHealthErr error
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
|
|||||||
Reference in New Issue
Block a user