113 lines
1.9 KiB
Go
113 lines
1.9 KiB
Go
|
package health
|
||
|
|
||
|
import (
|
||
|
"context"
|
||
|
"sync"
|
||
|
"time"
|
||
|
)
|
||
|
|
||
|
type Health struct {
|
||
|
liveChecks []Checker
|
||
|
readyChecks []Checker
|
||
|
maxConcurrent int
|
||
|
}
|
||
|
|
||
|
func New(opts ...Option) *Health {
|
||
|
cfg := newConfig(opts...)
|
||
|
return &Health{
|
||
|
liveChecks: cfg.liveChecks,
|
||
|
readyChecks: cfg.readyChecks,
|
||
|
maxConcurrent: cfg.maxConcurrent,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
type Status string
|
||
|
|
||
|
const (
|
||
|
StatusPass Status = "pass"
|
||
|
StatusFail Status = "fail"
|
||
|
)
|
||
|
|
||
|
func (s Status) String() string {
|
||
|
return string(s)
|
||
|
}
|
||
|
|
||
|
type SingleCheckResult struct {
|
||
|
Status Status
|
||
|
Err error
|
||
|
Time time.Time
|
||
|
}
|
||
|
|
||
|
type Result struct {
|
||
|
Status Status
|
||
|
Checks map[string][]SingleCheckResult
|
||
|
}
|
||
|
|
||
|
func (h *Health) CheckLive(ctx context.Context) Result {
|
||
|
return h.check(ctx, h.liveChecks)
|
||
|
}
|
||
|
|
||
|
func (h *Health) CheckReady(ctx context.Context) Result {
|
||
|
return h.check(ctx, h.readyChecks)
|
||
|
}
|
||
|
|
||
|
type singleCheckResultWithName struct {
|
||
|
Name string
|
||
|
SingleCheckResult
|
||
|
}
|
||
|
|
||
|
func (h *Health) check(ctx context.Context, checks []Checker) Result {
|
||
|
limiterCh := make(chan struct{}, h.maxConcurrent)
|
||
|
checkCh := make(chan singleCheckResultWithName)
|
||
|
var wg sync.WaitGroup
|
||
|
|
||
|
for _, ch := range checks {
|
||
|
wg.Add(1)
|
||
|
|
||
|
go func(ch Checker) {
|
||
|
defer wg.Done()
|
||
|
|
||
|
limiterCh <- struct{}{}
|
||
|
defer func() {
|
||
|
<-limiterCh
|
||
|
}()
|
||
|
|
||
|
check := SingleCheckResult{
|
||
|
Status: StatusPass,
|
||
|
Time: time.Now(),
|
||
|
}
|
||
|
|
||
|
if err := ch.Check(ctx); err != nil {
|
||
|
check.Status = StatusFail
|
||
|
check.Err = err
|
||
|
}
|
||
|
|
||
|
checkCh <- singleCheckResultWithName{
|
||
|
Name: ch.Name(),
|
||
|
SingleCheckResult: check,
|
||
|
}
|
||
|
}(ch)
|
||
|
}
|
||
|
|
||
|
go func() {
|
||
|
wg.Wait()
|
||
|
close(limiterCh)
|
||
|
close(checkCh)
|
||
|
}()
|
||
|
|
||
|
res := Result{
|
||
|
Status: StatusPass,
|
||
|
Checks: make(map[string][]SingleCheckResult, len(checks)),
|
||
|
}
|
||
|
|
||
|
for check := range checkCh {
|
||
|
if check.Status == StatusFail {
|
||
|
res.Status = StatusFail
|
||
|
}
|
||
|
|
||
|
res.Checks[check.Name] = append(res.Checks[check.Name], check.SingleCheckResult)
|
||
|
}
|
||
|
|
||
|
return res
|
||
|
}
|