controls

package
v1.10.5 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 2, 2026 License: MIT Imports: 9 Imported by: 0

README

Controls

Service orchestration and lifecycle management for long-running processes.

Provides:

  • Controller for managing background services
  • Lifecycle hooks for startup and shutdown
  • Standard interfaces for cancellable tasks

For detailed documentation and implementation patterns, see the Controls Component Documentation.

Documentation

Overview

Package controls provides a lifecycle controller for managing concurrent, long-running services such as HTTP servers, background workers, and schedulers.

The Controller orchestrates startup, health monitoring, and graceful shutdown with proper cleanup ordering. Shared communication channels carry errors, OS signals, and control messages between registered services.

The Controllable interface abstracts the controller for testing, exposing Start, Stop, IsRunning, IsStopped, IsStopping, Register, and configuration methods. Concrete transports are provided by the grpc and http sub-packages.

Index

Examples

Constants

View Source
const DefaultShutdownTimeout = 5 * time.Second

DefaultShutdownTimeout is the time allowed for graceful shutdown before services are force-stopped.

Variables

View Source
var ErrShutdown = errors.New("controller shutdown")

ErrShutdown is the cause attached to the controller context when a graceful shutdown is initiated. Callers can distinguish a controlled stop from an upstream cancellation via context.Cause(ctx) == controls.ErrShutdown.

Functions

This section is empty.

Types

type ChannelProvider

type ChannelProvider interface {
	Messages() chan Message
	Health() chan HealthMessage
	Errors() chan error
	Signals() chan os.Signal
}

ChannelProvider provides access to controller channels.

type CheckResult added in v1.8.0

type CheckResult struct {
	// Status is the health status.
	Status CheckStatus
	// Message provides human-readable detail about the check result.
	Message string
	// Timestamp is when this result was produced.
	Timestamp time.Time
}

CheckResult represents the outcome of a health check.

type CheckStatus added in v1.8.0

type CheckStatus int

CheckStatus represents the health state of a check.

const (
	// CheckHealthy indicates the check passed.
	CheckHealthy CheckStatus = iota
	// CheckDegraded indicates the check passed but with warnings.
	// Maps to OverallHealthy: true with Status: "DEGRADED".
	CheckDegraded
	// CheckUnhealthy indicates the check failed.
	// Maps to OverallHealthy: false with Status: "ERROR".
	CheckUnhealthy
)

type CheckType added in v1.8.0

type CheckType int

CheckType determines which health endpoint(s) a check contributes to.

const (
	// CheckTypeReadiness contributes to the readiness endpoint.
	CheckTypeReadiness CheckType = iota
	// CheckTypeLiveness contributes to the liveness endpoint.
	CheckTypeLiveness
	// CheckTypeBoth contributes to both liveness and readiness endpoints.
	CheckTypeBoth
)

type Configurable

type Configurable interface {
	SetErrorsChannel(errs chan error)
	SetMessageChannel(control chan Message)
	SetSignalsChannel(sigs chan os.Signal)
	SetHealthChannel(health chan HealthMessage)
	SetWaitGroup(wg *sync.WaitGroup)
	SetShutdownTimeout(d time.Duration)
	SetLogger(l logger.Logger)
}

Configurable provides controller configuration setters.

type Controllable

Controllable is the full controller interface, composed of all role-based interfaces. Prefer using the narrower interfaces (Runner, HealthReporter, Configurable, etc.) where possible.

type Controller

type Controller struct {
	// contains filtered or unexported fields
}

Controller orchestrates the lifecycle of registered services: startup ordering, health monitoring, graceful shutdown, and signal handling.

func NewController

func NewController(ctx context.Context, opts ...ControllerOpt) *Controller

NewController creates a Controller with the given context and options. By default, it registers SIGINT and SIGTERM handlers for graceful shutdown. Use WithoutSignals to disable signal handling (useful in tests).

Example
package main

import (
	"context"
	"fmt"
	"time"

	"github.com/phpboyscout/go-tool-base/pkg/controls"
)

func main() {
	ctx := context.Background()

	// Create a controller (WithoutSignals for non-daemon usage)
	controller := controls.NewController(ctx, controls.WithoutSignals())

	// Register an HTTP service
	controller.Register("http-api",
		controls.WithStart(func(ctx context.Context) error {
			fmt.Println("HTTP server starting")
			return nil
		}),
		controls.WithStop(func(ctx context.Context) {
			fmt.Println("HTTP server stopping")
		}),
		controls.WithStatus(func() error {
			return nil // healthy
		}),
	)

	// Start all services
	controller.Start()

	// Graceful shutdown
	time.Sleep(10 * time.Millisecond)
	controller.Stop()
	controller.Wait()
}

func (*Controller) Errors

func (c *Controller) Errors() chan error

func (*Controller) GetCheckResult added in v1.8.0

func (c *Controller) GetCheckResult(name string) (CheckResult, bool)

GetCheckResult returns the latest result for a named health check.

func (*Controller) GetContext

func (c *Controller) GetContext() context.Context

