2023-12-16 07:12:20 +00:00
|
|
|
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 {
|
2023-12-20 07:03:03 +00:00
|
|
|
name string
|
2023-12-16 07:12:20 +00:00
|
|
|
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{
|
2023-12-20 07:03:03 +00:00
|
|
|
name: ch.Name(),
|
2023-12-16 07:12:20 +00:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2023-12-20 07:03:03 +00:00
|
|
|
res.Checks[check.name] = append(res.Checks[check.name], check.SingleCheckResult)
|
2023-12-16 07:12:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return res
|
|
|
|
}
|