Documentation ¶
Overview ¶
A framework for component lifecycle orchestration.
Each "component" is a subroutine with its own lifecycle. Components may depend on other components, which are initialized in topological order. If multiple components depend on the same component (using the component name as the equivalence class), the same instance of the component is used.
Concepts ¶
`Args` is a runtime value used to customize a component for the caller's needs. It is mainly used for two purposes: (1) Distinguish between multiple instances of the same component type, e.g. two Kubernetes client sets connecting to two different clusters would be specified by a `ClusterName` field in the Args, which is included as part of its component name (thus the `name` function can accept `Args` as its argument). (2) Provide custom plugin implementations in the startup script, e.g. the `tracing.Observer` component is requested by specifying all observers implementations in Args in the main entrypoint, and just requested from other components without specifying the observer implementations; for this to work properly, the implementation-providing component request must precede all (direct and transitive) dependents of the component and still resolve to the same component name.
`Options` is a type that stores the data for the flags requested by each component. The `optionsFn` function registers flags into a given FlagSet, which are added to the global FlagSet using the component name as the prefix.
`Deps` is a type that stores the handles (`component.Dep[Api]`) for the dependency components it requested.
`State` stores the (possibly mutable) runtime states for the component. Data only need to be stored in `State` if they are to be used for lifecycle or component interactions.
`Api` is an external interface for dependents to interact with a dependency component. The actual value of `Api` should be a simple wrapper around the internal data (including the types above) that exposes abstract capabilities to downstream components.
The `init` function registers inter-component connections to prepare fthem for initialization. Since it is the first stage in the lifecycle that executes business logic, it is also the phase that constructs the `State`. A component `Api` is only considered fully usable after init is called. `init` is called in topological order of dependency, so `Api` of dependencies is considered fully usable in the init function as well.
Index ¶
- Variables
- func ApiFromMap[Api any](apiMap ApiMap, name string) Api
- func ApiOnly[Api any](name string, api Api) func(*DepRequests)
- func RequireDep[Api any](base Declared[Api]) func(*DepRequests)
- func RequireDeps(deps ...func(*DepRequests)) func(*DepRequests)
- type ApiMap
- type Component
- type Data
- type Declared
- type DeclaredCtor
- type Dep
- type DepRequests
- type HealthChecks
- type Lifecycle
- type NamedComponent
Constants ¶
This section is empty.
Variables ¶
var ErrRecursiveDependencies = errors.TagErrorf(
"RecursiveDependencies",
"recursive dependency chain",
)
Functions ¶
func ApiFromMap ¶
Retrieves the interface to interact with a component of a known name.
func ApiOnly ¶
func ApiOnly[Api any](name string, api Api) func(*DepRequests)
Provides a named component with a different implementation of its API. Used for mocking components in integration tests.
func RequireDep ¶
func RequireDep[Api any](base Declared[Api]) func(*DepRequests)
Returns a closure that can be passed to `cmd.Run`.
func RequireDeps ¶
func RequireDeps(deps ...func(*DepRequests)) func(*DepRequests)
Merges multiple `RequireDep` results into one.
Types ¶
type ApiMap ¶
Accessor to interact with components by name. Use ApiFromMap to get the actual interfaces.
func NamedComponentsToApiMap ¶
func NamedComponentsToApiMap(components []NamedComponent) ApiMap
Converts a NamedComponent slice to an ApiMap.
type Component ¶
type Component interface { // Registers flags for this component. AddFlags(fs *flag.FlagSet) // Registers inter-component connections. // // Should not interact with the environment or start any background tasks. // The context may be removed in the future; background tasks should use the context from Start instead. Init(ctx context.Context) error // Starts the actual work. Start(ctx context.Context) error // Waits for the component to shut down gracefully. // Should return when ctx expires. Join(ctx context.Context) error // Registers health check handlers. RegisterHealthChecks(handler *healthz.Handler, onFail func(name string, err error)) // contains filtered or unexported methods }
Controls the lifecycle of a component.
This interface is only useful for lifecycle orchestration and should not be implemented by other packages.
type Data ¶
type Data[Args any, Options any, Deps any, State any] struct { // Runtime arguments for this component, // typically used to differentiate between multiple instances of the same type. Args Args // Resolved flags for the component. // // Initialized after newOptions is called. Options Options // Dependency handles for the component. // // Initialized after newDeps is called. Deps Deps // Runtime states for the component. // // Initialized after init is called. State *State }
Stores various data related to a component.
type DeclaredCtor ¶
func Declare ¶
func Declare[Args any, Options any, Deps any, State any, Api any]( name func(args Args) string, newOptions func(args Args, fs *flag.FlagSet) Options, newDeps func(args Args, requests *DepRequests) Deps, init func(ctx context.Context, args Args, options Options, deps Deps) (*State, error), lifecycle Lifecycle[Args, Options, Deps, State], api func(d *Data[Args, Options, Deps, State]) Api, ) DeclaredCtor[Args, Deps, Api]
Declares a generic component. Returns a constructor for Declared that actually instantiates the component request.
This function is typically used to assign a global variable to act like a function:
var New = component.Declare(...)
Refer to package documentation for the description of the arguments.
func (DeclaredCtor[Args, Deps, Api]) WithMergeFn ¶
func (ctor DeclaredCtor[Args, Deps, Api]) WithMergeFn( onMerge func(*Args, *Deps, *DepRequests), ) DeclaredCtor[Args, Deps, Api]
type Dep ¶
type Dep[Api any] interface { // Returns the interface to work with a component. // // The return value may differ before startup completion. // Do not use the result of .Get() called during init in the main lifecycle. Get() Api }
A dependency handle.
type DepRequests ¶
type DepRequests struct {
// contains filtered or unexported fields
}
A registry of dependencies requested by components.
type HealthChecks ¶
A map of health checks, where each non-nil function returns nil for ready status and returns a non-nil error for unready status.
type Lifecycle ¶
type Lifecycle[Args any, Options any, Deps any, State any] struct { // Starts the background tasks of a component. // // `ctx` is canceled when the process starts terminating. // All public fields in `state` are available for use. Start func(ctx context.Context, args *Args, options *Options, deps *Deps, state *State) error // Waits for the component to shut down gracefully. // // `ctx` is canceled after graceful shutdown times out. // All public fields in `state` are available for use. Join func(ctx context.Context, args *Args, options *Options, deps *Deps, state *State) error // Defines health checks for this component. HealthChecks func(*State) HealthChecks }
Lifecycle hooks for a component.
The zero value (nil functions) is a valid default.
type NamedComponent ¶
type NamedComponent struct { Component Component Name string // contains filtered or unexported fields }
Exposes the lifecycle and interaction interface of a component, used for component orchestration.
func ResolveList ¶
func ResolveList(requestFns []func(*DepRequests)) []NamedComponent
Returns a slice of components with all dependencies resolved and in initialization order.