api/internal/endpoints/endpoint_manager.go
Peter Kurfer 9236a38be0 Moved endpoint handling to new module
- introduce new endpoints module
- introduce Endpoint and EndpointManager
- introduce new Logging abstraction API to allow proper mocking
- add error return value to Start and Shutdown of endpoints
- add mocks of some internals to allow easier testing
- add generate target to take care of all code generation
2020-04-14 00:15:33 +02:00

130 lines
3.3 KiB
Go

package endpoints
import (
"fmt"
"github.com/baez90/inetmock/internal/config"
"github.com/baez90/inetmock/internal/plugins"
"github.com/baez90/inetmock/pkg/logging"
"go.uber.org/zap"
"sync"
"time"
)
type EndpointManager interface {
RegisteredEndpoints() []Endpoint
StartedEndpoints() []Endpoint
CreateEndpoint(name string, multiHandlerConfig config.MultiHandlerConfig) error
StartEndpoints()
ShutdownEndpoints()
}
func NewEndpointManager(logger logging.Logger) EndpointManager {
return &endpointManager{
logger: logger,
registry: plugins.Registry(),
}
}
type endpointManager struct {
logger logging.Logger
registeredEndpoints []Endpoint
properlyStartedEndpoints []Endpoint
registry plugins.HandlerRegistry
}
func (e endpointManager) RegisteredEndpoints() []Endpoint {
return e.registeredEndpoints
}
func (e endpointManager) StartedEndpoints() []Endpoint {
return e.properlyStartedEndpoints
}
func (e *endpointManager) CreateEndpoint(name string, multiHandlerConfig config.MultiHandlerConfig) (err error) {
if handler, ok := e.registry.HandlerForName(multiHandlerConfig.HandlerName()); ok {
for _, handlerConfig := range multiHandlerConfig.HandlerConfigs() {
e.registeredEndpoints = append(e.registeredEndpoints, &endpoint{
name: name,
handler: handler,
config: handlerConfig,
})
}
} else {
err = fmt.Errorf("no matching handler registered for name %s", multiHandlerConfig.HandlerName())
}
return
}
func (e *endpointManager) StartEndpoints() {
startTime := time.Now()
for _, endpoint := range e.registeredEndpoints {
endpointLogger := e.logger.With(
zap.String("endpoint", endpoint.Name()),
)
endpointLogger.Info("Starting endpoint")
if ok := startEndpoint(endpoint, endpointLogger); ok {
e.properlyStartedEndpoints = append(e.properlyStartedEndpoints, endpoint)
endpointLogger.Info("successfully started endpoint")
} else {
endpointLogger.Error("error occurred during endpoint startup - will be skipped for now")
}
}
endpointStartupDuration := time.Since(startTime)
e.logger.Info(
"Startup of all endpoints completed",
zap.Duration("startupTime", endpointStartupDuration),
)
}
func (e *endpointManager) ShutdownEndpoints() {
var waitGroup sync.WaitGroup
waitGroup.Add(len(e.properlyStartedEndpoints))
for _, endpoint := range e.properlyStartedEndpoints {
endpointLogger := e.logger.With(
zap.String("endpoint", endpoint.Name()),
)
endpointLogger.Info("Triggering shutdown of endpoint")
go shutdownEndpoint(endpoint, endpointLogger, &waitGroup)
}
waitGroup.Wait()
}
func startEndpoint(ep Endpoint, logger logging.Logger) (success bool) {
defer func() {
if r := recover(); r != nil {
logger.Fatal(
"recovered panic during startup of endpoint",
zap.Any("recovered", r),
)
}
}()
if err := ep.Start(); err != nil {
logger.Error(
"failed to start endpoint",
zap.Error(err),
)
} else {
success = true
}
return
}
func shutdownEndpoint(ep Endpoint, logger logging.Logger, wg *sync.WaitGroup) {
defer func() {
if r := recover(); r != nil {
logger.Fatal(
"recovered panic during shutdown of endpoint",
zap.Any("recovered", r),
)
}
wg.Done()
}()
if err := ep.Shutdown(); err != nil {
logger.Error(
"Failed to shutdown endpoint",
zap.Error(err),
)
}
}