func (*Controller) GetLogger

func (c *Controller) GetLogger() logger.Logger

func (*Controller) GetServiceInfo added in v1.6.0

func (c *Controller) GetServiceInfo(name string) (ServiceInfo, bool)

GetServiceInfo returns the runtime information and statistics for a specific service.

func (*Controller) GetState

func (c *Controller) GetState() State

func (*Controller) Health

func (c *Controller) Health() chan HealthMessage

func (*Controller) IsRunning

func (c *Controller) IsRunning() bool

func (*Controller) IsStopped

func (c *Controller) IsStopped() bool

func (*Controller) IsStopping

func (c *Controller) IsStopping() bool

func (*Controller) Liveness added in v1.6.0

func (c *Controller) Liveness() HealthReport

Liveness returns an aggregate liveness report for all registered services and health checks.

func (*Controller) Messages

func (c *Controller) Messages() chan Message

func (*Controller) Readiness added in v1.6.0

func (c *Controller) Readiness() HealthReport

Readiness returns an aggregate readiness report for all registered services and health checks.

func (*Controller) Register

func (c *Controller) Register(id string, opts ...ServiceOption)

func (*Controller) RegisterHealthCheck added in v1.8.0

func (c *Controller) RegisterHealthCheck(check HealthCheck) error

RegisterHealthCheck adds a standalone health check to the controller. Must be called before Start(). The check name must be unique across both services and health checks.

func (*Controller) SetErrorsChannel

func (c *Controller) SetErrorsChannel(errs chan error)

func (*Controller) SetHealthChannel

func (c *Controller) SetHealthChannel(health chan HealthMessage)

func (*Controller) SetLogger

func (c *Controller) SetLogger(l logger.Logger)

func (*Controller) SetMessageChannel

func (c *Controller) SetMessageChannel(messages chan Message)

func (*Controller) SetShutdownTimeout

func (c *Controller) SetShutdownTimeout(d time.Duration)

func (*Controller) SetSignalsChannel

func (c *Controller) SetSignalsChannel(signals chan os.Signal)

func (*Controller) SetState

func (c *Controller) SetState(state State)

func (*Controller) SetWaitGroup

func (c *Controller) SetWaitGroup(wg *sync.WaitGroup)

func (*Controller) Signals

func (c *Controller) Signals() chan os.Signal

func (*Controller) Start

func (c *Controller) Start()

Start launches all registered services. Duplicate calls while already running are no-ops.

func (*Controller) Status added in v1.6.0

func (c *Controller) Status() HealthReport

Status returns an aggregate health report for all registered services and health checks.

func (*Controller) Stop

func (c *Controller) Stop()

Stop initiates a graceful shutdown. Duplicate calls while already stopping or stopped are safely ignored.

func (*Controller) Wait

func (c *Controller) Wait()

func (*Controller) WaitGroup

func (c *Controller) WaitGroup() *sync.WaitGroup

type ControllerOpt

type ControllerOpt func(Configurable)

ControllerOpt is a functional option for configuring a Controller.

func WithLogger

func WithLogger(l logger.Logger) ControllerOpt

WithLogger sets the controller logger.

func WithShutdownTimeout

func WithShutdownTimeout(d time.Duration) ControllerOpt

WithShutdownTimeout sets the graceful shutdown timeout.

func WithoutSignals

func WithoutSignals() ControllerOpt

WithoutSignals disables OS signal handling.

type HealthCheck added in v1.8.0

type HealthCheck struct {
	// Name is the unique identifier for this check.
	Name string
	// Check is the function that performs the health check.
	// It receives a context with the check's timeout applied.
	Check func(ctx context.Context) CheckResult
	// Timeout is the maximum duration for a single check execution.
	// Default: 5s.
	Timeout time.Duration
	// Interval is the polling interval for async checks.
	// Zero means synchronous (run on every health request).
	Interval time.Duration
	// Type determines which health endpoints this check feeds into.
	// Default: CheckTypeReadiness.
	Type CheckType
}

HealthCheck defines a named health check function.

type HealthCheckReporter added in v1.8.0

type HealthCheckReporter interface {
	HealthReporter
	// GetCheckResult returns the latest result for a named health check.
	GetCheckResult(name string) (CheckResult, bool)
}

HealthCheckReporter extends HealthReporter with check-specific queries.

type HealthMessage

type HealthMessage struct {
	Host    string `json:"host"`
	Port    int    `json:"port"`
	Status  int    `json:"status"`
	Message string `json:"message"`
}

HealthMessage is the JSON response body for HTTP health endpoints.

type HealthReport added in v1.6.0

type HealthReport struct {
	OverallHealthy bool            `json:"overall_healthy"`
	Services       []ServiceStatus `json:"services"`
}

HealthReport is the aggregate health status across all registered services.

type HealthReporter added in v1.6.2

type HealthReporter interface {
	Status() HealthReport
	Liveness() HealthReport
	Readiness() HealthReport
	GetServiceInfo(name string) (ServiceInfo, bool)
}

