Documentation
¶
Overview ¶
Package service installs a user-scoped supervisor unit that runs the gateway in the foreground and records that unit in the project lockfile before writing it. Rendering is pure; supervisor control is behind the Supervisor interface so tests never need a host session.
Index ¶
- Variables
- func HasEntries(ctx context.Context, home string) (bool, error)
- func Hash(text string) string
- func Label(name string) string
- func Lookup(ctx context.Context, home, name string) (*project.Entry, error)
- func ParseLinger(output string) string
- func Render(spec Spec) (string, error)
- func ResolveBinary(override string) (string, error)
- func SanitizeName(name string) string
- func UnitFileName(name string) string
- func UnitPath(home string, homeOverridden bool, platform Platform, name string) string
- type AttentionError
- type BuildInput
- type InfraError
- type InstallOptions
- type InstallResult
- type KV
- type LogOptions
- type Manager
- func (m *Manager) Install(ctx context.Context, opts InstallOptions) (InstallResult, error)
- func (m *Manager) Linger(ctx context.Context) (string, error)
- func (m *Manager) Preview(ctx context.Context, force bool) ([]ResetRow, error)
- func (m *Manager) Remove(ctx context.Context, force bool) ([]ResetRow, error)
- func (m *Manager) Status(ctx context.Context, name string) ([]Row, error)
- func (m *Manager) Stop(ctx context.Context, name string) (StopReport, error)
- func (m *Manager) Uninstall(ctx context.Context, name string, opts UninstallOptions) (UninstallResult, error)
- type Platform
- type ResetRow
- type Row
- type RuntimeState
- type Spec
- type StopReport
- type Supervisor
- type UninstallOptions
- type UninstallResult
Constants ¶
This section is empty.
Variables ¶
var ErrUnitNotLoaded = errors.New("unit not loaded")
ErrUnitNotLoaded means the user manager does not have the unit. Destroy treats that as unsupervised: there is no Restart=on-failure to trip.
Functions ¶
func HasEntries ¶
HasEntries reports whether any service entry exists.
func ParseLinger ¶
ParseLinger reads `Linger=yes` or `Linger=no` from loginctl. Anything else is unknown.
func Render ¶
Render returns the supervisor unit text for spec. The same spec always produces the same bytes.
func ResolveBinary ¶
ResolveBinary chooses the path the unit will execute.
An absolute executable --binary wins. Otherwise, if LookPath("gridctl") and os.Executable resolve to the same file, the LookPath result is written (the stable Homebrew symlink). If they differ, os.Executable is written so a development binary is not replaced by a release on PATH. The unit never relies on PATH to find gridctl.
func SanitizeName ¶
SanitizeName maps a stack name onto the supervisor unit alphabet [A-Za-z0-9._-]. Other runes become "-", runs of "-" collapse, and leading and trailing "-" and "." are trimmed.
func UnitFileName ¶
UnitFileName is the systemd unit filename (not a path).
func UnitPath ¶
UnitPath returns the supervisor unit path for name.
Linux uses <home>/.config/systemd/user/gridctl-<name>.service, except that XDG_CONFIG_HOME is honored when it is set and either the home is not overridden or the cleaned value is inside home. An XDG_CONFIG_HOME outside an overridden home is ignored so `gridctl --home /tmp/demo service install` cannot write into the real ~/.config. macOS always uses <home>/Library/LaunchAgents/dev.gridctl.<name>.plist.
Types ¶
type AttentionError ¶
type AttentionError struct {
Msg string
}
AttentionError is an exit-1 condition: the user has to choose or finish a step. The unit and lock entry are left as the message describes.
func (*AttentionError) Error ¶
func (e *AttentionError) Error() string
type BuildInput ¶
type BuildInput struct {
// Platform overrides the host. Empty uses the host platform.
Platform Platform
// StackName is the raw stack name. StackFile is absolute.
StackName string
StackFile string
Port int
BasePort int
Watch bool
// RuntimeFlag is the global --runtime value, forwarded only when set.
RuntimeFlag string
Home string
// HomeOverridden reports state.HomeOverridden so the unit path stays
// under an overridden home.
HomeOverridden bool
// Environ is the process environment to filter. Nil means os.Environ
// is not consulted here; the caller passes the slice.
Environ []string
// PassphraseFile is an absolute path already verified by the caller.
// It is written as GRIDCTL_VAULT_PASSPHRASE_FILE and is never read here.
PassphraseFile string
// Binary, when set, is used as-is (the caller already validated
// --binary). Empty resolves the gridctl binary.
Binary string
Pack string
}
BuildInput is the resolved install request BuildSpec turns into a Spec.
type InfraError ¶
type InfraError struct {
Msg string
}
InfraError is an exit-2 condition: the supervisor binary is missing or its output cannot be read.
func (*InfraError) Error ¶
func (e *InfraError) Error() string
type InstallOptions ¶
type InstallOptions struct {
Spec Spec
DryRun bool
Force bool
NoEnable bool
Replace bool
HomeOverridden bool
Notes []string
}
InstallOptions configures one install.
type InstallResult ¶
type InstallResult struct {
Name string `json:"name"`
Platform string `json:"platform"`
UnitPath string `json:"unit_path"`
Label string `json:"label"`
ExecStart []string `json:"exec_start"`
Environment map[string]string `json:"environment"`
Enabled bool `json:"enabled"`
Running bool `json:"running"`
PID int `json:"pid"`
Linger string `json:"linger,omitempty"`
Notes []string `json:"notes,omitempty"`
DryRun bool `json:"dry_run"`
Text string `json:"text,omitempty"`
WouldEnable bool `json:"would_enable,omitempty"`
}
InstallResult is the machine-readable install document.
type KV ¶
KV is one environment assignment, in unit order.
func FilterEnvironment ¶
FilterEnvironment keeps a positive allowlist from environ, in this order: PATH, DOCKER_HOST, DOCKER_CONTEXT, CONTAINER_HOST, GRIDCTL_RUNTIME, GRIDCTL_NO_SKILL_UPDATE_CHECK, any GRIDCTL_EXPERIMENTAL_*, and SSH_AUTH_SOCK only when its cleaned value is under $XDG_RUNTIME_DIR, /run/user/, or <home>/.ssh/. GRIDCTL_HOME is never copied; the caller writes it from the resolved home.
skipped lists a rejected SSH_AUTH_SOCK value (the path only). Every other variable is dropped silently. A kept key that is also in vault.InternalCredentialKeys is an error (defense in depth; the allowlist cannot produce one).
type LogOptions ¶
LogOptions configures a log follow.
type Manager ¶
type Manager struct {
// contains filtered or unexported fields
}
Manager owns service lock entries and the unit files they point at.
func NewManager ¶
func NewManager(home string, sup Supervisor) *Manager
NewManager builds a manager for home. sup may be nil only in tests that never call the supervisor; production callers pass NewSupervisor.
func (*Manager) Install ¶
func (m *Manager) Install(ctx context.Context, opts InstallOptions) (InstallResult, error)
Install records the unit in the lockfile and writes it. Dry-run checks conflict and drift against the read-only lock and returns before Set, Save, or the file write.
func (*Manager) Preview ¶
Preview lists the units reset would touch. force matches Remove, so a dry-run with --force does not report kept-drift for a unit Remove would delete. It does not call the supervisor.
func (*Manager) Remove ¶
Remove disables and stops each unit, removes the file when the hash matches (or force), and drops the record. Per-item failures are rows.
func (*Manager) Stop ¶
Stop stops a loaded unit without disabling it and without KillDaemon. A missing entry reports Present false. A unit that is not loaded is not a stop failure: the supervisor does not own the process.
func (*Manager) Uninstall ¶
func (m *Manager) Uninstall(ctx context.Context, name string, opts UninstallOptions) (UninstallResult, error)
Uninstall disables and stops the unit, removes the file when the hash matches (or Force), and drops the lock entry. A drifted unit without Force is kept and reported. A stop failure leaves the record so the command can be re-run.
type Platform ¶
type Platform string
Platform is the supervisor the unit targets.
func HostPlatform ¶
HostPlatform reports the supervisor for this operating system.
type ResetRow ¶
type ResetRow struct {
Name string
Client string
Path string
Action string
Detail string
Error string
}
ResetRow is the reset adapter's view of one unit. resetops converts it.
type Row ¶
type Row struct {
Name string `json:"name"`
Platform string `json:"platform"`
State string `json:"state"`
Enabled string `json:"enabled"`
PID int `json:"pid,omitempty"`
Port int `json:"port,omitempty"`
Stack string `json:"stack"`
Unit string `json:"unit"`
Linger string `json:"linger,omitempty"`
Remediation string `json:"remediation,omitempty"`
Label string `json:"label,omitempty"`
Pack string `json:"pack,omitempty"`
Detail string `json:"detail,omitempty"`
}
Row is one status line.
type RuntimeState ¶
type RuntimeState struct {
// State is running, failed, inactive, not-loaded, or unknown.
State string
Enabled bool
PID int
Detail string
}
RuntimeState is the parsed supervisor view of one unit.
func ParseLaunchctlPrint ¶
func ParseLaunchctlPrint(output string) RuntimeState
ParseLaunchctlPrint parses `launchctl print` loosely. Only `state = ` and `pid = ` are read. Anything else is unknown rather than an error, because the text format is not stable across macOS versions.
func ParseSystemctlShow ¶
func ParseSystemctlShow(output string) RuntimeState
ParseSystemctlShow parses `systemctl show -p ...` key=value lines. running requires ActiveState=active and SubState=running. A unit the manager does not know is not-loaded.
type Spec ¶
type Spec struct {
Platform Platform
Name string // sanitized service name
StackName string // raw stack name
Label string
Binary string
Argv []string
Env []KV
Home string
LogPath string
UnitPath string
PodmanSocketDependency bool
StackFile string
Pack string
Port int
BasePort int
}
Spec is the complete input to Render. It carries no I/O.
type StopReport ¶
type StopReport struct {
// Present is true when a service lock entry exists.
Present bool
Path string
// OwnedPID is the supervisor's main PID when that process is the
// recorded daemon, or the unit was active. Zero means the supervisor
// does not own the recorded process. Destroy must not KillDaemon an
// owned PID: Restart=on-failure would start it again.
OwnedPID int
// StoppedUnit is true when a loaded unit was stopped. Destroy prints
// the re-enable message only then.
StoppedUnit bool
// NotLoaded means the user manager does not have the unit, or no
// supervisor binary is installed. Destroy continues as unsupervised.
NotLoaded bool
}
StopReport is what destroy needs after asking the supervisor to stop.
type Supervisor ¶
type Supervisor interface {
Enable(ctx context.Context, spec Spec) error
Disable(ctx context.Context, spec Spec) error
// Stop stops the unit without disabling it. destroy uses this so
// Restart=on-failure does not see a killed process.
Stop(ctx context.Context, spec Spec) error
Query(ctx context.Context, spec Spec) (RuntimeState, error)
Logs(ctx context.Context, spec Spec, opts LogOptions) error
// Linger reports "yes", "no", or "unknown". Non-Linux supervisors
// return "unknown" and a nil error.
Linger(ctx context.Context) (string, error)
// DaemonReload refreshes the user manager after a unit file is
// removed. Non-Linux supervisors return nil.
DaemonReload(ctx context.Context) error
// Restart reloads unit text and restarts a unit the supervisor
// already owns. It is not KillDaemon: a five-second SIGKILL would
// trip Restart=on-failure.
Restart(ctx context.Context, spec Spec) error
}
Supervisor controls one user-scoped service manager. Implementations shell out to systemctl, loginctl, journalctl, or launchctl. Tests inject a fake.
func NewSupervisor ¶
func NewSupervisor() Supervisor
NewSupervisor returns the host supervisor. A missing binary is reported when a method runs, not here, so status and reset can still read the lockfile on a machine without systemctl.
type UninstallOptions ¶
UninstallOptions configures uninstall.