2022-09-10 01:19:29 +02:00
|
|
|
package curre
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
2022-09-10 01:25:07 +02:00
|
|
|
"os"
|
2022-09-10 01:19:29 +02:00
|
|
|
"sync"
|
2022-09-21 22:14:22 +02:00
|
|
|
"time"
|
2022-09-10 01:19:29 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
OK = 0
|
|
|
|
Internal = 1
|
|
|
|
)
|
|
|
|
|
|
|
|
type Manager struct {
|
|
|
|
components []Component
|
|
|
|
logger Logger
|
|
|
|
startStopLock sync.Mutex
|
|
|
|
started bool
|
|
|
|
exitChan chan int
|
|
|
|
exitCode int
|
|
|
|
lifetime Lifetime
|
|
|
|
}
|
|
|
|
|
|
|
|
func NewManager() *Manager {
|
|
|
|
return &Manager{
|
2022-09-10 01:39:36 +02:00
|
|
|
logger: &DefaultLogger{},
|
|
|
|
lifetime: ConsoleLifetime,
|
|
|
|
exitChan: make(chan int, 1),
|
2022-09-10 01:19:29 +02:00
|
|
|
exitCode: OK,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-09-21 21:40:44 +02:00
|
|
|
type ComponentsAreReady struct {
|
|
|
|
}
|
|
|
|
|
2022-09-21 22:19:54 +02:00
|
|
|
type CleanupFunc func(ctx context.Context) error
|
|
|
|
|
|
|
|
func (m *Manager) RunNonBlocking(ctx context.Context, readyChan chan ComponentsAreReady) (CleanupFunc, error) {
|
2022-09-21 21:45:48 +02:00
|
|
|
go func() error {
|
|
|
|
m.initLifetime()
|
|
|
|
err := m.init(ctx)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2022-09-21 21:40:44 +02:00
|
|
|
|
2022-09-21 21:45:48 +02:00
|
|
|
err = m.startBlocking(ctx)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2022-09-21 21:40:44 +02:00
|
|
|
|
2022-09-21 21:45:48 +02:00
|
|
|
readyChan <- ComponentsAreReady{}
|
2022-09-10 01:39:36 +02:00
|
|
|
|
2022-09-21 21:45:48 +02:00
|
|
|
err = m.wait(ctx)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}()
|
2022-09-21 22:19:54 +02:00
|
|
|
|
|
|
|
return func(ctx context.Context) error {
|
|
|
|
return m.shutdown(ctx)
|
|
|
|
}, nil
|
2022-09-10 01:39:36 +02:00
|
|
|
}
|
|
|
|
|
2022-09-10 01:25:07 +02:00
|
|
|
func (m *Manager) Run(ctx context.Context) error {
|
|
|
|
m.initLifetime()
|
|
|
|
err := m.init(ctx)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
err = m.start(ctx)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
err = m.wait(ctx)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
err = m.shutdown(ctx)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2022-09-10 01:39:36 +02:00
|
|
|
os.Exit(m.exitCode)
|
2022-09-10 01:25:07 +02:00
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2022-09-10 01:19:29 +02:00
|
|
|
func (m *Manager) Register(components ...Component) *Manager {
|
|
|
|
if m.started {
|
|
|
|
panic("cannot register to a started manager")
|
|
|
|
}
|
|
|
|
|
|
|
|
m.components = append(m.components, components...)
|
|
|
|
|
|
|
|
return m
|
|
|
|
}
|
|
|
|
|
2022-09-10 01:25:07 +02:00
|
|
|
func (m *Manager) init(ctx context.Context) error {
|
2022-09-10 01:19:29 +02:00
|
|
|
if m.started {
|
|
|
|
panic("cannot reinit a started manager")
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, c := range m.components {
|
|
|
|
err := c.Init(ctx)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
m.logger.Info("Manager: Init: %T", c)
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2022-09-10 01:25:07 +02:00
|
|
|
func (m *Manager) start(ctx context.Context) error {
|
2022-09-10 01:19:29 +02:00
|
|
|
for _, c := range m.components {
|
|
|
|
go m.startComponent(ctx, c)
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2022-09-21 21:40:44 +02:00
|
|
|
func (m *Manager) startBlocking(ctx context.Context) error {
|
|
|
|
for _, c := range m.components {
|
|
|
|
m.startComponent(ctx, c)
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2022-09-10 01:19:29 +02:00
|
|
|
func (m *Manager) startComponent(ctx context.Context, component Component) {
|
|
|
|
defer func() {
|
|
|
|
err := recover()
|
|
|
|
if err != nil {
|
|
|
|
m.logger.Error("Panic occurred in component: %T, error: %s", component, err)
|
2022-09-10 01:39:36 +02:00
|
|
|
m.exitChan <- Internal
|
2022-09-10 01:19:29 +02:00
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
|
|
|
m.logger.Info("Starting %T", component)
|
|
|
|
err := component.Start(ctx)
|
|
|
|
if err != nil {
|
|
|
|
m.logger.Error("Component: %T encountered an error: ", component, err)
|
|
|
|
m.exitChan <- Internal
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
m.logger.Info("Component %T, done running", component)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *Manager) initLifetime() {
|
|
|
|
go func() {
|
|
|
|
exitCode := m.lifetime()
|
|
|
|
m.logger.Info("Exit signal received: %d", exitCode)
|
|
|
|
m.exitChan <- exitCode
|
|
|
|
}()
|
|
|
|
}
|
|
|
|
|
2022-09-10 01:25:07 +02:00
|
|
|
func (m *Manager) wait(ctx context.Context) error {
|
2022-09-21 22:14:22 +02:00
|
|
|
select {
|
|
|
|
case exitCode := <-m.exitChan:
|
|
|
|
m.exitCode = exitCode
|
|
|
|
return nil
|
|
|
|
case <-ctx.Done():
|
|
|
|
return nil
|
|
|
|
}
|
2022-09-10 01:19:29 +02:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2022-09-10 01:25:07 +02:00
|
|
|
func (m *Manager) shutdown(ctx context.Context) error {
|
2022-09-10 01:19:29 +02:00
|
|
|
shutdownChan := make(chan struct{}, 1)
|
|
|
|
closers := m.getClosers(ctx)
|
|
|
|
|
|
|
|
go func(ctx context.Context) {
|
|
|
|
for _, c := range closers {
|
2022-09-10 13:08:16 +02:00
|
|
|
c.Stop(ctx)
|
2022-09-10 01:19:29 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
shutdownChan <- struct{}{}
|
|
|
|
}(ctx)
|
|
|
|
|
|
|
|
<-shutdownChan
|
|
|
|
m.logger.Info("Shutting down of components complete")
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *Manager) getClosers(ctx context.Context) []Component {
|
|
|
|
return m.components
|
|
|
|
}
|