73 lines
1.4 KiB
Go
73 lines
1.4 KiB
Go
package lifecycle
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"reflect"
|
|
)
|
|
|
|
var (
|
|
ErrInvalidGroup = errors.New("invalid lifecycle group")
|
|
ErrRunnerStopped = errors.New("lifecycle runner stopped")
|
|
)
|
|
|
|
type Runner interface {
|
|
Run(context.Context) error
|
|
}
|
|
|
|
type Group struct {
|
|
runners []Runner
|
|
}
|
|
|
|
func NewGroup(runners ...Runner) (*Group, error) {
|
|
if len(runners) == 0 {
|
|
return nil, ErrInvalidGroup
|
|
}
|
|
owned := make([]Runner, len(runners))
|
|
for index, runner := range runners {
|
|
if isNilRunner(runner) {
|
|
return nil, ErrInvalidGroup
|
|
}
|
|
owned[index] = runner
|
|
}
|
|
return &Group{runners: owned}, nil
|
|
}
|
|
|
|
func (group *Group) Run(ctx context.Context) error {
|
|
if group == nil || ctx == nil || len(group.runners) == 0 {
|
|
return ErrInvalidGroup
|
|
}
|
|
groupCtx, cancel := context.WithCancel(ctx)
|
|
defer cancel()
|
|
done := make(chan error, len(group.runners))
|
|
for _, runner := range group.runners {
|
|
go func() { done <- runner.Run(groupCtx) }()
|
|
}
|
|
|
|
first := <-done
|
|
cancel()
|
|
for range len(group.runners) - 1 {
|
|
<-done
|
|
}
|
|
if ctx.Err() != nil {
|
|
return ctx.Err()
|
|
}
|
|
if first == nil {
|
|
return ErrRunnerStopped
|
|
}
|
|
return first
|
|
}
|
|
|
|
func isNilRunner(runner Runner) bool {
|
|
if runner == nil {
|
|
return true
|
|
}
|
|
reflected := reflect.ValueOf(runner)
|
|
switch reflected.Kind() {
|
|
case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Pointer, reflect.Slice:
|
|
return reflected.IsNil()
|
|
default:
|
|
return false
|
|
}
|
|
}
|