Documentation
¶
Overview ¶
Package serviceops contains the shared business logic for installing, starting, stopping, and removing lerd services. The CLI commands and the MCP tools both call into here so they enforce identical preset gating, dependency cascades, and dynamic_env regeneration.
Index ¶
- func CreateDatabase(svc, name string) (bool, error)
- func DeleteSnapshot(service, database, name string, allDatabases bool) error
- func DropDatabase(svc, name string) (bool, error)
- func EnsureCustomServiceQuadlet(svc *config.CustomService) error
- func EnsureDefaultPresetQuadlet(name string) error
- func EnsureDefaultPresetQuadletPinned(name, pinnedImage string) error
- func EnsureS3Bucket(name string) (bool, error)
- func EnsureServiceRunning(name string) error
- func InstallPresetByName(name, version string) (*config.CustomService, error)
- func InstallPresetStreaming(name, version string, emit func(PhaseEvent)) (*config.CustomService, error)
- func IsBuiltin(name string) bool
- func MigrateService(name, targetImage string, emit func(PhaseEvent)) error
- func MissingPresetDependencies(svc *config.CustomService) []string
- func RegenerateFamilyConsumers(family string)
- func RegenerateFamilyConsumersForService(name string)
- func ReinstallService(name string, resetData bool, emit func(PhaseEvent)) error
- func RemoveService(name string, opts RemoveOptions, emit func(PhaseEvent)) error
- func ReprovisionLinkedSites(serviceName string, emit func(PhaseEvent)) error
- func ResolveMigrateTarget(name, currentImage, version string) (string, error)
- func RestoreSnapshot(t SnapshotTarget, name string, emit func(PhaseEvent)) error
- func RollbackService(name string, emit func(PhaseEvent)) error
- func S3BucketName(name string) string
- func ServiceFamily(name string) string
- func SnapshotFamilySupported(family string) bool
- func StartDependencies(svc *config.CustomService) error
- func StopWithDependents(name string)
- func SupportsMigration(name string) bool
- func UpdateServiceStreaming(name, targetImage string, emit func(PhaseEvent)) error
- type PhaseEvent
- type RemoveOptions
- type Snapshot
- type SnapshotMeta
- type SnapshotTarget
- type UpdateAvailability
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func CreateDatabase ¶ added in v1.19.0
CreateDatabase creates dbName inside the named service container if it does not already exist. svc is the service name (e.g. "mysql", "mysql-5-6", "mariadb-11", "postgres-14"); the container is always "lerd-<svc>". The SQL client used is determined by the family inferred from svc. Returns (true, nil) if created, (false, nil) if it already existed, or (false, err) on failure.
func DeleteSnapshot ¶ added in v1.22.0
DeleteSnapshot removes a stored snapshot, erroring when it does not exist so callers can report the miss clearly.
func DropDatabase ¶ added in v1.22.0
DropDatabase removes the named database from the service container. Returns (true, nil) if it was dropped, (false, nil) if it was already gone, or (false, err) on failure.
func EnsureCustomServiceQuadlet ¶
func EnsureCustomServiceQuadlet(svc *config.CustomService) error
EnsureCustomServiceQuadlet writes the quadlet for a custom service and reloads systemd only when the file actually changed on disk. Materialises any declared file mounts and resolves dynamic_env directives so the rendered quadlet has the computed values.
func EnsureDefaultPresetQuadlet ¶ added in v1.19.0
EnsureDefaultPresetQuadlet writes the quadlet for a default-preset service (mysql, postgres, redis, ...) by resolving the canonical CustomService from its YAML preset, layering the user's image / extra-port overrides from global config, applying the platform-specific image override last (matching the legacy "platform override wins" semantics), and finally writing through the shared custom-service quadlet writer.
This replaces the older embedded-template flow (cli.ensureServiceQuadlet) so default services and add-on presets share one code path.
func EnsureDefaultPresetQuadletPinned ¶ added in v1.19.0
EnsureDefaultPresetQuadletPinned is the reinstall-aware sibling of EnsureDefaultPresetQuadlet. When pinnedImage is non-empty, it is used as the source-of-truth for the Image= line, taking precedence over both the preset.Image fallback and the on-disk preserved image. Reinstall captures the on-disk image *before* RemoveService deletes the quadlet, then passes it here so the fresh install pins the same tag the user was running — otherwise the rolling preset.Image bump that the v1.19.0-beta.6 fix was designed to prevent fires on every reinstall.
Callers outside the reinstall path should use EnsureDefaultPresetQuadlet (which passes pinnedImage="").
func EnsureS3Bucket ¶ added in v1.19.0
EnsureS3Bucket creates a bucket for the given name in lerd-rustfs using an ephemeral mc container. Returns (true, nil) if created, (false, nil) if it already existed, or (false, err) on failure. Retries up to 3 times (2s apart) to bridge the window between the host TCP port becoming reachable and the container network being fully ready for mc operations.
func EnsureServiceRunning ¶
EnsureServiceRunning starts the service if it is not already active and waits until it is ready. Recurses through depends_on for custom services.
func InstallPresetByName ¶
func InstallPresetByName(name, version string) (*config.CustomService, error)
InstallPresetByName materialises a bundled preset as a custom service. version selects a tag for multi-version presets; empty falls back to the preset's DefaultVersion.
func InstallPresetStreaming ¶ added in v1.18.0
func InstallPresetStreaming(name, version string, emit func(PhaseEvent)) (*config.CustomService, error)
InstallPresetStreaming runs the full install flow and emits a PhaseEvent at every step. The image is pulled before StartUnit so the hidden on-demand pull latency becomes visible progress in the UI.
func IsBuiltin ¶
IsBuiltin reports whether name is a built-in (default-preset) lerd service. Kept as a passthrough so callers don't have to import config.
func MigrateService ¶ added in v1.19.0
func MigrateService(name, targetImage string, emit func(PhaseEvent)) error
MigrateService runs a per-family dump/restore migration so the service can move across data-incompatible SQL versions (e.g. mysql 8.0 → 9.0). Errors when no handler is registered for the service family.
func MissingPresetDependencies ¶
func MissingPresetDependencies(svc *config.CustomService) []string
MissingPresetDependencies returns the names of services that svc declares in depends_on but which are not currently installed. A built-in (default preset) counts as installed only when its quadlet is on disk: the legacy "built-ins are always available" assumption broke once `lerd service remove <builtin>` shipped, and a dependent install that later hits EnsureServiceRunning on a missing built-in would fail with no rollback (which is exactly what reinstall pre-flight is meant to prevent).
func RegenerateFamilyConsumers ¶
func RegenerateFamilyConsumers(family string)
RegenerateFamilyConsumers re-renders the quadlet of any installed custom service whose dynamic_env references the named family. Active consumers are stopped, removed, and started so the new generated unit is the one systemd loads.
func RegenerateFamilyConsumersForService ¶
func RegenerateFamilyConsumersForService(name string)
RegenerateFamilyConsumersForService is a convenience that wraps RegenerateFamilyConsumers in a no-op when name has no recognised family.
func ReinstallService ¶ added in v1.19.0
func ReinstallService(name string, resetData bool, emit func(PhaseEvent)) error
ReinstallService stops, removes, and reinstalls a service, optionally wiping its data and reprovisioning per-site state (databases, buckets) on the fresh service.
Reinstall requires that the service is currently installed: for default presets that means a quadlet on disk; for custom services that means a custom-service YAML. If neither is found ReinstallService returns an error.
Pre-flight validation runs before RemoveService so configuration errors (unknown preset, bad version, missing dependencies, unreachable image) fail with the on-disk state intact rather than after the service config has already been deleted.
When resetData is true the data dir is renamed-aside (recoverable as .pre-remove-<ts>) and ReprovisionLinkedSites is invoked after the install completes so dependent sites' DBs/buckets exist on the fresh service.
func RemoveService ¶ added in v1.19.0
func RemoveService(name string, opts RemoveOptions, emit func(PhaseEvent)) error
RemoveService stops, removes, and (optionally) wipes the data of a service. It is the single entry point shared by the CLI, MCP, and UI handlers.
Order:
- emit stopping_unit, StopUnit (only if active/activating; abort on error)
- emit removing_container, RemoveContainer
- (if RemoveData) emit removing_data, rename-aside ~/.local/share/lerd/data/<name>
- emit removing_quadlet, RemoveQuadlet, DaemonReload
- emit removing_config, RemoveCustomService (no-op for default presets)
- emit regenerating_consumers, RegenerateFamilyConsumers (if family known)
- emit done
emit may be nil. Default-preset services are accepted: their YAML doesn't exist on disk, so RemoveCustomService is a tolerated no-op.
func ReprovisionLinkedSites ¶ added in v1.19.0
func ReprovisionLinkedSites(serviceName string, emit func(PhaseEvent)) error
ReprovisionLinkedSites recreates per-site state on a freshly installed service after a reset-data reinstall. For db families (mysql/mariadb/postgres) it ensures each linked site's database exists. For object-storage families (rustfs) it ensures each linked site's bucket exists. Other families are a no-op (cache services hold no client-owned state).
Per-site failures are collected and joined; the loop continues so one misconfigured site doesn't block the rest.
func ResolveMigrateTarget ¶ added in v1.22.0
ResolveMigrateTarget maps a migrate version argument to a target image. A bare preset version ("18") resolves to that version's full preset image; an unknown argument is substituted as a literal tag onto the current image.
func RestoreSnapshot ¶ added in v1.22.0
func RestoreSnapshot(t SnapshotTarget, name string, emit func(PhaseEvent)) error
RestoreSnapshot loads a stored snapshot back into its database. A per-database restore drops and recreates the target database first so no orphan tables survive; an all-databases restore replays the self-cleaning dump as-is.
func RollbackService ¶ added in v1.19.0
func RollbackService(name string, emit func(PhaseEvent)) error
RollbackService swaps a service back to its previously-running image. Refuses when no previous image is recorded or when the most recent op was a migrate (binary/schema mismatch would corrupt the data dir).
func S3BucketName ¶ added in v1.19.0
S3BucketName converts a project handle into a valid S3 bucket name: lowercase, hyphens instead of underscores, leading/trailing non-alphanumerics stripped, max length 63.
func ServiceFamily ¶
ServiceFamily returns the family of a service by name. Honours the explicit Family field on a custom service first, falls back to config.InferFamily for built-ins and pattern-matched alternates.
func SnapshotFamilySupported ¶ added in v1.22.0
SnapshotFamilySupported reports whether the named database family can be snapshotted (SQL engines only).
func StartDependencies ¶
func StartDependencies(svc *config.CustomService) error
StartDependencies ensures every entry in svc.DependsOn is up and ready before the parent is started.
func StopWithDependents ¶
func StopWithDependents(name string)
StopWithDependents stops every custom service that depends on name (depth-first), then stops name itself.
func SupportsMigration ¶ added in v1.19.0
SupportsMigration reports whether a registered family migrator exists for the named service.
func UpdateServiceStreaming ¶ added in v1.19.0
func UpdateServiceStreaming(name, targetImage string, emit func(PhaseEvent)) error
UpdateServiceStreaming pulls the chosen image, persists it, rewrites the quadlet, and restarts the unit. Phases: checking_registry, pulling_image, writing_quadlet, restarting_unit, done.
Types ¶
type PhaseEvent ¶ added in v1.18.0
type PhaseEvent struct {
Phase string `json:"phase"`
Image string `json:"image,omitempty"`
Message string `json:"message,omitempty"`
Dep string `json:"dep,omitempty"`
State string `json:"state,omitempty"`
Unit string `json:"unit,omitempty"`
}
PhaseEvent is one step of the streaming preset-install flow.
type RemoveOptions ¶ added in v1.19.0
type RemoveOptions struct {
// RemoveData renames the service's data directory to a timestamped
// `.pre-remove-<ts>` sibling. The data is recoverable by renaming back.
// On EXDEV the helper falls back to copy-tree + delete so the
// recoverability promise holds across filesystems too.
RemoveData bool
// SkipFamilyRegen suppresses the post-remove RegenerateFamilyConsumers
// pass. Set by ReinstallService, which then drives the regen itself
// after install: InstallPresetByName regenerates internally for custom
// services, while default-preset and failure paths call regen
// explicitly. The regen-during-remove was racing the post-install
// regen on macOS (launchctl bootout/bootstrap can fall through to
// kickstart which doesn't re-read the plist), leaving consumers on
// the partial plist.
SkipFamilyRegen bool
}
RemoveOptions controls optional side effects of RemoveService.
type Snapshot ¶ added in v1.22.0
type Snapshot struct {
Name string `json:"name"`
Created time.Time `json:"created"`
Service string `json:"service"`
Family string `json:"family"`
Database string `json:"database"`
AllDatabases bool `json:"all_databases"`
DumpFile string `json:"dump_file"`
Compressed bool `json:"compressed"`
SizeBytes int64 `json:"size_bytes"`
Site string `json:"site,omitempty"`
GitBranch string `json:"git_branch,omitempty"`
}
Snapshot is the meta.json sidecar describing one stored database snapshot.
func CreateSnapshot ¶ added in v1.22.0
func CreateSnapshot(t SnapshotTarget, name string, ctx SnapshotMeta, emit func(PhaseEvent)) (*Snapshot, error)
CreateSnapshot dumps the target database (or every database when t.AllDatabases is set) into a new named snapshot under config.SnapshotsDir(). An empty name is auto-generated from the current UTC time.
func ListSnapshots ¶ added in v1.22.0
ListSnapshots returns the stored snapshots for a service. A non-empty database narrows the result to that database; an empty database lists every database on the service. Service-wide all-databases snapshots are included when includeAll is set. Results are sorted newest first.
type SnapshotMeta ¶ added in v1.22.0
SnapshotMeta carries the optional best-effort context recorded into a snapshot's meta.json.
type SnapshotTarget ¶ added in v1.22.0
SnapshotTarget identifies the live database a snapshot is taken from or restored into. The cli and mcp layers build it from a resolved DB env.
type UpdateAvailability ¶ added in v1.19.0
type UpdateAvailability struct {
Service string `json:"service"`
CurrentImage string `json:"current_image"`
CurrentTag string `json:"current_tag"`
LatestTag string `json:"latest_tag,omitempty"`
LatestImage string `json:"latest_image,omitempty"`
Available bool `json:"available"`
Strategy string `json:"strategy"`
UpgradeTag string `json:"upgrade_tag,omitempty"`
UpgradeImage string `json:"upgrade_image,omitempty"`
PreviousImage string `json:"previous_image,omitempty"`
// CanRollback is false when the most recent op was a migrate (rolling the
// image back without restoring the pre-migrate data dir would corrupt it).
CanRollback bool `json:"can_rollback"`
}
UpdateAvailability is the metadata returned by CheckUpdateAvailable so the UI can render an "update available → v8.4.3" badge without applying it.
func CheckUpdateAvailable ¶ added in v1.19.0
func CheckUpdateAvailable(name string) (*UpdateAvailability, error)
CheckUpdateAvailable queries the registry for a newer tag matching the preset's update_strategy. Network and unsupported-registry errors are swallowed so the UI stays quiet on offline / custom-registry installs. Successful results are cached for updateAvailabilityTTL so snapshot rebuilds don't fork a `podman image inspect` per service per rebuild.