Maint: healthcheck package interface rework

- return concrete struct type
- Add compilation checks for implementations
This commit is contained in:
Quentin McGaw (desktop)
2021-07-23 19:22:41 +00:00
parent c39ff5c233
commit 54610866f2
6 changed files with 64 additions and 54 deletions

View File

@@ -31,10 +31,10 @@ func (c *CLI) HealthCheck(ctx context.Context, env params.Env,
const timeout = 10 * time.Second const timeout = 10 * time.Second
httpClient := &http.Client{Timeout: timeout} httpClient := &http.Client{Timeout: timeout}
healthchecker := healthcheck.NewChecker(httpClient) client := healthcheck.NewClient(httpClient)
ctx, cancel := context.WithTimeout(ctx, timeout) ctx, cancel := context.WithTimeout(ctx, timeout)
defer cancel() defer cancel()
url := "http://127.0.0.1:" + port url := "http://127.0.0.1:" + port
return healthchecker.Check(ctx, url) return client.Check(ctx, url)
} }

View File

@@ -12,26 +12,28 @@ var (
ErrHTTPStatusNotOK = errors.New("HTTP response status is not OK") ErrHTTPStatusNotOK = errors.New("HTTP response status is not OK")
) )
var _ Checker = (*Client)(nil)
type Checker interface { type Checker interface {
Check(ctx context.Context, url string) error Check(ctx context.Context, url string) error
} }
type checker struct { type Client struct {
httpClient *http.Client httpClient *http.Client
} }
func NewChecker(httpClient *http.Client) Checker { func NewClient(httpClient *http.Client) *Client {
return &checker{ return &Client{
httpClient: httpClient, 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) request, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
if err != nil { if err != nil {
return err return err
} }
response, err := h.httpClient.Do(request) response, err := c.httpClient.Do(request)
if err != nil { if err != nil {
return err return err
} }

View File

@@ -9,7 +9,7 @@ import (
"time" "time"
) )
func (s *server) runHealthcheckLoop(ctx context.Context, done chan<- struct{}) { func (s *Server) runHealthcheckLoop(ctx context.Context, done chan<- struct{}) {
defer close(done) defer close(done)
s.openvpn.healthyTimer = time.NewTimer(s.openvpn.healthyWait) s.openvpn.healthyTimer = time.NewTimer(s.openvpn.healthyWait)

View File

@@ -5,9 +5,16 @@ import (
"time" "time"
"github.com/qdm12/gluetun/internal/constants" "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.logger.Info("program has been unhealthy for " +
s.openvpn.healthyWait.String() + ": restarting OpenVPN") s.openvpn.healthyWait.String() + ": restarting OpenVPN")
_, _ = s.openvpn.looper.ApplyStatus(ctx, constants.Stopped) _, _ = s.openvpn.looper.ApplyStatus(ctx, constants.Stopped)

View File

@@ -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
}

View File

@@ -2,21 +2,20 @@ package healthcheck
import ( import (
"context" "context"
"errors"
"net" "net"
"net/http"
"time"
"github.com/qdm12/gluetun/internal/configuration" "github.com/qdm12/gluetun/internal/configuration"
"github.com/qdm12/gluetun/internal/openvpn" "github.com/qdm12/gluetun/internal/openvpn"
"github.com/qdm12/golibs/logging" "github.com/qdm12/golibs/logging"
) )
type Server interface { var _ ServerRunner = (*Server)(nil)
type ServerRunner interface {
Run(ctx context.Context, done chan<- struct{}) Run(ctx context.Context, done chan<- struct{})
} }
type server struct { type Server struct {
logger logging.Logger logger logging.Logger
handler *handler handler *handler
resolver *net.Resolver resolver *net.Resolver
@@ -24,15 +23,9 @@ type server struct {
openvpn openvpnHealth openvpn openvpnHealth
} }
type openvpnHealth struct {
looper openvpn.Looper
healthyWait time.Duration
healthyTimer *time.Timer
}
func NewServer(config configuration.Health, func NewServer(config configuration.Health,
logger logging.Logger, openvpnLooper openvpn.Looper) Server { logger logging.Logger, openvpnLooper openvpn.Looper) *Server {
return &server{ return &Server{
logger: logger, logger: logger,
handler: newHandler(logger), handler: newHandler(logger),
resolver: net.DefaultResolver, 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
}