HealthReporter provides read access to service health, liveness, and readiness reports, and to per-service runtime information. Handlers and transports that only need to query health should depend on this interface rather than the full Controllable.

type Message

type Message string

Message represents a control message sent to the controller (e.g. "stop").

const (
	Stop   Message = "stop"
	Status Message = "status"
)

type ProbeFunc added in v1.6.0

type ProbeFunc func() error

ProbeFunc is a health check function for liveness or readiness probes.

type RestartPolicy added in v1.6.0

type RestartPolicy struct {
	MaxRestarts            int
	InitialBackoff         time.Duration
	MaxBackoff             time.Duration
	HealthFailureThreshold int
	HealthCheckInterval    time.Duration
}

RestartPolicy defines how a service should be restarted on failure.

type Runner

type Runner interface {
	Start()
	Stop()
	IsRunning() bool
	IsStopped() bool
	IsStopping() bool
	Register(id string, opts ...ServiceOption)
}

Runner provides service lifecycle operations.

type Service

type Service struct {
	Name          string
	Start         StartFunc
	Stop          StopFunc
	Status        StatusFunc
	Liveness      ProbeFunc
	Readiness     ProbeFunc
	RestartPolicy *RestartPolicy
}

Service represents a managed background service with start/stop lifecycle, health probes, and optional restart policy.

type ServiceInfo added in v1.6.0

type ServiceInfo struct {
	Name         string
	RestartCount int
	LastStarted  time.Time
	LastStopped  time.Time
	Error        error
}

ServiceInfo holds runtime metadata about a registered service.

type ServiceOption

type ServiceOption func(*Service)

ServiceOption is a functional option for configuring a Service.

func WithLiveness added in v1.6.0

func WithLiveness(fn ProbeFunc) ServiceOption

WithLiveness sets a liveness probe for the service.

Example
package main

import (
	"context"
	"net/http"

	"github.com/phpboyscout/go-tool-base/pkg/controls"
)

func main() {
	controller := controls.NewController(context.Background(), controls.WithoutSignals())

	controller.Register("api",
		controls.WithStart(func(ctx context.Context) error { return nil }),
		controls.WithLiveness(func() error {
			// Check if the service can respond
			resp, err := http.Get("http://localhost:8080/healthz")
			if err != nil {
				return err
			}

			_ = resp.Body.Close()

			return nil
		}),
	)

	_ = controller
}

func WithReadiness added in v1.6.0

func WithReadiness(fn ProbeFunc) ServiceOption

WithReadiness sets a readiness probe for the service.

func WithRestartPolicy added in v1.6.0

func WithRestartPolicy(policy RestartPolicy) ServiceOption

WithRestartPolicy configures automatic restart behaviour for a service.

Example
package main

import (
	"context"
	"time"

	"github.com/phpboyscout/go-tool-base/pkg/controls"
)

func main() {
	controller := controls.NewController(context.Background(), controls.WithoutSignals())

	controller.Register("worker",
		controls.WithStart(func(ctx context.Context) error {
			return nil
		}),
		controls.WithRestartPolicy(controls.RestartPolicy{
			MaxRestarts:    3,
			InitialBackoff: time.Second,
			MaxBackoff:     30 * time.Second,
		}),
	)

	_ = controller
}

func WithStart

func WithStart(fn StartFunc) ServiceOption

WithStart sets the service's start function.

func WithStatus

func WithStatus(fn StatusFunc) ServiceOption

WithStatus sets the service's health check function.

func WithStop

func WithStop(fn StopFunc) ServiceOption

WithStop sets the service's graceful shutdown function.

type ServiceStatus added in v1.6.0

type ServiceStatus struct {
	Name   string `json:"name"`
	Status string `json:"status"` // "OK", "ERROR"
	Error  string `json:"error,omitempty"`
}

ServiceStatus is the health status of a single service, used in HealthReport.

type Services

type Services struct {
	// contains filtered or unexported fields
}

Services manages the collection of registered services and their lifecycle.

type StartFunc

type StartFunc func(context.Context) error

StartFunc is the callback invoked to start a service. It receives a context that is cancelled when the controller shuts down.

type State

type State string

State represents the lifecycle state of the controller (Created, Starting, Running, Stopped).

const (
	Unknown  State = "unknown"
	Running  State = "running"
	Stopping State = "stopping"
	Stopped  State = "stopped"
)

type StateAccessor

type StateAccessor interface {
	GetState() State
	SetState(state State)
	GetContext() context.Context
	GetLogger() logger.Logger
}

StateAccessor provides read access to controller state and context.

type StatusFunc

type StatusFunc func() error

StatusFunc is the callback invoked to check a service's health. Returns nil if healthy, an error otherwise.

type StopFunc

type StopFunc func(context.Context)

StopFunc is the callback invoked to stop a service gracefully. The context carries the shutdown timeout.

type ValidErrorFunc

type ValidErrorFunc func(error) bool

ValidErrorFunc determines whether an error from a service is expected (e.g. http.ErrServerClosed) and should not trigger a restart.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL