Documentation
¶
Index ¶
- Variables
- func BackupDeletable(ctx context.Context, js store.JobStore, backupID string) error
- func BackupInFlight(ctx context.Context, js store.JobStore, backupID string) (bool, error)
- func RestoreInFlight(ctx context.Context, js store.JobStore, backupID string) (bool, error)
- func SetDeployVerifyTimeout(d time.Duration)
- func SetVerifyTimeout(d time.Duration)
- func ValidateTemplate(t store.Template) error
- type ApplyOptions
- type ApplyRequest
- type BackupRequest
- type BlobStore
- type BlobWriter
- type DeleteOptions
- type EvacuateRequest
- type EvacuationPlan
- type Manifest
- type MigrateRequest
- type Observed
- type ObservedContainer
- type ObservedPod
- type ObservedPortMapping
- type ObservedVolume
- type PlanIssue
- type PlannedMove
- type RestoreRequest
- type Service
- func (s *Service) Apply(ctx context.Context, host string, req ApplyRequest, opts ApplyOptions) error
- func (s *Service) ApplyAndObserve(ctx context.Context, host string, req ApplyRequest, opts ApplyOptions) (Observed, error)
- func (s *Service) Backup(ctx context.Context, req BackupRequest, step func(step, detail string)) error
- func (s *Service) CheckBackupable(ctx context.Context, host, tmpl, slug string) error
- func (s *Service) CheckMigratable(ctx context.Context, req MigrateRequest) error
- func (s *Service) CheckRestorable(ctx context.Context, backupID string) (store.Backup, error)
- func (s *Service) CloneTemplate(ctx context.Context, srcID, newID string) (store.Template, error)
- func (s *Service) CopyVolume(ctx context.Context, fromHost, toHost, name string) error
- func (s *Service) CreateTemplate(ctx context.Context, t store.Template) error
- func (s *Service) Delete(ctx context.Context, host, tmpl, slug string, opts DeleteOptions) error
- func (s *Service) DeleteBackup(ctx context.Context, id string) error
- func (s *Service) DeleteHostSecret(ctx context.Context, host, name string) error
- func (s *Service) DeleteTemplate(ctx context.Context, id string, force bool) error
- func (s *Service) DeleteVolume(ctx context.Context, host, name string, force bool) error
- func (s *Service) Get(ctx context.Context, host, tmpl, slug string) (Observed, error)
- func (s *Service) GetBackup(ctx context.Context, id string) (store.Backup, error)
- func (s *Service) GetTemplate(ctx context.Context, id string) (store.Template, error)
- func (s *Service) HostCounts(ctx context.Context, host string) (instances, containers int, err error)
- func (s *Service) HostLoad(ctx context.Context, host string) (podman.HostInfo, error)
- func (s *Service) HostSecrets(ctx context.Context, host string) ([]podman.Secret, error)
- func (s *Service) Hosts() []config.Host
- func (s *Service) InstanceCount(ctx context.Context, host string) (int, error)
- func (s *Service) InstanceSecretState(ctx context.Context, host, tmpl, slug string) (map[string]bool, error)
- func (s *Service) InstanceVolumes(ctx context.Context, host, tmpl, slug string) ([]podman.Volume, error)
- func (s *Service) List(ctx context.Context, host, tmpl string) ([]Observed, error)
- func (s *Service) ListAllInstances(ctx context.Context, host string) ([]Observed, error)
- func (s *Service) ListBackups(ctx context.Context, host, tmpl, slug string, limit int) ([]store.Backup, error)
- func (s *Service) Logs(ctx context.Context, host, tmpl, slug, container string, ...) (<-chan podman.LogLine, error)
- func (s *Service) Migrate(ctx context.Context, req MigrateRequest, step func(step, detail string)) error
- func (s *Service) Ping(ctx context.Context, host string) error
- func (s *Service) PlanEvacuation(ctx context.Context, req EvacuateRequest) (EvacuationPlan, error)
- func (s *Service) PortsInUse(ctx context.Context, host string) ([]podman.PortMapping, error)
- func (s *Service) PutHostSecret(ctx context.Context, host, name string, value []byte, persist bool) error
- func (s *Service) ReconcileBackup(ctx context.Context, req BackupRequest, step func(step, detail string)) (resolved, ok bool, message string, err error)
- func (s *Service) ReconcileMigrate(ctx context.Context, req MigrateRequest, step func(step, detail string)) (resolved, succeeded bool, message string, err error)
- func (s *Service) ReconcileSpecsOnHost(ctx context.Context, hostID string)
- func (s *Service) ResolveEvacuation(ctx context.Context, req EvacuateRequest) ([]MigrateRequest, error)
- func (s *Service) Restart(ctx context.Context, host, tmpl, slug string) error
- func (s *Service) Restore(ctx context.Context, req RestoreRequest, step func(step, detail string)) error
- func (s *Service) RotateInstanceSecrets(ctx context.Context, host, tmpl, slug string, newSecrets map[string]string) error
- func (s *Service) SetBlobStore(bs BlobStore)
- func (s *Service) SetHosts(hosts []config.Host)
- func (s *Service) SetIngress(c ingress.Controller, network string)
- func (s *Service) SetStore(st Store)
- func (s *Service) SetVerifyVolumes(v bool)
- func (s *Service) Start(ctx context.Context, host, tmpl, slug string) (Observed, error)
- func (s *Service) Stop(ctx context.Context, host, tmpl, slug string) error
- func (s *Service) Template(ctx context.Context, id string) (store.Template, error)
- func (s *Service) Templates(ctx context.Context) ([]store.Template, error)
- func (s *Service) UpdateTemplate(ctx context.Context, t store.Template) error
- func (s *Service) Upgrade(ctx context.Context, host string, req ApplyRequest, image string) error
- func (s *Service) UpgradeImage(ctx context.Context, host, tmpl, slug, image string) error
- func (s *Service) Version(ctx context.Context, host string) (string, error)
- type Store
Constants ¶
This section is empty.
Variables ¶
var ( ErrUnknownHost = errors.New("unknown host") ErrUnknownTemplate = errors.New("unknown template") ErrInstanceNotFound = errors.New("instance not found") ErrInstanceExists = errors.New("instance already exists") ErrHostSecretMissing = errors.New("required host secret missing") ErrImagePull = errors.New("image pull failed") ErrHostDraining = errors.New("host is draining") ErrPortConflict = errors.New("required host port already in use") ErrSameHost = errors.New("source and destination host are the same") ErrStoreDisabled = errors.New("migrate requires the state store") ErrVolumeIntegrity = errors.New("volume copy failed integrity check") ErrBackupNotFound = errors.New("backup not found") ErrBackupNotRestorable = errors.New("backup is not restorable") ErrBackupBusy = errors.New("backup has a backup or restore in flight") ErrBackupsDisabled = errors.New("backups require a blob store (-backup-dir)") )
Sentinel errors mapped by the API layer to JSON error codes.
var ( ErrTemplateInUse = errors.New("template is in use by one or more instances") ErrTemplateExists = errors.New("template already exists") // ErrInvalidTemplate wraps validation failures (bad id, unparsable body, // unknown parameter type, ingress mismatch) so the API can map them to 400. ErrInvalidTemplate = errors.New("invalid template") )
Template-management sentinel errors, mapped by the API layer to JSON codes.
var ErrInvalidEvacuation = errors.New("invalid evacuation request")
ErrInvalidEvacuation means the request cannot be planned against the stored specs: an instance on the host has no destination in the map, a map entry names no instance, a slug is ambiguous across templates, or a map entry names an unknown destination host. The API maps it to 400 invalid_request, giving every bad-map case one consistent status.
Functions ¶
func BackupDeletable ¶
BackupDeletable checks whether a backup is safe to delete: returns nil if neither a backup nor a restore job is active for it, ErrBackupBusy if one is. When js is nil (jobs disabled) the backup is always considered deletable. Callers must invoke this before Service.DeleteBackup.
func BackupInFlight ¶
BackupInFlight reports whether any active (queued/running/reconciling) backup job targets backupID. Used by the delete handler to refuse deleting a backup while it is still being written (ErrBackupBusy). Note: the gate is intentionally job-based, not row-state-based — a crashed daemon can leave a creating row with no live job, and that row must stay deletable.
func RestoreInFlight ¶
RestoreInFlight reports whether any active (queued/running/reconciling) restore job targets backupID. Shared by the API and UI delete handlers to refuse deleting a backup mid-restore (ErrBackupBusy).
func SetDeployVerifyTimeout ¶
SetDeployVerifyTimeout configures the readiness wait applied after Apply and Start. No-op for d <= 0. Called at startup via -deploy-verify-timeout flag.
func SetVerifyTimeout ¶
SetVerifyTimeout overrides the maximum time waitRunning waits for the destination to become ready before the migrate fails (and rolls back). No-op for d <= 0. Called once at startup from the -migrate-verify-timeout flag.
func ValidateTemplate ¶
ValidateTemplate checks an authored or seed template before it is persisted:
- The template id must be a valid DNS-label-style name.
- A dry-run render of the body (with a dummy value for every declared parameter) must succeed — this catches template syntax errors and references to undeclared parameters (missingkey=error).
- If the template declares ingress, its container must be non-empty and its port in 1..65535 (render.ValidateIngress), AND the rendered pod must contain a container whose name matches Ingress.Container.
Types ¶
type ApplyOptions ¶
type ApplyOptions struct {
Replace bool // if false and the pod exists, return ErrInstanceExists
SkipPull bool // if true, do not pre-pull container images (CI / local-only refs)
// AllowMissingSecrets relaxes the "every PerInstance secret must be present"
// validation rule for this Apply. It is used by the secret-rotation path:
// rotation overlays new values onto a stored spec and re-applies it, and must
// not be blocked just because a PerInstance secret was already unset in that
// spec (e.g. a template that gained a secret after the instance was deployed).
// The "unknown secret" check still applies. Deploys never set this.
AllowMissingSecrets bool
}
ApplyOptions controls the side effects of Apply beyond the request body.
type ApplyRequest ¶
type ApplyRequest struct {
Template string `json:"template"`
Slug string `json:"slug"`
Parameters map[string]any `json:"parameters"`
Secrets map[string]string `json:"secrets"`
Domains []string `json:"domains,omitempty"`
}
ApplyRequest is the body of POST /instances and PUT /instances/{...}.
type BackupRequest ¶
type BackupRequest struct {
BackupID string `json:"backup_id"`
Host string `json:"host"`
Template string `json:"template"`
Slug string `json:"slug"`
}
BackupRequest is the backup job's args. BackupID is generated at enqueue time (store.NewBackupID) so POST can return it before the job runs.
type BlobWriter ¶
type BlobWriter = extension.BlobWriter
type DeleteOptions ¶
DeleteOptions controls cleanup beyond the pod itself.
type EvacuateRequest ¶
type EvacuateRequest struct {
FromHost string `json:"from_host"`
Map map[string]string `json:"map"`
// Concurrency, if >0, overrides the server's default for how many child
// migrations run at once (clamped to [1,32] by the handler). Request-only:
// it does not affect the migrate plan, so ResolveEvacuation ignores it.
Concurrency int `json:"concurrency,omitempty"`
}
EvacuateRequest is the POST /evacuate body and the evacuate job's args. Map is slug -> destination host; it carries no template (resolved from the stored spec) and no parameters (Migrate merges the spec's own).
type EvacuationPlan ¶
type EvacuationPlan struct {
FromHost string `json:"from_host"`
Moves []PlannedMove `json:"moves"`
}
EvacuationPlan is the result of PlanEvacuation: the resolved per-instance moves plus, for each, whether the destination would currently accept it.
type Manifest ¶
type Manifest map[string]fileInfo
Manifest fingerprints a volume's tar export, keyed by cleaned path.
type MigrateRequest ¶
type MigrateRequest struct {
FromHost string `json:"from_host"`
ToHost string `json:"to_host"`
Template string `json:"template"`
Slug string `json:"slug"`
Parameters map[string]any `json:"parameters"`
}
MigrateRequest is the POST /migrate body and the migrate job's args.
type Observed ¶
type Observed struct {
Template string `json:"template"`
Slug string `json:"slug"`
Ready bool `json:"ready"`
Pod ObservedPod `json:"pod"`
Containers []ObservedContainer `json:"containers"`
Volumes []ObservedVolume `json:"volumes,omitempty"`
EnvSummary map[string]string `json:"env_summary,omitempty"`
Warnings []string `json:"warnings,omitempty"`
}
Observed is the JSON shape returned for an instance.
func Normalize ¶
func Normalize(p podman.Pod, template, slug string, vols []podman.Volume, secretEnvs map[string]bool) Observed
Normalize builds Observed from a Pod + the volumes the API thinks the instance owns. Env vars whose names appear in secretEnvs (the set derived from the template's secretKeyRef blocks) are dropped from env_summary so secret material never returns to the CMS. A defensive substring check on SECRET also catches anything not anchored to a known template.
type ObservedContainer ¶
type ObservedContainer struct {
Name string `json:"name"`
Image string `json:"image"`
ImageTag string `json:"image_tag,omitempty"`
Status string `json:"status"`
Health string `json:"health,omitempty"`
StartedAt time.Time `json:"started_at,omitempty"`
RestartCount int `json:"restart_count"`
Ports []ObservedPortMapping `json:"ports,omitempty"`
}
type ObservedPod ¶
type ObservedPortMapping ¶
type ObservedVolume ¶
type PlanIssue ¶
PlanIssue is a single reason a move is not clean: a blocking destination condition or an inconclusive (check_error) check.
type PlannedMove ¶
type PlannedMove struct {
Slug string `json:"slug"`
Template string `json:"template"`
ToHost string `json:"to_host"`
OK bool `json:"ok"` // true iff Issues is empty
Issues []PlanIssue `json:"issues"`
Provisions []string `json:"provisions"` // per-host secrets auto-provisioned on dest; informational, does not affect ok; [] not null
}
PlannedMove is one instance's planned move and its preflight verdict.
type RestoreRequest ¶
type RestoreRequest struct {
BackupID string `json:"backup_id"`
}
RestoreRequest is the restore job's args.
type Service ¶
type Service struct {
// contains filtered or unexported fields
}
Service orchestrates instance operations against podman hosts.
func (*Service) Apply ¶
func (s *Service) Apply(ctx context.Context, host string, req ApplyRequest, opts ApplyOptions) error
Apply creates or replaces an instance. If opts.Replace is false and the pod exists, returns ErrInstanceExists. Unless opts.SkipPull is set, every container image referenced in the rendered Pod spec is pulled before the manifest is played. Apply acquires the per-host lock (domain-carrying requests only, taken before the instance lock — a consistent order so the two never deadlock) and the per-instance lock, then runs applyLocked.
func (*Service) ApplyAndObserve ¶
func (s *Service) ApplyAndObserve(ctx context.Context, host string, req ApplyRequest, opts ApplyOptions) (Observed, error)
ApplyAndObserve creates or replaces an instance (via Apply), waits for container healthchecks to pass (up to deployVerifyTimeout), then returns the observed state. On readiness timeout the operation still succeeds but Observed.Warnings carries a human-readable message.
func (*Service) Backup ¶
func (s *Service) Backup(ctx context.Context, req BackupRequest, step func(step, detail string)) error
Backup snapshots every volume of an instance into the blob store: stop, export each volume (teed into the blob write and the manifest build in one pass), record metadata, restart. The instance is restarted even on failure; it is only restarted at all if it was running to begin with. step is a best-effort progress callback (may be nil).
func (*Service) CheckBackupable ¶
CheckBackupable runs the cheap synchronous validation the POST handler needs: known host, known template, stored spec present, blob store wired.
func (*Service) CheckMigratable ¶
func (s *Service) CheckMigratable(ctx context.Context, req MigrateRequest) error
CheckMigratable runs the cheap synchronous validation the POST handler needs: distinct known hosts, known template, and an existing stored spec. No mutation.
func (*Service) CheckRestorable ¶
CheckRestorable runs the synchronous validation the POST handler needs and returns the backup row: row exists and is complete, host known and not draining, instance (spec) still present. The drain check is upfront so a draining host can't fail the job after teardown.
func (*Service) CloneTemplate ¶
CloneTemplate copies srcID to a new template with id newID and Origin "user". ErrUnknownTemplate if src is absent; ErrTemplateExists if newID is taken.
func (*Service) CopyVolume ¶
CopyVolume streams a named volume's contents from one host to another through an in-process pipe — the data crosses the daemon's network (two connections) but never its disk. The destination volume must already exist. The source is only ever read, so a failed copy leaves it untouched (migrate relies on this).
func (*Service) CreateTemplate ¶
CreateTemplate validates t and persists it; ErrTemplateExists if the id already exists. Origin defaults to "user" when the caller leaves it blank.
func (*Service) Delete ¶
Delete removes the pod and optionally its volumes and per-instance secrets.
func (*Service) DeleteBackup ¶
DeleteBackup removes a backup's blobs, then its row — in that order, so a crash between the two leaves a harmless blob-less row rather than orphaned blobs. Callers must check BackupDeletable first.
func (*Service) DeleteHostSecret ¶
DeleteHostSecret removes a host secret from the host and from the store. Like PutHostSecret, the store write is a non-atomic tail: a store-delete failure surfaces after the host removal succeeded, but a retry skips the already-gone host secret and re-deletes the store row, so the divergence is self-healing.
func (*Service) DeleteTemplate ¶
DeleteTemplate removes a template. Unless force is set it is rejected with ErrTemplateInUse when any instance on any host references it.
func (*Service) DeleteVolume ¶
DeleteVolume removes a named volume on a host. Idempotent.
func (*Service) GetTemplate ¶
GetTemplate returns a stored template by id (ErrUnknownTemplate if absent).
func (*Service) HostCounts ¶
func (s *Service) HostCounts(ctx context.Context, host string) (instances, containers int, err error)
HostCounts returns the number of managed instances and the total number of their containers on a host, in a single ListAllInstances sweep.
func (*Service) HostSecrets ¶
HostSecrets lists secrets on a host.
func (*Service) InstanceCount ¶
InstanceCount returns the total number of podman-api-managed pods on a host across all known templates. Used by /hosts to surface drain decisions.
func (*Service) InstanceSecretState ¶
func (s *Service) InstanceSecretState(ctx context.Context, host, tmpl, slug string) (map[string]bool, error)
InstanceSecretState reports, per stored per-instance secret name, that a value is present — presence only, never the value (the secret model is write-only). Names a template declares but the instance never set are simply absent from the map. Returns ErrInstanceNotFound when no spec is stored, or the store's error (incl. store.ErrSpecCorrupt or store.ErrSecretsUndecryptable) when the spec cannot be read.
func (*Service) InstanceVolumes ¶
func (s *Service) InstanceVolumes(ctx context.Context, host, tmpl, slug string) ([]podman.Volume, error)
InstanceVolumes returns the named volumes the API believes belong to this instance. Volumes that don't exist on the host are omitted (no error).
func (*Service) ListAllInstances ¶
ListAllInstances returns every podman-api-managed pod on a host across all known templates. The result is the union of List(host, t) for each catalog template id, so a pod for a template the daemon doesn't know about is silently omitted.
func (*Service) ListBackups ¶
func (s *Service) ListBackups(ctx context.Context, host, tmpl, slug string, limit int) ([]store.Backup, error)
ListBackups returns an instance's backups, newest first.
func (*Service) Logs ¶
func (s *Service) Logs(ctx context.Context, host, tmpl, slug, container string, opts podman.LogOptions) (<-chan podman.LogLine, error)
Logs returns a channel of log lines from one container in an instance.
func (*Service) Migrate ¶
func (s *Service) Migrate(ctx context.Context, req MigrateRequest, step func(step, detail string)) error
Migrate moves an instance from one host to another: stop source, copy volumes, apply the spec on the destination, verify it is healthy, then reap the source. Failures before the destination is verified roll back. step is a best-effort progress callback (may be nil).
func (*Service) PlanEvacuation ¶
func (s *Service) PlanEvacuation(ctx context.Context, req EvacuateRequest) (EvacuationPlan, error)
PlanEvacuation previews an evacuation without mutating anything or enqueuing a job. It defers to ResolveEvacuation for static map validation (returning the same sentinel errors the real POST /evacuate would), then runs the live destination preflight checks per resolved move, collecting every problem. A move with no issues would currently be accepted by the destination.
func (*Service) PortsInUse ¶
PortsInUse returns all currently-bound host ports on hostID.
func (*Service) PutHostSecret ¶
func (s *Service) PutHostSecret(ctx context.Context, host, name string, value []byte, persist bool) error
PutHostSecret creates-or-rotates a host secret on the host, then (when persist is true) records the value so a later migrate/evacuate can re-provision it on a destination. We "rotate" by removing then recreating, since podman secrets are immutable. Push happens before persist: we never store a value we failed to apply to the host. The store write is a non-atomic tail — if it fails the host already holds the new value while the store lags; the caller's retry re-rotates and re-persists idempotently, so the divergence is self-healing.
func (*Service) ReconcileBackup ¶
func (s *Service) ReconcileBackup(ctx context.Context, req BackupRequest, step func(step, detail string)) (resolved, ok bool, message string, err error)
ReconcileBackup drives a backup interrupted by a daemon restart to a terminal state: mark the row failed (CAS — a row that already completed means the job finished its work and only the terminal write was lost), delete any partial blobs, and restart the instance. Returns (ok=true) when the backup actually completed, (ok=false, message) when it was failed. resolved=false only when the host is unreachable and the restart attempt was inconclusive.
Unlike Backup, which only restarts if the instance was running before the snapshot began, ReconcileBackup always attempts to restart: post-crash the prior run-state is unknowable, so reconcile errs on the side of availability. A deliberately-stopped instance interrupted mid-backup may therefore come back running.
func (*Service) ReconcileMigrate ¶
func (s *Service) ReconcileMigrate(ctx context.Context, req MigrateRequest, step func(step, detail string)) (resolved, succeeded bool, message string, err error)
ReconcileMigrate drives a migrate that was interrupted by a daemon restart to a consistent state, inspecting the real host state rather than trusting any persisted progress. It returns:
resolved=false inconclusive (a host was unreachable) — caller retries later resolved=true, succeeded=true rolled forward (or the commit had finished) resolved=true, succeeded=false rolled back, or the dest is an orphan left in place
message is an operator-facing summary recorded in the job's error field for terminal failed outcomes; it is empty for success and for inconclusive results.
step is a best-effort progress callback (may be nil). It reuses the same primitives as Migrate (waitRunning/Start/Delete) and takes migrateLock so it cannot race a re-issued migrate of the same instance.
func (*Service) ReconcileSpecsOnHost ¶
ReconcileSpecsOnHost checks every stored instance spec on host against real pod state and re-converges any that are missing (not running). It is called once at daemon startup as a one-shot boot converge, so managed pods survive a host reboot. Errors are logged per-instance and never propagated to the HTTP layer — the method always returns nil (it tolerates any failure by logging and continuing so a partial host outage does not block the rest).
Concurrency: per-instance operations are serialized under the existing per-instance lock so this cannot race a concurrent Apply/Delete/Upgrade. No per-host lock is taken because boot converge re-creates only instances whose store row already exists and whose domains are already claimed — it creates no new cross-instance domain claims.
Limitations (by design):
- No image pull: images are expected to be cached from the original deploy.
- One-shot: called once on startup; no periodic drift-correction loop.
- Template-missing instances are skipped with a warning (not reaped).
- Secrets-undecryptable instances are skipped (wrong key file — operator must restart with the correct -spec-key-file).
func (*Service) ResolveEvacuation ¶
func (s *Service) ResolveEvacuation(ctx context.Context, req EvacuateRequest) ([]MigrateRequest, error)
ResolveEvacuation validates the request against the specs stored on FromHost and returns the per-instance migrate plan, sorted by slug for determinism. It is pure read/validation (no mutation) and is called both synchronously by the POST handler (fast-fail, result discarded) and by the evacuate job handler at execution time (state may have drifted since enqueue).
func (*Service) Restore ¶
func (s *Service) Restore(ctx context.Context, req RestoreRequest, step func(step, detail string)) error
Restore replaces an instance's volumes in place from a backup: stop, tear down containers + volumes, recreate volumes from blobs, verify each against the stored manifest, re-apply the CURRENT spec, wait healthy. There is no rollback: a failure after teardown leaves the instance DOWN with volumes partially restored, but the spec row is preserved so the restore can be retried. The job error names the failed step. step is a best-effort progress callback (may be nil).
func (*Service) RotateInstanceSecrets ¶
func (s *Service) RotateInstanceSecrets(ctx context.Context, host, tmpl, slug string, newSecrets map[string]string) error
RotateInstanceSecrets overlays newSecrets onto the instance's stored per-instance secrets and re-applies (Replace=true), restarting the pod. Names absent from newSecrets keep their existing value — callers are write-only and never see current values. An empty newSecrets is rejected so a blank submit does not pointlessly restart the instance. Returns ErrInstanceNotFound when no spec is stored, or the store's error (incl. store.ErrSpecCorrupt or store.ErrSecretsUndecryptable) when the spec cannot be read.
The load (GetSpec) and re-apply (applyLocked) happen atomically under the per-instance lock: rotation is a read-modify-write of the stored secrets, so holding the lock across both halves keeps a concurrent rotation/upgrade of the same instance from reading the pre-commit spec and dropping this update. It takes only the instance lock (no host lock): rotation re-applies the instance's own already-persisted domains unchanged, which validateIngress excludes from its uniqueness check, so it can never create a new cross-instance domain claim and needs no per-host lock. (If a future edit let this method *change* domains, the missing host lock would become a real bug — the no-hostLock safety rests on domains being unchanged.) (#114)
func (*Service) SetBlobStore ¶
SetBlobStore wires the backup artifact store. Backups/restores are refused (ErrBackupsDisabled) until this is set; main always sets it.
func (*Service) SetHosts ¶
SetHosts atomically replaces the live host set. Used by main on SIGHUP to pick up edits to hosts/*.yaml (e.g. flipping drain) without restart.
func (*Service) SetIngress ¶
func (s *Service) SetIngress(c ingress.Controller, network string)
SetIngress enables ingress reconciliation. network is the shared podman network app pods join; passing a real controller marks ingress enabled so Apply will accept domains. Call with ingress.Disabled{} and "" to disable.
func (*Service) SetStore ¶
SetStore wires the template catalog + desired-state store. The store is mandatory — every template lookup and spec persist goes through it — so main must call this at startup, before the server begins accepting requests (tests pass a store.Memory). Unlike SetHosts it is NOT a concurrent hot-swap.
func (*Service) SetVerifyVolumes ¶
SetVerifyVolumes toggles post-copy volume integrity verification during migrate. Default true; set false (via -migrate-verify-volumes=false) to skip the extra source+dest re-export per volume.
func (*Service) Start ¶
Start starts a stopped instance and waits for container healthchecks to pass (up to deployVerifyTimeout). On readiness timeout the call still succeeds and Observed.Warnings carries a human-readable message.
func (*Service) Template ¶
Template returns one catalog template by ID (read-only view), or store.ErrNotFound. A point lookup for callers that need a single template, avoiding Templates()' full-catalog list + scan.
func (*Service) Templates ¶
Templates returns the catalog's templates (read-only view). A store error is propagated so callers can surface it (e.g. an HTTP 500) rather than rendering an empty catalog as if it succeeded.
func (*Service) UpdateTemplate ¶
UpdateTemplate validates t and upserts it. The template must already exist (ErrUnknownTemplate otherwise). The stored Origin is preserved so an edit cannot silently flip a "seed" template to "user".
func (*Service) Upgrade ¶
Upgrade replaces the pod with a new image. The pull happens inside Apply (which scans the rendered manifest and pulls every container image), so a bad image ref still fails fast — without a duplicate pre-pull here.
func (*Service) UpgradeImage ¶
UpgradeImage performs an image-only upgrade: it loads the instance's stored spec (parameters + secrets), overrides the "image" parameter, and re-applies with Replace. Existing secrets and parameters are reused as-is — the operator supplies only the new image; rotating a secret is a separate operation. Like RotateInstanceSecrets it sets AllowMissingSecrets, so a template that gained a required per-instance secret after the instance was deployed does not block an image upgrade of that already-running instance (the missing secret was already missing; the upgrade never worsens the pod). Returns ErrInstanceNotFound when no spec is stored for the instance.
The load (GetSpec) and re-apply (applyLocked) happen atomically under the per-instance lock: the image override is a read-modify-write of the stored parameters, so holding the lock across both halves keeps a concurrent rotation/upgrade of the same instance from reading the pre-commit spec and dropping this update. It takes only the instance lock (no host lock): the upgrade re-applies the instance's own already-persisted domains unchanged, which validateIngress excludes from its uniqueness check, so it can never create a new cross-instance domain claim and needs no per-host lock. (If a future edit let this method *change* domains, the missing host lock would become a real bug — the no-hostLock safety rests on domains being unchanged.) (#114)
type Store ¶
type Store interface {
store.Store
store.TemplateStore
store.BackupStore
}
Store is the persistence surface the instance Service needs: the desired-state spec/host-secret store plus the template catalog. main wires a single store.DB, which satisfies this; tests pass a store.Memory. The Service always has a store — callers MUST SetStore before use.