Prepare API
This commit is contained in:
parent
c85c4f3512
commit
81b65b42b6
24 changed files with 307 additions and 22 deletions
35
api/check_handler.go
Normal file
35
api/check_handler.go
Normal file
|
@ -0,0 +1,35 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/baez90/nurse/check"
|
||||
)
|
||||
|
||||
var _ http.Handler = (*CheckHandler)(nil)
|
||||
|
||||
type CheckHandler struct {
|
||||
Timeout time.Duration
|
||||
Check check.SystemChecker
|
||||
}
|
||||
|
||||
func (c CheckHandler) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
|
||||
var (
|
||||
ctx = request.Context()
|
||||
cancel context.CancelFunc
|
||||
)
|
||||
if c.Timeout != 0 {
|
||||
ctx, cancel = context.WithTimeout(ctx, c.Timeout)
|
||||
defer cancel()
|
||||
}
|
||||
if err := c.Check.Execute(ctx); err != nil {
|
||||
writer.WriteHeader(http.StatusServiceUnavailable)
|
||||
_, _ = writer.Write([]byte(err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
writer.WriteHeader(200)
|
||||
return
|
||||
}
|
31
api/mux.go
Normal file
31
api/mux.go
Normal file
|
@ -0,0 +1,31 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"go.uber.org/zap"
|
||||
|
||||
"github.com/baez90/nurse/check"
|
||||
"github.com/baez90/nurse/config"
|
||||
)
|
||||
|
||||
func PrepareMux(instance *config.Nurse, modLookup check.ModuleLookup, srvLookup config.ServerLookup) (http.Handler, error) {
|
||||
mux := http.NewServeMux()
|
||||
|
||||
logger := zap.L()
|
||||
|
||||
for route, spec := range instance.Endpoints {
|
||||
logger.Info("Configuring route", zap.String("route", route.String()))
|
||||
chk, err := check.CheckForScript(spec.Checks, modLookup, srvLookup)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
mux.Handle(route.String(), CheckHandler{
|
||||
Timeout: spec.Timeout(instance.CheckTimeout),
|
||||
Check: chk,
|
||||
})
|
||||
}
|
||||
|
||||
return mux, nil
|
||||
}
|
|
@ -26,4 +26,12 @@ type (
|
|||
CallUnmarshaler interface {
|
||||
UnmarshalCall(c grammar.Call) error
|
||||
}
|
||||
|
||||
CheckerLookup interface {
|
||||
Lookup(c grammar.Check, srvLookup config.ServerLookup) (SystemChecker, error)
|
||||
}
|
||||
|
||||
ModuleLookup interface {
|
||||
Lookup(modName string) (CheckerLookup, error)
|
||||
}
|
||||
)
|
||||
|
|
31
check/collection.go
Normal file
31
check/collection.go
Normal file
|
@ -0,0 +1,31 @@
|
|||
package check
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"golang.org/x/sync/errgroup"
|
||||
|
||||
"github.com/baez90/nurse/config"
|
||||
"github.com/baez90/nurse/grammar"
|
||||
)
|
||||
|
||||
var _ SystemChecker = (Collection)(nil)
|
||||
|
||||
type Collection []SystemChecker
|
||||
|
||||
func (Collection) UnmarshalCheck(grammar.Check, config.ServerLookup) error {
|
||||
panic("unmarshalling is not supported for a collection")
|
||||
}
|
||||
|
||||
func (c Collection) Execute(ctx context.Context) error {
|
||||
grp, grpCtx := errgroup.WithContext(ctx)
|
||||
|
||||
for i := range c {
|
||||
chk := c[i]
|
||||
grp.Go(func() error {
|
||||
return chk.Execute(grpCtx)
|
||||
})
|
||||
}
|
||||
|
||||
return grp.Wait()
|
||||
}
|
27
check/endpoint.go
Normal file
27
check/endpoint.go
Normal file
|
@ -0,0 +1,27 @@
|
|||
package check
|
||||
|
||||
import (
|
||||
"github.com/baez90/nurse/config"
|
||||
"github.com/baez90/nurse/grammar"
|
||||
)
|
||||
|
||||
func CheckForScript(script []grammar.Check, lkp ModuleLookup, srvLookup config.ServerLookup) (SystemChecker, error) {
|
||||
compiledChecks := make([]SystemChecker, 0, len(script))
|
||||
|
||||
for i := range script {
|
||||
rawChk := script[i]
|
||||
mod, err := lkp.Lookup(rawChk.Initiator.Module)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
compiledCheck, err := mod.Lookup(rawChk, srvLookup)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
compiledChecks = append(compiledChecks, compiledCheck)
|
||||
}
|
||||
|
||||
return Collection(compiledChecks), nil
|
||||
}
|
|
@ -37,8 +37,9 @@ func WithCheck(name string, factory Factory) ModuleOption {
|
|||
})
|
||||
}
|
||||
|
||||
func NewModule(opts ...ModuleOption) (*Module, error) {
|
||||
func NewModule(name string, opts ...ModuleOption) (*Module, error) {
|
||||
m := &Module{
|
||||
name: name,
|
||||
knownChecks: make(map[string]Factory),
|
||||
}
|
||||
|
||||
|
@ -52,10 +53,15 @@ func NewModule(opts ...ModuleOption) (*Module, error) {
|
|||
}
|
||||
|
||||
type Module struct {
|
||||
name string
|
||||
lock sync.RWMutex
|
||||
knownChecks map[string]Factory
|
||||
}
|
||||
|
||||
func (m *Module) Name() string {
|
||||
return m.name
|
||||
}
|
||||
|
||||
func (m *Module) Lookup(c grammar.Check, srvLookup config.ServerLookup) (SystemChecker, error) {
|
||||
m.lock.RLock()
|
||||
defer m.lock.RUnlock()
|
||||
|
|
53
check/registry.go
Normal file
53
check/registry.go
Normal file
|
@ -0,0 +1,53 @@
|
|||
package check
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrModuleNameConflict = errors.New("module name conflict")
|
||||
ErrNoSuchModule = errors.New("no module of given name known")
|
||||
)
|
||||
|
||||
func NewRegistry() *Registry {
|
||||
return &Registry{
|
||||
mods: make(map[string]*Module),
|
||||
}
|
||||
}
|
||||
|
||||
type (
|
||||
Registry struct {
|
||||
lock sync.RWMutex
|
||||
mods map[string]*Module
|
||||
}
|
||||
)
|
||||
|
||||
func (r *Registry) Register(module *Module) error {
|
||||
r.lock.Lock()
|
||||
defer r.lock.Unlock()
|
||||
|
||||
modName := strings.ToLower(module.Name())
|
||||
|
||||
if _, ok := r.mods[modName]; ok {
|
||||
return fmt.Errorf("%w: %s", ErrModuleNameConflict, modName)
|
||||
}
|
||||
|
||||
r.mods[modName] = module
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Registry) Lookup(modName string) (CheckerLookup, error) {
|
||||
r.lock.RLock()
|
||||
defer r.lock.RUnlock()
|
||||
|
||||
modName = strings.ToLower(modName)
|
||||
|
||||
if mod, ok := r.mods[modName]; !ok {
|
||||
return nil, fmt.Errorf("%w: %s", ErrNoSuchModule, modName)
|
||||
} else {
|
||||
return mod, nil
|
||||
}
|
||||
}
|
|
@ -38,6 +38,18 @@ type Nurse struct {
|
|||
CheckTimeout time.Duration
|
||||
}
|
||||
|
||||
func (n Nurse) ServerLookup() (*ServerRegister, error) {
|
||||
register := NewServerRegister()
|
||||
|
||||
for name, srv := range n.Servers {
|
||||
if err := register.Register(name, srv); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return register, nil
|
||||
}
|
||||
|
||||
// Merge merges the current Nurse instance with another one
|
||||
// giving the current instance precedence means no set value is overwritten
|
||||
func (n Nurse) Merge(other Nurse) Nurse {
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"encoding"
|
||||
"fmt"
|
||||
"path"
|
||||
"strings"
|
||||
|
@ -10,8 +9,6 @@ import (
|
|||
"github.com/baez90/nurse/grammar"
|
||||
)
|
||||
|
||||
var _ encoding.TextUnmarshaler = (*EndpointSpec)(nil)
|
||||
|
||||
type Route string
|
||||
|
||||
func (r Route) String() string {
|
||||
|
@ -26,17 +23,25 @@ type EndpointSpec struct {
|
|||
Checks []grammar.Check
|
||||
}
|
||||
|
||||
func (e *EndpointSpec) UnmarshalText(text []byte) error {
|
||||
func (s EndpointSpec) Timeout(fallback time.Duration) time.Duration {
|
||||
if s.CheckTimeout != 0 {
|
||||
return s.CheckTimeout
|
||||
}
|
||||
|
||||
return fallback
|
||||
}
|
||||
|
||||
func (s *EndpointSpec) Parse(text string) error {
|
||||
parser, err := grammar.NewParser[grammar.Script]()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
script, err := parser.Parse(string(text))
|
||||
script, err := parser.Parse(text)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
e.Checks = script.Checks
|
||||
s.Checks = script.Checks
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -50,7 +50,7 @@ func EndpointsFromEnv() (map[Route]EndpointSpec, error) {
|
|||
|
||||
endpointRoute := path.Join(Split(ToLower(Trim(Replace(key, EndpointKeyPrefix, "", -1), "_")), "_")...)
|
||||
spec := EndpointSpec{}
|
||||
if err := spec.UnmarshalText([]byte(value)); err != nil {
|
||||
if err := spec.Parse(value); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
|
|
@ -95,7 +95,7 @@ func TestEndpointsFromEnv(t *testing.T) {
|
|||
},
|
||||
want: td.Map(make(map[config.Route]config.EndpointSpec), td.MapEntries{
|
||||
config.Route("readiness"): td.Struct(config.EndpointSpec{}, td.StructFields{
|
||||
"Checks": td.Len(1),
|
||||
"Script": td.Len(1),
|
||||
}),
|
||||
}),
|
||||
wantErr: false,
|
||||
|
@ -107,7 +107,7 @@ func TestEndpointsFromEnv(t *testing.T) {
|
|||
},
|
||||
want: td.Map(make(map[config.Route]config.EndpointSpec), td.MapEntries{
|
||||
config.Route("readiness"): td.Struct(config.EndpointSpec{}, td.StructFields{
|
||||
"Checks": td.Len(2),
|
||||
"Script": td.Len(2),
|
||||
}),
|
||||
}),
|
||||
wantErr: false,
|
||||
|
@ -119,7 +119,7 @@ func TestEndpointsFromEnv(t *testing.T) {
|
|||
},
|
||||
want: td.Map(make(map[config.Route]config.EndpointSpec), td.MapEntries{
|
||||
config.Route("readiness/redis"): td.Struct(config.EndpointSpec{}, td.StructFields{
|
||||
"Checks": td.Len(2),
|
||||
"Script": td.Len(2),
|
||||
}),
|
||||
}),
|
||||
wantErr: false,
|
||||
|
@ -132,10 +132,10 @@ func TestEndpointsFromEnv(t *testing.T) {
|
|||
},
|
||||
want: td.Map(make(map[config.Route]config.EndpointSpec), td.MapEntries{
|
||||
config.Route("readiness"): td.Struct(config.EndpointSpec{}, td.StructFields{
|
||||
"Checks": td.Len(1),
|
||||
"Script": td.Len(1),
|
||||
}),
|
||||
config.Route("liveness"): td.Struct(config.EndpointSpec{}, td.StructFields{
|
||||
"Checks": td.Len(1),
|
||||
"Script": td.Len(1),
|
||||
}),
|
||||
}),
|
||||
wantErr: false,
|
||||
|
|
1
go.mod
1
go.mod
|
@ -11,6 +11,7 @@ require (
|
|||
github.com/testcontainers/testcontainers-go v0.13.0
|
||||
go.uber.org/zap v1.21.0
|
||||
golang.org/x/exp v0.0.0-20220428152302-39d4317da171
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b
|
||||
)
|
||||
|
||||
|
|
6
go.sum
6
go.sum
|
@ -82,6 +82,7 @@ github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kd
|
|||
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
|
||||
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
|
||||
github.com/aws/aws-sdk-go v1.15.11/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0=
|
||||
github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
|
||||
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
|
||||
github.com/beorn7/perks v0.0.0-20160804104726-4c0e84591b9a/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||
|
@ -718,17 +719,15 @@ go.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M=
|
|||
go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
|
||||
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
|
||||
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
go.uber.org/atomic v1.4.0 h1:cxzIVoETapQEqDhQu3QfnvXAV4AlzcvUCxkVUFw3+EU=
|
||||
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
|
||||
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||
go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
|
||||
go.uber.org/goleak v1.1.12 h1:gZAh5/EyT/HQwlpkCy6wTpqfH9H8Lz8zbm3dZh+OyzA=
|
||||
go.uber.org/goleak v1.1.12/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
|
||||
go.uber.org/multierr v1.1.0 h1:HoEmRHQPVSqub6w2z2d2EOVs2fjyFRGyofhKuyDq0QI=
|
||||
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
||||
go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4=
|
||||
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
|
||||
go.uber.org/zap v1.10.0 h1:ORx85nbTijNz8ljznvCMR1ZBIPKFn3jQrag10X2AsuM=
|
||||
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||
go.uber.org/zap v1.21.0 h1:WefMeulhovoZ2sYXz7st6K0sLj7bBhpiFaud4r4zST8=
|
||||
go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw=
|
||||
|
@ -835,6 +834,7 @@ golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJ
|
|||
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
|
|
|
@ -1,5 +1,16 @@
|
|||
package grammar
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
var (
|
||||
_ json.Unmarshaler = (*Check)(nil)
|
||||
_ yaml.Unmarshaler = (*Check)(nil)
|
||||
)
|
||||
|
||||
type Call struct {
|
||||
Module string `parser:"(@Module'.')?"`
|
||||
Name string `parser:"@Ident"`
|
||||
|
@ -15,6 +26,34 @@ type Check struct {
|
|||
Validators *Filters `parser:"( '=>' @@)?"`
|
||||
}
|
||||
|
||||
func (c *Check) UnmarshalYAML(value *yaml.Node) error {
|
||||
parser, err := NewParser[Check]()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
chk, err := parser.Parse(value.Value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*c = *chk
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Check) UnmarshalJSON(bytes []byte) error {
|
||||
parser, err := NewParser[Check]()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
chk, err := parser.ParseBytes(bytes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*c = *chk
|
||||
return nil
|
||||
}
|
||||
|
||||
type Script struct {
|
||||
Checks []Check `parser:"(@@';'?)*"`
|
||||
}
|
||||
|
|
|
@ -49,3 +49,12 @@ func (p Parser[T]) Parse(rawRule string) (*T, error) {
|
|||
|
||||
return into, nil
|
||||
}
|
||||
|
||||
func (p Parser[T]) ParseBytes(data []byte) (*T, error) {
|
||||
into := new(T)
|
||||
if err := p.grammarParser.ParseBytes("", data, into); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return into, nil
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@ import (
|
|||
)
|
||||
|
||||
var wantParsedScript = td.Struct(new(grammar.Script), td.StructFields{
|
||||
"Checks": td.Bag(
|
||||
"Script": td.Bag(
|
||||
grammar.Check{
|
||||
Initiator: &grammar.Call{
|
||||
Module: "http",
|
||||
|
|
35
main.go
35
main.go
|
@ -1,13 +1,18 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
|
||||
"go.uber.org/zap"
|
||||
"go.uber.org/zap/zapcore"
|
||||
|
||||
"github.com/baez90/nurse/api"
|
||||
"github.com/baez90/nurse/check"
|
||||
"github.com/baez90/nurse/config"
|
||||
"github.com/baez90/nurse/protocols/redis"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -24,7 +29,7 @@ func main() {
|
|||
|
||||
logger := zap.L()
|
||||
|
||||
envCfg, err := config.New(
|
||||
nurseInstance, err := config.New(
|
||||
config.WithValuesFrom(cfg),
|
||||
config.WithConfigFile(cfgFile),
|
||||
config.WithServersFromEnv(),
|
||||
|
@ -32,11 +37,33 @@ func main() {
|
|||
)
|
||||
|
||||
if err != nil {
|
||||
logger.Error("Failed to load config from environment", zap.Error(err))
|
||||
os.Exit(1)
|
||||
logger.Fatal("Failed to load config from environment", zap.Error(err))
|
||||
}
|
||||
|
||||
logger.Debug("Loaded config", zap.Any("config", envCfg))
|
||||
logger.Debug("Loaded config", zap.Any("config", nurseInstance))
|
||||
|
||||
chkRegistry := check.NewRegistry()
|
||||
if err := chkRegistry.Register(redis.Module()); err != nil {
|
||||
logger.Fatal("Failed to register Redis module", zap.Error(err))
|
||||
}
|
||||
|
||||
srvLookup, err := nurseInstance.ServerLookup()
|
||||
if err != nil {
|
||||
logger.Fatal("Failed to prepare server lookup", zap.Error(err))
|
||||
}
|
||||
|
||||
mux, err := api.PrepareMux(nurseInstance, chkRegistry, srvLookup)
|
||||
if err != nil {
|
||||
logger.Fatal("Failed to prepare server mux", zap.Error(err))
|
||||
}
|
||||
|
||||
if err := http.ListenAndServe(":8080", mux); err != nil {
|
||||
if errors.Is(err, http.ErrServerClosed) {
|
||||
return
|
||||
}
|
||||
|
||||
logger.Fatal("Failed to serve HTTP", zap.Error(err))
|
||||
}
|
||||
}
|
||||
|
||||
func setupLogging() {
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
|
||||
func Module() *check.Module {
|
||||
m, _ := check.NewModule(
|
||||
"redis",
|
||||
check.WithCheck("ping", check.FactoryFunc(func() check.SystemChecker {
|
||||
return new(PingCheck)
|
||||
})),
|
|
@ -12,7 +12,7 @@ import (
|
|||
|
||||
"github.com/baez90/nurse/config"
|
||||
"github.com/baez90/nurse/grammar"
|
||||
"github.com/baez90/nurse/redis"
|
||||
"github.com/baez90/nurse/protocols/redis"
|
||||
)
|
||||
|
||||
func TestChecks_Execute(t *testing.T) {
|
Loading…
Reference in a new issue