mirror of
https://github.com/foomo/keel.git
synced 2025-10-16 12:35:34 +00:00
128 lines
2.7 KiB
Go
128 lines
2.7 KiB
Go
package keel
|
|
|
|
import (
|
|
"context"
|
|
"sync"
|
|
"time"
|
|
|
|
"go.uber.org/zap"
|
|
|
|
"github.com/foomo/keel/log"
|
|
)
|
|
|
|
type ServiceFunc func() Service
|
|
|
|
type ServiceEnabler struct {
|
|
l *zap.Logger
|
|
ctx context.Context
|
|
name string
|
|
service Service
|
|
serviceFn ServiceFunc
|
|
syncEnabled bool
|
|
syncEnabledLock sync.RWMutex
|
|
enabledFn func() bool
|
|
syncClosed bool
|
|
syncClosedLock sync.RWMutex
|
|
}
|
|
|
|
func NewServiceEnabler(l *zap.Logger, name string, serviceFn ServiceFunc, enabledFn func() bool) *ServiceEnabler {
|
|
return &ServiceEnabler{
|
|
l: log.WithServiceName(l, name),
|
|
name: name,
|
|
serviceFn: serviceFn,
|
|
syncEnabled: enabledFn(),
|
|
enabledFn: enabledFn,
|
|
}
|
|
}
|
|
|
|
func (w *ServiceEnabler) Name() string {
|
|
return w.name
|
|
}
|
|
|
|
func (w *ServiceEnabler) Start(ctx context.Context) error {
|
|
w.ctx = ctx
|
|
w.watch(w.ctx) //nolint:contextcheck
|
|
if w.enabled() {
|
|
if err := w.enable(w.ctx); err != nil { //nolint:contextcheck
|
|
return err
|
|
}
|
|
} else {
|
|
w.l.Info("skipping disabled dynamic service")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (w *ServiceEnabler) Close(ctx context.Context) error {
|
|
l := log.WithServiceName(w.l, w.Name())
|
|
w.setClosed(true)
|
|
if w.enabled() {
|
|
if err := w.disable(w.ctx); err != nil { //nolint:contextcheck
|
|
return err
|
|
}
|
|
} else {
|
|
l.Info("skipping disabled dynamic service")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (w *ServiceEnabler) closed() bool {
|
|
w.syncClosedLock.RLock()
|
|
defer w.syncClosedLock.RUnlock()
|
|
return w.syncClosed
|
|
}
|
|
|
|
func (w *ServiceEnabler) setClosed(v bool) {
|
|
w.syncClosedLock.Lock()
|
|
defer w.syncClosedLock.Unlock()
|
|
w.syncClosed = v
|
|
}
|
|
|
|
func (w *ServiceEnabler) enabled() bool {
|
|
w.syncEnabledLock.RLock()
|
|
defer w.syncEnabledLock.RUnlock()
|
|
return w.syncEnabled
|
|
}
|
|
|
|
func (w *ServiceEnabler) setEnabled(v bool) {
|
|
w.syncEnabledLock.Lock()
|
|
defer w.syncEnabledLock.Unlock()
|
|
w.syncEnabled = v
|
|
}
|
|
|
|
func (w *ServiceEnabler) enable(ctx context.Context) error {
|
|
w.setEnabled(true)
|
|
w.service = w.serviceFn()
|
|
w.l.Info("starting dynamic service")
|
|
return w.service.Start(ctx)
|
|
}
|
|
|
|
func (w *ServiceEnabler) disable(ctx context.Context) error {
|
|
w.setEnabled(false)
|
|
w.l.Info("stopping dynamic service")
|
|
return w.service.Close(ctx)
|
|
}
|
|
|
|
func (w *ServiceEnabler) watch(ctx context.Context) {
|
|
go func() {
|
|
for {
|
|
time.Sleep(time.Second)
|
|
if w.closed() {
|
|
break
|
|
}
|
|
if value := w.enabledFn(); value != w.enabled() {
|
|
if value {
|
|
go func() {
|
|
if err := w.enable(ctx); err != nil {
|
|
w.l.Fatal("failed to dynamically start service", log.FError(err))
|
|
}
|
|
}()
|
|
} else {
|
|
if err := w.disable(context.TODO()); err != nil { //nolint:contextcheck
|
|
w.l.Fatal("failed to dynamically close service", log.FError(err))
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}()
|
|
}
|