diff --git a/internal/cli/healthcheck.go b/internal/cli/healthcheck.go index 51f05eba..839cfaab 100644 --- a/internal/cli/healthcheck.go +++ b/internal/cli/healthcheck.go @@ -31,10 +31,10 @@ func (c *CLI) HealthCheck(ctx context.Context, env params.Env, const timeout = 10 * time.Second httpClient := &http.Client{Timeout: timeout} - healthchecker := healthcheck.NewChecker(httpClient) + client := healthcheck.NewClient(httpClient) ctx, cancel := context.WithTimeout(ctx, timeout) defer cancel() url := "http://127.0.0.1:" + port - return healthchecker.Check(ctx, url) + return client.Check(ctx, url) } diff --git a/internal/healthcheck/client.go b/internal/healthcheck/client.go index b2b0f84c..76458a33 100644 --- a/internal/healthcheck/client.go +++ b/internal/healthcheck/client.go @@ -12,26 +12,28 @@ var ( ErrHTTPStatusNotOK = errors.New("HTTP response status is not OK") ) +var _ Checker = (*Client)(nil) + type Checker interface { Check(ctx context.Context, url string) error } -type checker struct { +type Client struct { httpClient *http.Client } -func NewChecker(httpClient *http.Client) Checker { - return &checker{ +func NewClient(httpClient *http.Client) *Client { + return &Client{ httpClient: httpClient, } } -func (h *checker) Check(ctx context.Context, url string) error { +func (c *Client) Check(ctx context.Context, url string) error { request, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) if err != nil { return err } - response, err := h.httpClient.Do(request) + response, err := c.httpClient.Do(request) if err != nil { return err } diff --git a/internal/healthcheck/health.go b/internal/healthcheck/health.go index 179929f3..a2502640 100644 --- a/internal/healthcheck/health.go +++ b/internal/healthcheck/health.go @@ -9,7 +9,7 @@ import ( "time" ) -func (s *server) runHealthcheckLoop(ctx context.Context, done chan<- struct{}) { +func (s *Server) runHealthcheckLoop(ctx context.Context, done chan<- struct{}) { defer close(done) s.openvpn.healthyTimer = time.NewTimer(s.openvpn.healthyWait) diff --git a/internal/healthcheck/openvpn.go b/internal/healthcheck/openvpn.go index b74c3918..7e618ea6 100644 --- a/internal/healthcheck/openvpn.go +++ b/internal/healthcheck/openvpn.go @@ -5,9 +5,16 @@ import ( "time" "github.com/qdm12/gluetun/internal/constants" + "github.com/qdm12/gluetun/internal/openvpn" ) -func (s *server) onUnhealthyOpenvpn(ctx context.Context) { +type openvpnHealth struct { + looper openvpn.Looper + healthyWait time.Duration + healthyTimer *time.Timer +} + +func (s *Server) onUnhealthyOpenvpn(ctx context.Context) { s.logger.Info("program has been unhealthy for " + s.openvpn.healthyWait.String() + ": restarting OpenVPN") _, _ = s.openvpn.looper.ApplyStatus(ctx, constants.Stopped) diff --git a/internal/healthcheck/run.go b/internal/healthcheck/run.go new file mode 100644 index 00000000..be5418a2 --- /dev/null +++ b/internal/healthcheck/run.go @@ -0,0 +1,40 @@ +package healthcheck + +import ( + "context" + "errors" + "net/http" + "time" +) + +func (s *Server) Run(ctx context.Context, done chan<- struct{}) { + defer close(done) + + loopDone := make(chan struct{}) + go s.runHealthcheckLoop(ctx, loopDone) + + server := http.Server{ + Addr: s.config.ServerAddress, + Handler: s.handler, + } + serverDone := make(chan struct{}) + go func() { + defer close(serverDone) + <-ctx.Done() + const shutdownGraceDuration = 2 * time.Second + shutdownCtx, cancel := context.WithTimeout(context.Background(), shutdownGraceDuration) + defer cancel() + if err := server.Shutdown(shutdownCtx); err != nil { + s.logger.Error("failed shutting down: " + err.Error()) + } + }() + + s.logger.Info("listening on " + s.config.ServerAddress) + err := server.ListenAndServe() + if err != nil && !errors.Is(ctx.Err(), context.Canceled) { + s.logger.Error(err.Error()) + } + + <-loopDone + <-serverDone +} diff --git a/internal/healthcheck/server.go b/internal/healthcheck/server.go index 4b7b9b7a..34f45a59 100644 --- a/internal/healthcheck/server.go +++ b/internal/healthcheck/server.go @@ -2,21 +2,20 @@ package healthcheck import ( "context" - "errors" "net" - "net/http" - "time" "github.com/qdm12/gluetun/internal/configuration" "github.com/qdm12/gluetun/internal/openvpn" "github.com/qdm12/golibs/logging" ) -type Server interface { +var _ ServerRunner = (*Server)(nil) + +type ServerRunner interface { Run(ctx context.Context, done chan<- struct{}) } -type server struct { +type Server struct { logger logging.Logger handler *handler resolver *net.Resolver @@ -24,15 +23,9 @@ type server struct { openvpn openvpnHealth } -type openvpnHealth struct { - looper openvpn.Looper - healthyWait time.Duration - healthyTimer *time.Timer -} - func NewServer(config configuration.Health, - logger logging.Logger, openvpnLooper openvpn.Looper) Server { - return &server{ + logger logging.Logger, openvpnLooper openvpn.Looper) *Server { + return &Server{ logger: logger, handler: newHandler(logger), resolver: net.DefaultResolver, @@ -43,35 +36,3 @@ func NewServer(config configuration.Health, }, } } - -func (s *server) Run(ctx context.Context, done chan<- struct{}) { - defer close(done) - - loopDone := make(chan struct{}) - go s.runHealthcheckLoop(ctx, loopDone) - - server := http.Server{ - Addr: s.config.ServerAddress, - Handler: s.handler, - } - serverDone := make(chan struct{}) - go func() { - defer close(serverDone) - <-ctx.Done() - const shutdownGraceDuration = 2 * time.Second - shutdownCtx, cancel := context.WithTimeout(context.Background(), shutdownGraceDuration) - defer cancel() - if err := server.Shutdown(shutdownCtx); err != nil { - s.logger.Error("failed shutting down: " + err.Error()) - } - }() - - s.logger.Info("listening on " + s.config.ServerAddress) - err := server.ListenAndServe() - if err != nil && !errors.Is(ctx.Err(), context.Canceled) { - s.logger.Error(err.Error()) - } - - <-loopDone - <-serverDone -}