mirror of
https://github.com/hanzo-fi/go-libs.git
synced 2026-10-30 20:53:44 +00:00
151 lines
3.0 KiB
Go
151 lines
3.0 KiB
Go
package testservice
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"io"
|
|
"strings"
|
|
|
|
"github.com/google/uuid"
|
|
"github.com/spf13/cobra"
|
|
|
|
logging "github.com/hanzo-fi/go-libs/v5/pkg/observe/log"
|
|
"github.com/hanzo-fi/go-libs/v5/pkg/service"
|
|
)
|
|
|
|
type Service struct {
|
|
BaseConfiguration
|
|
commandFactory func() *cobra.Command
|
|
cancel func()
|
|
ctx context.Context
|
|
errorChan chan error
|
|
id string
|
|
}
|
|
|
|
func (s *Service) GetID() string {
|
|
return s.id
|
|
}
|
|
|
|
func (s *Service) Start(ctx context.Context) error {
|
|
|
|
ctx = logging.ContextWithLogger(ctx, logging.Testing())
|
|
ctx = service.ContextWithLifecycle(ctx)
|
|
ctx, cancel := context.WithCancel(ctx)
|
|
|
|
runConfiguration := &RunConfiguration{
|
|
ctx: ctx,
|
|
serviceID: s.id,
|
|
}
|
|
for _, instrument := range s.Instruments {
|
|
err := instrument.Instrument(ctx, runConfiguration)
|
|
if err != nil {
|
|
cancel()
|
|
return err
|
|
}
|
|
}
|
|
|
|
s.Logger.Logf("Starting application with flags: %s", strings.Join(runConfiguration.args, " "))
|
|
cmd := s.commandFactory()
|
|
cmd.SetArgs(runConfiguration.args)
|
|
cmd.SilenceErrors = true
|
|
output := runConfiguration.output
|
|
if output == nil {
|
|
output = io.Discard
|
|
}
|
|
cmd.SetOut(output)
|
|
cmd.SetErr(output)
|
|
|
|
go func() {
|
|
s.errorChan <- cmd.ExecuteContext(runConfiguration.ctx)
|
|
}()
|
|
|
|
select {
|
|
case <-service.Ready(runConfiguration.ctx):
|
|
case err := <-s.errorChan:
|
|
cancel()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return errors.New("unexpected service stop")
|
|
}
|
|
|
|
s.ctx, s.cancel = runConfiguration.ctx, cancel
|
|
|
|
return nil
|
|
}
|
|
|
|
func (s *Service) Stop(ctx context.Context) error {
|
|
if s.cancel == nil {
|
|
return nil
|
|
}
|
|
s.cancel()
|
|
s.cancel = nil
|
|
|
|
// Wait app to be marked as stopped
|
|
select {
|
|
case <-service.Stopped(s.ctx):
|
|
case <-ctx.Done():
|
|
return errors.New("service should have been stopped")
|
|
}
|
|
|
|
// Ensure the app has been properly shutdown
|
|
select {
|
|
case err := <-s.errorChan:
|
|
return err
|
|
case <-ctx.Done():
|
|
return errors.New("service should have been stopped without error")
|
|
}
|
|
}
|
|
|
|
func (s *Service) Restart(ctx context.Context) error {
|
|
if err := s.Stop(ctx); err != nil {
|
|
return err
|
|
}
|
|
if err := s.Start(ctx); err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (s *Service) GetContext() context.Context {
|
|
return s.ctx
|
|
}
|
|
|
|
func New(commandFactory func() *cobra.Command, opts ...Option) *Service {
|
|
baseConfiguration := &BaseConfiguration{}
|
|
for _, opt := range append(defaultOptions, opts...) {
|
|
opt(baseConfiguration)
|
|
}
|
|
return &Service{
|
|
BaseConfiguration: *baseConfiguration,
|
|
commandFactory: commandFactory,
|
|
id: uuid.NewString()[:8],
|
|
errorChan: make(chan error, 1),
|
|
}
|
|
}
|
|
|
|
type BaseConfiguration struct {
|
|
Logger Logger
|
|
Instruments []Instrumentation
|
|
}
|
|
|
|
type Option func(s *BaseConfiguration)
|
|
|
|
func WithLogger(logger Logger) Option {
|
|
return func(s *BaseConfiguration) {
|
|
s.Logger = logger
|
|
}
|
|
}
|
|
|
|
func WithInstruments(instruments ...Instrumentation) Option {
|
|
return func(s *BaseConfiguration) {
|
|
s.Instruments = append(s.Instruments, instruments...)
|
|
}
|
|
}
|
|
|
|
var defaultOptions = []Option{
|
|
WithLogger(NoOpLogger),
|
|
}
|