Documentation
¶
Overview ¶
Package service provides platform-agnostic service management actions.
Index ¶
- Constants
- type Provider
- func (p *Provider) CompensateDisable(activationRecord *op.ActivationRecord, receipt *Receipt) error
- func (p *Provider) CompensateEnable(activationRecord *op.ActivationRecord, receipt *Receipt) error
- func (p *Provider) CompensateRestart(activationRecord *op.ActivationRecord, _ *Receipt) error
- func (p *Provider) CompensateStart(activationRecord *op.ActivationRecord, receipt *Receipt) error
- func (p *Provider) CompensateStop(activationRecord *op.ActivationRecord, receipt *Receipt) error
- func (p *Provider) Disable(activationRecord *op.ActivationRecord, name *Resource) (*Resource, *Receipt, error)
- func (p *Provider) Enable(activationRecord *op.ActivationRecord, name *Resource) (*Resource, *Receipt, error)
- func (p *Provider) Enabled(name *Resource) (bool, error)
- func (p *Provider) Exists(name *Resource) (bool, error)
- func (p *Provider) Restart(activationRecord *op.ActivationRecord, name *Resource) (*Resource, *Receipt, error)
- func (p *Provider) Running(name *Resource) (bool, error)
- func (p *Provider) Start(activationRecord *op.ActivationRecord, name *Resource) (*Resource, *Receipt, error)
- func (p *Provider) Stop(activationRecord *op.ActivationRecord, name *Resource) (*Resource, *Receipt, error)
- type Receipt
- type Resource
- func (r *Resource) Addressing() op.AddressingMode
- func (*Resource) CanConvertFrom(source reflect.Type) bool
- func (*Resource) ConvertFrom(value any) (any, error)
- func (r *Resource) Digest() (op.Digest, error)
- func (r *Resource) Equal(other any) bool
- func (r *Resource) Etag() (string, error)
- func (r *Resource) String() string
- func (r *Resource) UnmarshalJSON(data []byte) error
- func (r *Resource) UnmarshalText(text []byte) error
- func (r *Resource) UnmarshalYAML(unmarshal func(any) error) error
Constants ¶
const ( Disable op.ActionName = "service.disable" Enable op.ActionName = "service.enable" Enabled op.ActionName = "service.enabled" Exists op.ActionName = "service.exists" Restart op.ActionName = "service.restart" Running op.ActionName = "service.running" Start op.ActionName = "service.start" Stop op.ActionName = "service.stop" )
Action-name constants for the service provider's plan-mode actions.
Each constant is the short dotted action label its method dispatches under. Pass these to plan.Plan, op.ReceiverRegistry().BuildAction, RuntimeEnvironment.ActionByName, or WithActionNamed in place of a string literal so a typo is a compile error and rename / find-references work through the constant.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Provider ¶
type Provider struct {
op.ProviderBase
}
Provider provides platform-agnostic service management.
Platform-specific behavior is delegated to p.RuntimeEnvironment().Platform.ServiceManager.
+devlore:access=planned
func NewProvider ¶
func NewProvider(runtimeEnvironment *op.RuntimeEnvironment) *Provider
NewProvider creates a service provider bound to the given runtime environment.
func (*Provider) CompensateDisable ¶
func (p *Provider) CompensateDisable(activationRecord *op.ActivationRecord, receipt *Receipt) error
CompensateDisable undoes a Disable by re-enabling the service if it was enabled before.
Parameters:
- `activationRecord`: the dispatch activation (the required floor for compensating actions — step 27).
- `receipt`: the *Receipt from Provider.Disable; a no-op when serviceless or not enabled.
Returns:
- `error`: non-nil when no service manager is available or the enable command fails.
func (*Provider) CompensateEnable ¶
func (p *Provider) CompensateEnable(activationRecord *op.ActivationRecord, receipt *Receipt) error
CompensateEnable undoes an Enable by disabling the service if it wasn't enabled before.
Parameters:
- `activationRecord`: the dispatch activation (the required floor for compensating actions — step 27).
- `receipt`: the *Receipt from Provider.Enable; a no-op when serviceless or already enabled.
Returns:
- `error`: non-nil when no service manager is available or the disable command fails.
func (*Provider) CompensateRestart ¶
func (p *Provider) CompensateRestart(activationRecord *op.ActivationRecord, _ *Receipt) error
CompensateRestart is a no-op. A restarted service was already running.
Parameters:
- `activationRecord`: the dispatch activation (the required floor for compensating actions — step 27).
- `receipt`: the *Receipt from Provider.Restart; ignored.
Returns:
- `error`: always nil.
func (*Provider) CompensateStart ¶
func (p *Provider) CompensateStart(activationRecord *op.ActivationRecord, receipt *Receipt) error
CompensateStart undoes a Start by stopping the service if it wasn't running before.
Parameters:
- `activationRecord`: the dispatch activation (the required floor for compensating actions — step 27).
- `receipt`: the *Receipt from Provider.Start; a no-op when serviceless or already running.
Returns:
- `error`: non-nil when no service manager is available or the stop command fails.
func (*Provider) CompensateStop ¶
func (p *Provider) CompensateStop(activationRecord *op.ActivationRecord, receipt *Receipt) error
CompensateStop undoes a Stop by starting the service if it was running before.
Parameters:
- `activationRecord`: the dispatch activation (the required floor for compensating actions — step 27).
- `receipt`: the *Receipt from Provider.Stop; a no-op when serviceless or not running.
Returns:
- `error`: non-nil when no service manager is available or the start command fails.
func (*Provider) Disable ¶
func (p *Provider) Disable(activationRecord *op.ActivationRecord, name *Resource) (*Resource, *Receipt, error)
Disable disables a service from starting at boot.
Parameters:
- `activationRecord`: the dispatch activation (the required floor for compensable actions — step 27).
- `name`: service resource identifying the service.
Returns:
- `*Resource`: the service resource (identity unchanged).
- `*Receipt`: compensation state recording whether the service was enabled before.
- `error`: non-nil when no service manager is available or the disable command fails.
func (*Provider) Enable ¶
func (p *Provider) Enable(activationRecord *op.ActivationRecord, name *Resource) (*Resource, *Receipt, error)
Enable enables a service to start at boot.
Parameters:
- `activationRecord`: the dispatch activation (the required floor for compensable actions — step 27).
- `name`: service resource identifying the service.
Returns:
- `*Resource`: the service resource (identity unchanged).
- `*Receipt`: compensation state recording whether the service was enabled before.
- `error`: non-nil when no service manager is available or the enable command fails.
func (*Provider) Enabled ¶
Enabled returns true if the named service is enabled to start at boot.
Parameters:
- `name`: service resource to check.
Returns:
- `bool`: true when the service is enabled.
- `error`: non-nil when no service manager is available.
func (*Provider) Exists ¶
Exists returns true if the named service exists on the system.
Parameters:
- `name`: service resource to check.
Returns:
- `bool`: true when the service exists.
- `error`: non-nil when no service manager is available.
func (*Provider) Restart ¶
func (p *Provider) Restart(activationRecord *op.ActivationRecord, name *Resource) (*Resource, *Receipt, error)
Restart restarts a service by stopping then starting it.
Parameters:
- `activationRecord`: the dispatch activation (the required floor for compensable actions — step 27).
- `name`: service resource identifying the service.
Returns:
- `*Resource`: the service resource (identity unchanged).
- `*Receipt`: compensation state; Provider.CompensateRestart is a no-op (the service was already running).
- `error`: non-nil when no service manager is available or the stop/start commands fail.
func (*Provider) Running ¶
Running returns true if the named service is currently running.
Parameters:
- `name`: service resource to check.
Returns:
- `bool`: true when the service is running.
- `error`: non-nil when no service manager is available.
func (*Provider) Start ¶
func (p *Provider) Start(activationRecord *op.ActivationRecord, name *Resource) (*Resource, *Receipt, error)
Start starts a service.
Parameters:
- `activationRecord`: the dispatch activation (the required floor for compensable actions — step 27).
- `name`: service resource identifying the service.
Returns:
- `*Resource`: the service resource (identity unchanged).
- `*Receipt`: compensation state recording whether the service was running before.
- `error`: non-nil when no service manager is available or the start command fails.
func (*Provider) Stop ¶
func (p *Provider) Stop(activationRecord *op.ActivationRecord, name *Resource) (*Resource, *Receipt, error)
Stop stops a service.
Parameters:
- `activationRecord`: the dispatch activation (the required floor for compensable actions — step 27).
- `name`: service resource identifying the service.
Returns:
- `*Resource`: the service resource (identity unchanged).
- `*Receipt`: compensation state recording whether the service was running before.
- `error`: non-nil when no service manager is available or the stop command fails.
type Receipt ¶
type Receipt struct {
op.ReceiptBase
// WasRunning records whether the service was running before the action.
WasRunning bool
// WasEnabled records whether the service was enabled before the action.
WasEnabled bool
}
Receipt holds service-specific compensation state for Provider.Start, Provider.Stop, Provider.Enable, and Provider.Disable calls.
The embedded op.ReceiptBase carries the affected service Resource and the opaque op.ReceiptBase.TransactionID minted at op.ReceiptBase.Commit time. The service name is read through the resource — no per-receipt name field. WasRunning and WasEnabled record the pre-call running and enabled flags so the corresponding Compensate methods can restore the service to its prior state.
func (*Receipt) MarshalJSON ¶
MarshalJSON encodes the receipt as JSON: the base envelope (action, resource_uri, transaction_id) extended with was_running and was_enabled.
Delegates to Receipt.MarshalYAML for the serialized-shape value, then runs json.Marshal over it.
Returns:
- `[]byte`: JSON-encoded object.
- `error`: any error from Receipt.MarshalYAML or json.Marshal.
func (*Receipt) MarshalYAML ¶
MarshalYAML returns the receipt's full state as an anonymous struct value the YAML encoder serializes.
Returns:
- `any`: the populated anonymous struct for the YAML encoder to walk.
- `error`: nil under normal conditions.
func (*Receipt) RestoreEncoded ¶
func (r *Receipt) RestoreEncoded( runtimeEnvironment *op.RuntimeEnvironment, base op.ReceiptData, fields map[string]any, ) error
RestoreEncoded reconstructs the receipt from its codec-decoded envelope, resolving its service Resource against the rehydrated catalog.
It is the op.Receipt.RestoreEncoded override the recovery stack drives at re-arm (via [op.reconstructReceipt]) — the env is threaded in explicitly as a parameter, not read off the receiver, so the stack path (which loads a bare receipt before the catalog is rehydrated) can reconstruct it. The service Resource is resolved from `base.ResourceURI` (its "svc:" scheme stripped) via DiscoverResource; the base is re-seated via op.NewReceiptBase so op.ReceiptBase.Restore's URI-match check has a live resource, then Restore writes the full base and the service-specific `was_running` / `was_enabled` flags are read from `fields`.
Parameters:
- `runtimeEnvironment`: the resume environment; its catalog must hold (or be able to construct) the resource.
- `base`: the codec-decoded base execution state; its `ResourceURI` ("svc:<name>") names the resource.
- `fields`: the receipt's whole decoded object; `was_running` / `was_enabled` are read from it.
Returns:
- `error`: a missing catalog. The envelope and the rehydrated catalog arrive post-op.LoadTrace — checksum-verified — so a missing or mistyped resource entry, a base-restore failure, or a mistyped flag field is a serialization bug and panics (docs/architecture/5-graph-trace-integrity.md).
type Resource ¶
type Resource struct {
op.ResourceBase
// Name is the service name (e.g., "nginx", "sshd"). Identity-bearing — appears in the URI <specific> as
// `svc:<Name>`. Derivable from URI.
Name string
}
Resource represents a system service identified by name.
Location-keyed: the canonical URI is `tag:devlore.noblefactor.com,2026-01-01:svc:<Name>#...service.Resource`. Service state (running, enabled, mode, last-changed) is host-side and not part of identity — two service.Resources with the same Name on different hosts share a URI and a catalog entry.
func DiscoverResource ¶
func DiscoverResource(runtimeEnvironment *op.RuntimeEnvironment, value any) (*Resource, error)
DiscoverResource registers a service.Resource via op.ResourceCatalog.Discover without claiming production.
Used by the framework's resource registry adapter for slot coercion (when starlark supplies a string and the slot expects a *service.Resource) and by callers holding a reference handle without claiming production. UnmarshalJSON / UnmarshalText / UnmarshalYAML rehydration is the canonical use case.
Discover does not stamp a producer, so unlike NewResource it takes only `runtimeEnvironment` — no unit reference is needed.
Same value-shape dispatch as NewResource: bare service name or canonical tag URI.
Nil-Catalog tolerance: returns the unlinked candidate when no catalog is present.
Parameters:
- `runtimeEnvironment`: the session runtime environment.
- `value`: a bare service name string, or a canonical tag URI; same dispatch as NewResource.
Returns:
- `*Resource`: canonical catalog entry, or the unlinked candidate when no catalog is present.
- `error`: non-string input, malformed URI, or op.ResourceBase construction failure.
func NewResource ¶
func NewResource(runtimeEnvironment *op.RuntimeEnvironment, producerID string, value any) (*Resource, error)
NewResource constructs a service.Resource and claims production via op.ResourceCatalog.GetOrCreate.
Use NewResource from a producer dispatch context — typically a provider method that has received an op.ActivationRecord from the framework. The returned Resource is the canonical catalog entry, stamped with `producerID = activationRecord.CallerID.ID()` (or empty when `Unit` is nil for non-graph dispatch). Use DiscoverResource instead when the caller is not claiming production (rehydration, reference handles, the framework's slot-coercion adapter).
Today no service provider method actually claims production — Start, Stop, Enable, Disable, Restart all take an existing *Resource and mutate the on-host service state without changing the URI. NewResource exists for symmetry with the two-constructor pattern and as a stable surface for any future service producer.
Nil-Catalog tolerance: returns the unlinked candidate when no catalog is present.
Parameters:
- `runtimeEnvironment`: the session runtime environment.
- `producerID`: the producing caller's id (`activationRecord.CallerID`), or "" for caller-less dispatch. for non-graph dispatch.
- `value`: a bare service name string, or a canonical tag URI (`tag:..:svc:<name>#...`).
Returns:
- `*Resource`: canonical catalog entry, or the unlinked candidate when no catalog is present.
- `error`: non-string input, malformed URI, or op.ResourceBase construction failure.
func (*Resource) Addressing ¶
func (r *Resource) Addressing() op.AddressingMode
Addressing reports that service.Resource is location-keyed by service name.
Overrides op.ResourceBase.Addressing's op.AddressingUnknown default. The boot-discipline check in pkg/op/addressing_test.go relies on every announced Resource type returning a non-Unknown mode here.
Returns:
- `op.AddressingMode`: op.AddressingLocation — identity is the service name embedded in the URI.
func (*Resource) CanConvertFrom ¶
CanConvertFrom reports whether `source` can be projected into a *Resource via Resource.ConvertFrom.
Opts the service Resource into the framework's op.TargetConverter contract — accepted source shape is `string` (interpreted as a bare service name like "nginx", or a canonical tag URI like `tag:..:svc:<name>#...`). The framework consults this probe both at plan-time via [op.typesAreInterconvertible] (the bubble-up parameter-consistency check) and at dispatch-time via op.Convert step 7 (env-less fallback). The canonical dispatch-time path remains the registered constructor at op.Convert step 6, which receives the full op.RuntimeEnvironment and dispatches the bare-name vs URI input shape via [buildCandidate].
Cheap-probe contract: this method is called against a nil-or-zero `*Resource` receiver by [op.typesAreInterconvertible] during plan-time bubble-up checks. MUST NOT dereference receiver fields.
Parameters:
- `source`: the candidate source type to test.
Returns:
- `bool`: true when `source` is `string`.
func (*Resource) ConvertFrom ¶
ConvertFrom projects `value` into an env-less unlinked *Resource.
Used by op.Convert step 7 when the env-aware registered constructor (step 6) is unavailable — env-less library callers, tests, or op.RuntimeEnvironment.Registry-missing contexts. The returned Resource carries only the Name set from `value`; the canonical URI on the embedded op.ResourceBase is NOT populated here. Provider methods consuming the projected Resource are responsible for re-canonicalization via their own NewResource/DiscoverResource path when full identity is required.
Parameters:
- `value`: the source value; must be `string`.
Returns:
- `any`: the constructed unlinked *Resource.
- `error`: non-nil when `value` is not a `string`.
func (*Resource) Digest ¶
Digest returns the SHA-256 of the canonical URI.
Service state (running/enabled/mode) is host-side and not part of identity, so the digest derives from the identity itself (the URI) rather than runtime state. Hashing the URI keeps the digest algorithm consistent with the rest of the system (op.ParseDigest only accepts sha256) and gives service.Resource a stable token for the catalog's etag-mismatch path. Overrides op.ResourceBase.Digest's op.ErrUnimplemented default.
Returns:
- `op.Digest`: sha256 of the URI; Algorithm = "sha256", Bytes = 32 raw digest bytes.
- `error`: nil under normal conditions.
func (*Resource) Equal ¶
Equal reports whether r and other identify the same service.Resource.
Strict equality: other must be a *service.Resource (not merely an op.Resource with the same URI). Once the type check passes, URI comparison is delegated to op.ResourceBase.Equal.
Parameters:
- `other`: candidate value to compare against; nil or any non-*service.Resource value returns false.
Returns:
- `bool`: true when `other` is a *service.Resource with the same URI as r.
func (*Resource) Etag ¶
Etag returns the canonical URI as the change-detection token.
For a location-keyed Resource whose state is host-side and outside its identity, the URI doubles as the etag: two service.Resources with the same Name share a URI and are the same Resource. Any host-side state change (the service stops, restarts, changes mode) is detected by callers via explicit probes, not through Etag.
Returns:
- `string`: the canonical URI (identical to op.ResourceBase.URI).
- `error`: nil under normal conditions.
func (*Resource) String ¶
String returns the compact JSON encoding of the Resource for debug output.
Delegates to op.ResourceBase.Format per the project Go style guideline that String() of every concrete Resource type calls r.Format(r).
Returns:
- `string`: the compact JSON encoding of r.
func (*Resource) UnmarshalJSON ¶
UnmarshalJSON populates the receiver from its JSON document (a bare URI string).
The caller pre-seeds the receiver's embedded op.ResourceBase with a valid op.RuntimeEnvironment before invoking this method. The URI alone is sufficient — identity is the service name encoded in the URI's <specific> as `svc:<Name>`.
Parameters:
- `data`: JSON bytes encoding a single bare URI string.
Returns:
- `error`: missing RuntimeEnvironment on receiver, malformed JSON, or rehydration failure.
func (*Resource) UnmarshalText ¶
UnmarshalText populates the receiver from raw UTF-8 bytes containing the URI.
Same prerequisites and semantics as Resource.UnmarshalJSON; the receiver's op.RuntimeEnvironment must be set before invocation.
Parameters:
- `text`: UTF-8 bytes containing the canonical tag URI.
Returns:
- `error`: missing RuntimeEnvironment on receiver, or rehydration failure.
func (*Resource) UnmarshalYAML ¶
UnmarshalYAML populates the receiver from its YAML document (a bare URI scalar).
Same prerequisites and semantics as Resource.UnmarshalJSON; the receiver's op.RuntimeEnvironment must be set before invocation.
Parameters:
- `unmarshal`: yaml decode hook supplied by the YAML library; called with a *string target.
Returns:
- `error`: missing RuntimeEnvironment on receiver, decode failure, or rehydration failure.