- Go 99.1%
- Dockerfile 0.9%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
|
|
||
| .devcontainer | ||
| .github/workflows | ||
| debian | ||
| examples | ||
| hooks | ||
| httpserver | ||
| .dockerignore | ||
| .golangci.yml | ||
| .markdownlint.json | ||
| Dockerfile | ||
| errors.go | ||
| errors_test.go | ||
| errorsfanin.go | ||
| errorsfanin_test.go | ||
| go.mod | ||
| go.sum | ||
| group.go | ||
| group_settings.go | ||
| group_settings_test.go | ||
| group_test.go | ||
| helpers.go | ||
| helpers_test.go | ||
| hooks.go | ||
| LICENSE | ||
| mocks_generate_test.go | ||
| mocks_test.go | ||
| README.md | ||
| restarter.go | ||
| restarter_settings.go | ||
| restarter_settings_test.go | ||
| restarter_test.go | ||
| runwrapper.go | ||
| runwrapper_integration_test.go | ||
| runwrapper_test.go | ||
| sequence.go | ||
| sequence_settings.go | ||
| sequence_settings_test.go | ||
| sequence_test.go | ||
| service.go | ||
| settings_helpers.go | ||
| settings_helpers_test.go | ||
| state.go | ||
| state_test.go | ||
Go services
goservices is a Go package to help manage services.
🚧 Logo to be added 🚧
What is a service? It's this interface currently:
type Service interface {
// String returns the service name.
// It is assumed to be constant over the lifetime of the service.
String() string
// Start starts the service.
// On success, it returns a run error channel and a nil error.
// On failure, it returns a nil run error channel and an error.
// If the service crashes, only one single error should be sent in
// the error channel.
// When the service is stopped, the service should NOT send an error
// in the run error channel or close this one.
// Start takes in a context and the implementation should promptly return
// the context error wrapped in `startErr` if the context is canceled.
Start(ctx context.Context) (runError <-chan error, startErr error)
// Stops stops the service.
// A service should NOT close or write an error to its run error channel
// if it is stopped.
Stop() (err error)
}
Stability
- the code is fully test covered
- Zero dependency (except for tests with
golang/mockandstretchr/testify) - - the Go API should be stable until a v1.0.0 release
- the Go API will be guaranteed stable from the v1.0.0 release
- the code is linted with
golangci-lintand a lot of linters - There is a CI pipeline to test, lint, check mocks and check documentation on every commit.
Sequence of services
To start and stop a sequence of services, you can use the Sequence type.
Note it itself implements the Service interface, so you can nest it with other service management types, like Group.
ctx := context.Background()
settings := goservices.SequenceSettings{
ServicesStart: []goservices.Service{serviceA, serviceB},
ServicesStop: []goservices.Service{serviceB, serviceA},
}
sequence, err := goservices.NewSequence(settings)
if err != nil {
return fmt.Errorf("creating services sequence: %w", err)
}
runError, err := sequence.Start(ctx)
if err != nil {
return fmt.Errorf("starting services sequence: %w", err)
}
select {
case err = <-runError:
return fmt.Errorf("services sequence crashed: %w", err)
case <-ctx.Done():
err = sequence.Stop()
if err != nil {
return fmt.Errorf("stopping services sequence: %w", err)
}
return nil
}
Group of services
To start and stop a group of services all in parallel, you can use the Group type.
Note it itself implements the Service interface, so you can nest it with other service management types, like Sequence.
A simplistic example would be:
ctx := context.Background()
settings := goservices.GroupSettings{
Services: []goservices.Service{serviceA, serviceB},
}
group, err := goservices.NewGroup(settings)
if err != nil {
return fmt.Errorf("creating services group: %w", err)
}
runError, err := group.Start(ctx)
if err != nil {
return fmt.Errorf("starting services group: %w", err)
}
select {
case err = <-runError:
return fmt.Errorf("services group crashed: %w", err)
case <-ctx.Done():
err = group.Stop()
if err != nil {
return fmt.Errorf("stopping services group: %w", err)
}
return nil
}
Auto-restart a service
To automatically restart a service when it crashes, you can use the Restarter type.
Note it itself implements the Service interface, so you can nest it with other service management types, like Sequence.
ctx := context.Background()
settings := goservices.RestarterSettings{
Service: serviceToRestart,
}
restarter, err := goservices.NewRestarter(settings)
if err != nil {
return fmt.Errorf("creating restarter: %w", err)
}
runError, startErr := restarter.Start(ctx)
if startErr != nil {
return fmt.Errorf("starting restarter: %w", startErr)
}
select {
case err = <-runError:
return fmt.Errorf("restarter crashed: %w", err)
case <-ctx.Done():
err = restarter.Stop()
if err != nil {
return fmt.Errorf("stopping restarter: %w", err)
}
return nil
}
Create a service
You can implement yourself the interface.
HOWEVER this is tedious to get right especially with the many race conditions possible (i.e. what if the service crashes at the same time as it is stopped?).
This is why this library provides a RunWrapper which creates a service from a RunFunction:
type RunFunction func(ctx context.Context,
ready chan<- struct{}, runError, stopError chan<- error)
Please see the documentation of the RunFunction to know the details on how to implement it correctly.
A concrete example is the httpserver service which is implemented using this RunWrapper.
Pre-built services
This library provides a few pre-built services: