docker

package
v0.0.0-...-b38047d Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 28, 2026 License: MIT Imports: 32 Imported by: 0

Documentation

Overview

Package docker wraps the official Docker SDK with the small surface hope needs: list containers grouped into compose stacks by label, control a single container, and open log/stat streams. Domain types here are plain JSON shapes for the frontend — they do not leak SDK types over the wire.

Index

Constants

View Source
const (
	LabelProject = "com.docker.compose.project"
	LabelService = "com.docker.compose.service"
	LabelNumber  = "com.docker.compose.container-number"
	// LabelManaged marks a container/network/volume hope created via a deploy, so
	// the UI and teardown can tell hope-owned objects from externally-created ones.
	LabelManaged = "ink.hope.managed"
	// LabelSystem marks a hope-owned INFRASTRUCTURE network — the plugin bridge and
	// the tunnel fallback bridge. Distinct from LabelManaged (which also lands on
	// ordinary stack networks, which stay deletable): hope refuses to delete a network
	// carrying this, since removing it breaks plugin/tunnel connectivity.
	LabelSystem = "ink.hope.system"
)

Exported compose + hope label keys, so other packages (internal/deploy) build on these instead of redefining the same string literals. The unexported aliases in docker.go keep internal call sites terse.

View Source
const (
	LabelPlugin      = labelPlugin
	LabelPluginPort  = labelPluginPort
	LabelPluginPath  = labelPluginPath
	LabelPluginTitle = labelPluginTitle
	LabelPluginIcon  = labelPluginIcon
)

Exported plugin label keys — the installer stamps these on a deployed plugin container so discovery picks it up (the same labels an author would set by hand).

View Source
const PluginNetwork = "ink-plugins"

PluginNetwork is the dedicated user bridge hope uses to reach plugins on a daemon: hope (or the agent) and each enabled plugin container both join it, and hope dials the plugin by a stable alias on it — no published port, no hairpin, deterministic DNS. Created on demand per daemon (local socket, or an agent's daemon over tunnel).

Variables

This section is empty.

Functions

func PluginNetAlias

func PluginNetAlias(containerID string) string

PluginNetAlias is the DNS name hope dials a plugin container by on the shared network: its SHORT container id. docker's embedded DNS registers the short id (the default hostname) automatically on every user network the container joins — so a plain NetworkConnect (no explicit alias) is enough, sidestepping the "already connected, custom alias dropped" no-op. Always a valid DNS label (hex).

func ReplicaAlias

func ReplicaAlias(project, service string) string

ReplicaAlias is the stack-network alias hope assigns to every replica of a scaled service so a tunnel route can round-robin across them. The tunnels router ATTACHES this alias to each replica; OriginIndex RECONSTRUCTS it from labels to map a route origin back to its stack/service — the two must be byte-identical, so both go through here rather than hand-building the string.

func WithManaged

func WithManaged(in map[string]string) map[string]string

WithManaged tags labels with LabelManaged=1 (creating the map if needed).

Types

type API

type API interface {
	AddRegistryCreds(server, user, pass string, source RegistrySource)
	AllUpdates(ctx context.Context) ([]ClusterUpdate, time.Time, error)
	AttachNetwork(ctx context.Context, containerID, netName string, aliases []string) error
	AuthedRegistries() []string
	CachedStatus(ref string) string
	Close() error
	Connectors(ctx context.Context) ([]Connector, error)
	ContainerImage(ctx context.Context, id string) (string, error)
	ContainerMatchInfo(ctx context.Context, id string) (image string, labels map[string]string, err error)
	ContainerName(ctx context.Context, id string) (string, error)
	BuildImageStream(ctx context.Context, dockerfile, tag string, emit func(string)) error
	ContainerNetworks(ctx context.Context, id string) ([]string, error)
	ContainerSpecOf(ctx context.Context, id string) (*stackspec.ContainerSpec, error)
	CreateContainer(ctx context.Context, name string, spec stackspec.ContainerSpec, pull bool, emit func(string)) (string, error)
	CreateNetwork(ctx context.Context, spec stackspec.NetworkSpec) (string, error)
	CreateVolume(ctx context.Context, spec stackspec.VolumeSpec) (string, error)
	DeployConnector(ctx context.Context, name, tunnelID, token string, isDefault bool) (string, error)
	DetachNetwork(ctx context.Context, containerID, netName string) error
	DiskUsage(ctx context.Context) (any, error)
	DiskUsageCached() (any, time.Time)
	EnsurePluginNetwork(ctx context.Context) error
	EnsureTunnelsNetwork(ctx context.Context) (string, error)
	Exists(ctx context.Context, id string) bool
	History(ctx context.Context, id string) ([]ImageLayer, error)
	ImageByRef(ctx context.Context, ref string) (*ImageInfo, error)
	ImageInUse(ctx context.Context, id string) (bool, []ImageUser, error)
	Images(ctx context.Context) ([]ImageInfo, error)
	ImagesForProject(ctx context.Context, project string) ([]string, error)
	Info(ctx context.Context) (any, error)
	Inspect(ctx context.Context, id string) (any, error)
	IsConfigRegistry(server string) bool
	IsLocalSocket() bool
	Kill(ctx context.Context, id string) error
	NetworkByRef(ctx context.Context, ref string) (*NetworkInfo, error)
	NetworkExists(ctx context.Context, name string) (bool, error)
	Networks(ctx context.Context) ([]NetworkInfo, error)
	OriginIndex(ctx context.Context) (map[string]OriginRef, error)
	Ping(ctx context.Context) error
	PluginContainers(ctx context.Context) ([]PluginContainer, error)
	PluginDialCandidates(ctx context.Context, id string, port int) (netTargets, directTargets []string, attachNet string, err error)
	PluginNetworkIP(ctx context.Context, id string) string
	ProjectContainerIDs(ctx context.Context, project string) ([]string, error)
	ProjectContainers(ctx context.Context, project, service string) ([]ContainerRef, error)
	ProjectSpec(ctx context.Context, project string) (*stackspec.StackSpec, error)
	ProjectStats(ctx context.Context, project string) ([]ContainerStat, error)
	ProjectUpdates(ctx context.Context, project string) ([]ImageUpdate, error)
	PruneBuildCache(ctx context.Context) (uint64, error)
	PruneImages(ctx context.Context, all bool) (PruneResult, error)
	PruneImagesStream(ctx context.Context, all bool, emit func(string)) error
	PullContainers(ctx context.Context, ids []string, emit func(string)) error
	PullImage(ctx context.Context, ref string) error
	PullImageStream(ctx context.Context, ref string, emit func(string)) error
	Recreate(ctx context.Context, id string) error
	RecreateFromSpec(ctx context.Context, id string, spec stackspec.ContainerSpec, pull bool, emit func(string)) error
	RecreateManaged(ctx context.Context, id string) error
	RedeployContainer(ctx context.Context, id string, pull, force bool, emit func(string)) error
	RedeployProject(ctx context.Context, project string, pull, force bool, emit func(string)) error
	RefreshDiskUsage(ctx context.Context) (any, time.Time, error)
	RefreshImageStatus(ctx context.Context, ref string)
	RefreshProjectStatus(ctx context.Context, project string)
	RefreshUpdates(ctx context.Context)
	RegistryList() []RegistryEntry
	Remove(ctx context.Context, id string) error
	RemoveImage(ctx context.Context, id string, force bool) error
	RemoveManagedResources(ctx context.Context, project string, emit func(string)) (int, error)
	RemoveNetwork(ctx context.Context, id string) error
	RemoveRegistryCreds(server string) bool
	RemoveVolume(ctx context.Context, name string, force bool) error
	Restart(ctx context.Context, id string) error
	SDK() *client.Client
	SelfContainerID(ctx context.Context) string
	SelfID() string
	ServerInfo(ctx context.Context) (ServerInfo, error)
	SetSelfID(id string)
	SetUpdateCache(store UpdateCacheStore, key string)
	SetUpdateHook(fn func())
	Stacks(ctx context.Context) ([]StackSummary, error)
	Start(ctx context.Context, id string) error
	StartCredWatcher(ctx context.Context, every time.Duration)
	StartDiskCrawler(ctx context.Context, every time.Duration)
	StartUpdateCrawler(ctx context.Context, every time.Duration, cachePath string)
	StatsSnapshot(ctx context.Context, id string) (ContainerStat, error)
	Stop(ctx context.Context, id string) error
	Top(ctx context.Context, id string) (TopResult, error)
	VerifyRegistry(ctx context.Context, server, user, pass string) error
	VolumeExists(ctx context.Context, name string) (bool, error)
	Volumes(ctx context.Context) ([]VolumeInfo, error)
}

API is the full method surface of *Client. hosts.ActiveFor and friends hand callers an API rather than a concrete *Client, so the routers (containers/stacks/system/ deploy/tunnels/pluginhost) that reach the daemon through the hosts seam can be exercised in tests against a mock. *Client is the only production implementation; the assertion below fails the build if any signature drifts from it. Test mocks embed API and override only the methods the test touches.

type Client

type Client struct {
	// contains filtered or unexported fields
}

Client is hope's Docker facade over a single daemon endpoint. The underlying SDK handle is held atomically so the active daemon can be retargeted at runtime (Adopt) — e.g. binding a late-connecting agent tunnel as the primary host without rebuilding the routers that hold this client.

func New

func New(host, configPath string) (*Client, error)

New dials the Docker daemon at host (e.g. "unix:///var/run/docker.sock" or "tcp://host:2375") with API-version negotiation. configPath points at a docker config.json for private-registry pull credentials (empty = the default ~/.docker/config.json).

func NewOverDialer

func NewOverDialer(configPath string, dial func(ctx context.Context, network, addr string) (net.Conn, error)) (*Client, error)

NewOverDialer builds a Client that talks to a Docker daemon reached through a custom dialer — e.g. a hope-agent tunnel, where every connection is a stream back to a remote host's socket. The SDK speaks plain HTTP over the dialer, so all of hope's existing operations work against the remote daemon unchanged.

func (*Client) AddRegistryCreds

func (c *Client) AddRegistryCreds(server, user, pass string, source RegistrySource)

AddRegistryCreds registers an explicit registry credential and rebuilds the auth map. Works without a credential helper. source tags where it came from (config vs runtime db) so the UI can gate editing. Re-adding the same server upserts (replacing any prior entry for that host).

func (*Client) AllUpdates

func (c *Client) AllUpdates(ctx context.Context) ([]ClusterUpdate, time.Time, error)

AllUpdates maps the running containers to the cached freshness verdicts and returns them with the time of the last crawl. Containers whose ref hasn't been crawled yet read as "unknown".

func (*Client) AttachNetwork

func (c *Client) AttachNetwork(ctx context.Context, containerID, netName string, aliases []string) error

AttachNetwork connects a container to a network (optionally with aliases). A no-op-safe wrapper: "already exists" is treated as success.

func (*Client) AuthedRegistries

func (c *Client) AuthedRegistries() []string

AuthedRegistries lists the registries hope currently has credentials for — a startup diagnostic so "still rate-limited?" has an obvious answer.

func (*Client) BuildImageStream

func (c *Client) BuildImageStream(ctx context.Context, dockerfile, tag string, emit func(string)) error

BuildImageStream builds an image from a CONTEXTLESS Dockerfile — its text alone, with no build context. A Dockerfile that COPY/ADDs from a local path therefore can't resolve its sources; callers reject those before getting here (see the deploy engine / UI). It's the clean subset for one-off "bring your own Dockerfile" deploys (FROM + RUN + ENV + CMD). Progress lines from the daemon are forwarded via emit; a mid-stream error (a failed RUN, an unreachable base image) is surfaced as an error rather than draining silently.

func (*Client) CachedStatus

func (c *Client) CachedStatus(ref string) string

CachedStatus returns the cached freshness verdict for an image ref ("current" | "outdated" | "unknown") with no network call.

func (*Client) Close

func (c *Client) Close() error

Close releases the underlying client.

func (*Client) Connectors

func (c *Client) Connectors(ctx context.Context) ([]Connector, error)

Connectors lists hope-managed cloudflared connectors on this daemon.

func (*Client) ContainerImage

func (c *Client) ContainerImage(ctx context.Context, id string) (string, error)

ContainerImage returns the image reference a container was created from.

func (*Client) ContainerMatchInfo

func (c *Client) ContainerMatchInfo(ctx context.Context, id string) (image string, labels map[string]string, err error)

ContainerMatchInfo returns a container's image ref + labels, for evaluating a plugin's container-surface match against it.

func (*Client) ContainerName

func (c *Client) ContainerName(ctx context.Context, id string) (string, error)

ContainerName returns a container's friendly name (leading slash trimmed), for user-facing notifications. Best-effort — callers fall back to the short id.

func (*Client) ContainerNetworks

func (c *Client) ContainerNetworks(ctx context.Context, id string) ([]string, error)

ContainerNetworks returns the user-defined networks a container is attached to.

func (*Client) ContainerSpecOf

func (c *Client) ContainerSpecOf(ctx context.Context, id string) (*stackspec.ContainerSpec, error)

ContainerSpecOf reconstructs a single container's editable spec from its live inspect — the seed for the "edit container" form. name is the compose service when set, else the container name.

func (*Client) CreateContainer

func (c *Client) CreateContainer(ctx context.Context, name string, spec stackspec.ContainerSpec, pull bool, emit func(string)) (string, error)

CreateContainer creates and starts a container from spec under the given docker name. spec.Labels, spec.Networks and spec.Image are taken as final (the deploy engine resolves compose labels, network name prefixes, etc. before calling). When pull is true the image is pulled first, streaming progress to emit. Returns the new container id.

func (*Client) CreateNetwork

func (c *Client) CreateNetwork(ctx context.Context, spec stackspec.NetworkSpec) (string, error)

CreateNetwork creates a network from spec, returning its id. It generalizes the hardcoded bridge create used for the tunnels fallback network.

func (*Client) CreateVolume

func (c *Client) CreateVolume(ctx context.Context, spec stackspec.VolumeSpec) (string, error)

CreateVolume creates a named volume from spec, returning its name.

func (*Client) DeployConnector

func (c *Client) DeployConnector(ctx context.Context, name, tunnelID, token string, isDefault bool) (string, error)

DeployConnector pulls cloudflared and runs it as a hope-managed connector for the given tunnel token, labeled so Connectors() discovers it. Returns the new container id.

func (*Client) DetachNetwork

func (c *Client) DetachNetwork(ctx context.Context, containerID, netName string) error

DetachNetwork disconnects a container from a network (ignores "not connected").

func (*Client) DiskUsage

func (c *Client) DiskUsage(ctx context.Context) (any, error)

DiskUsage returns the daemon's disk-usage breakdown.

func (*Client) DiskUsageCached

func (c *Client) DiskUsageCached() (any, time.Time)

DiskUsageCached returns the last crawled disk usage and when it was taken.

func (*Client) EnsurePluginNetwork

func (c *Client) EnsurePluginNetwork(ctx context.Context) error

EnsurePluginNetwork creates the shared ink-plugins bridge if missing (idempotent).

func (*Client) EnsureTunnelsNetwork

func (c *Client) EnsureTunnelsNetwork(ctx context.Context) (string, error)

EnsureTunnelsNetwork makes sure the fallback user-defined bridge exists (for loose containers that only have the default bridge, which lacks name DNS).

func (*Client) Exists

func (c *Client) Exists(ctx context.Context, id string) bool

Exists reports whether a container id/name resolves — used by the logstream plugin to reject a stream before the first byte.

func (*Client) History

func (c *Client) History(ctx context.Context, id string) ([]ImageLayer, error)

History returns an image's layer history (`docker history`) — how it was built, layer by layer, with per-layer size. Newest layer first.

func (*Client) ImageByRef

func (c *Client) ImageByRef(ctx context.Context, ref string) (*ImageInfo, error)

ImageByRef finds a single local image by id (full or short), an exact tag, or a repo digest — for the shared image-detail modal opened from anywhere a container's image is shown. Returns (nil, nil) when nothing matches.

func (*Client) ImageInUse

func (c *Client) ImageInUse(ctx context.Context, id string) (bool, []ImageUser, error)

ImageInUse reports whether any container (running or stopped) references the image, and by whom. Used to refuse a (force-)remove of an in-use image at the RPC layer — the UI hides that action, but the guard must live on the server.

func (*Client) Images

func (c *Client) Images(ctx context.Context) ([]ImageInfo, error)

Images lists local top-level images, tagging each with whether a container uses it and whether it's dangling (untagged). Sorted largest first.

func (*Client) ImagesForProject

func (c *Client) ImagesForProject(ctx context.Context, project string) ([]string, error)

ImagesForProject returns the unique image references used by a project's containers, for a stack-wide pull.

func (*Client) Info

func (c *Client) Info(ctx context.Context) (any, error)

Info returns daemon-wide info (version, container/image counts, resources).

func (*Client) Inspect

func (c *Client) Inspect(ctx context.Context, id string) (any, error)

Inspect returns the full raw inspect JSON for a container (rendered as-is in the UI's inspect panel).

func (*Client) IsConfigRegistry

func (c *Client) IsConfigRegistry(server string) bool

IsConfigRegistry reports whether a server's credential is config-sourced (config.json or [registry]) and therefore read-only. Used to reject UI edits that would shadow a config entry.

func (*Client) IsLocalSocket

func (c *Client) IsLocalSocket() bool

IsLocalSocket reports whether this client talks to a local unix socket (as opposed to a remote tcp:// daemon). Only for a local socket can hope join the plugin's network and reach it by container DNS; a remote tcp daemon needs a published port.

func (*Client) Kill

func (c *Client) Kill(ctx context.Context, id string) error

Kill sends SIGKILL to a container.

func (*Client) NetworkByRef

func (c *Client) NetworkByRef(ctx context.Context, ref string) (*NetworkInfo, error)

NetworkByRef finds a single network by id (full or short) or exact name, with its attached-container mapping — for the shared network-detail modal opened from anywhere a network is shown. Returns (nil, nil) when nothing matches.

func (*Client) NetworkExists

func (c *Client) NetworkExists(ctx context.Context, name string) (bool, error)

NetworkExists reports whether a network with the exact name already exists.

func (*Client) Networks

func (c *Client) Networks(ctx context.Context) ([]NetworkInfo, error)

Networks lists Docker networks with the containers attached to each (the "who's on this network" reverse mapping), busiest first.

func (*Client) OriginIndex

func (c *Client) OriginIndex(ctx context.Context) (map[string]OriginRef, error)

OriginIndex maps container name AND per-network alias -> OriginRef, so an ingress service URL host ("blog-web-1" or "hope-blog-web") resolves to a stack.

func (*Client) Ping

func (c *Client) Ping(ctx context.Context) error

Ping verifies the daemon is reachable.

func (*Client) PluginContainers

func (c *Client) PluginContainers(ctx context.Context) ([]PluginContainer, error)

PluginContainers lists containers on this daemon that opt into the plugin system (label hope.plugin truthy) and declare a valid port. Mirrors Connectors.

func (*Client) PluginDialCandidates

func (c *Client) PluginDialCandidates(ctx context.Context, id string, port int) (netTargets, directTargets []string, attachNet string, err error)

PluginDialCandidates returns ordered host:port addresses hope can try to reach a container's port, plus the user network to attach to. Order:

  1. the container's network IP (works when hope shares the network — the normal containerized deployment, after AttachNetwork);
  2. any PUBLISHED host port at 127.0.0.1 (works for NATIVE hope, including Docker Desktop, which forwards published ports to localhost) — the dev fast path.

Errors only if the container exposes neither. PluginDialCandidates resolves how hope can reach a plugin container. It returns two kinds of address:

  • netTargets: the container's network IP:port — reachable only from the plugin's docker network (hope attaches to it locally, or the agent dials it on the host).
  • directTargets: a published host port at an address hope can reach DIRECTLY — 127.0.0.1 for a local daemon, or the daemon's host IP when the daemon is a remote TCP endpoint (so a plugin published on the remote host is reachable at <daemon-host>:<published> without an agent on that host).

attachNet is the user network name to attach the routing container to for the container-IP path.

func (*Client) PluginNetworkIP

func (c *Client) PluginNetworkIP(ctx context.Context, id string) string

PluginNetworkIP returns the container's IP address on the shared ink-plugins network, or "" if it isn't attached (or can't be inspected). Used as a DNS-INDEPENDENT dial fallback right after a live AttachNetwork: docker's embedded DNS record for the freshly-joined alias can lag the first request (NXDOMAIN), whereas the endpoint IP is present in inspect immediately. A port-less plugin (no published port) has no other fallback, so without this a disable→re-enable races the DNS registration and the schema query fails.

func (*Client) ProjectContainerIDs

func (c *Client) ProjectContainerIDs(ctx context.Context, project string) ([]string, error)

ProjectContainerIDs returns the container ids of a compose project, ordered by service then container-number for stable operation order.

func (*Client) ProjectContainers

func (c *Client) ProjectContainers(ctx context.Context, project, service string) ([]ContainerRef, error)

ProjectContainers returns refs for a project, optionally filtered to a single compose service. Ordered by service then container-number.

func (*Client) ProjectSpec

func (c *Client) ProjectSpec(ctx context.Context, project string) (*stackspec.StackSpec, error)

ProjectSpec reconstructs a StackSpec from the live containers of a project (compose labels + inspect). It powers the editor's "adopt an existing stack" path — including stacks hope did not deploy. Replicas collapse to one service; the reconstruction is best-effort and portable (no live IPs/MACs).

func (*Client) ProjectStats

func (c *Client) ProjectStats(ctx context.Context, project string) ([]ContainerStat, error)

ProjectStats snapshots every running container in a compose project, concurrently. Non-running containers are skipped; per-container errors are dropped so one bad container doesn't fail the whole snapshot.

func (*Client) ProjectUpdates

func (c *Client) ProjectUpdates(ctx context.Context, project string) ([]ImageUpdate, error)

ProjectUpdates checks every container in a project against its registry. The registry lookup is done once per distinct image ref (deduped), concurrently.

func (*Client) PruneBuildCache

func (c *Client) PruneBuildCache(ctx context.Context) (uint64, error)

PruneBuildCache clears the builder cache (the layer/step cache from image builds — often the biggest reclaimable chunk, and invisible to image prune). Returns bytes reclaimed.

func (*Client) PruneImages

func (c *Client) PruneImages(ctx context.Context, all bool) (PruneResult, error)

PruneImages removes unreferenced images: dangling-only by default, or every unused image when all is true. Returns how many were deleted and bytes freed.

func (*Client) PruneImagesStream

func (c *Client) PruneImagesStream(ctx context.Context, all bool, emit func(string)) error

PruneImagesStream removes unused images one at a time, emitting a line per image (removed / skipped + reason) so the UI can show live progress and the exact reason an image can't be deleted.

func (*Client) PullContainers

func (c *Client) PullContainers(ctx context.Context, ids []string, emit func(string)) error

PullContainers pulls the images backing the given containers (deduped), streaming progress to emit. It does not recreate anything.

func (*Client) PullImage

func (c *Client) PullImage(ctx context.Context, ref string) error

PullImage pulls ref and consumes the progress stream, surfacing any error the registry reports MID-STREAM (rate limits, auth failures). The daemon returns those as JSON `error` lines, not as a transport error — so draining blindly would make a throttled pull look successful and a redeploy keep the old image.

func (*Client) PullImageStream

func (c *Client) PullImageStream(ctx context.Context, ref string, emit func(string)) error

PullImageStream pulls ref and forwards human-readable progress to emit, one line per layer status change (chatty per-chunk progress is deduped). Returns the registry's error on failure, like PullImage.

func (*Client) Recreate

func (c *Client) Recreate(ctx context.Context, id string) error

Recreate rebuilds a container in place so it picks up a freshly pulled image, preserving its config, host config, name, labels (so it stays grouped in its compose project), and network attachments. This is the API-only equivalent of `docker compose up -d --force-recreate` for one container.

func (*Client) RecreateFromSpec

func (c *Client) RecreateFromSpec(ctx context.Context, id string, spec stackspec.ContainerSpec, pull bool, emit func(string)) error

RecreateFromSpec rebuilds a container in place under its current name with the edited spec — the API-only "edit container settings". The container's internal compose/hope labels are preserved (so it stays grouped/managed); the user labels come from the spec. Networks in the spec are taken as-is (the edit form seeds them from the live container).

func (*Client) RecreateManaged

func (c *Client) RecreateManaged(ctx context.Context, id string) error

RecreateManaged recreates a container, but if it's a hope-image container (hope itself, or a hope-agent — anything carrying HOPE_MANAGED=1) it hands the job to a detached helper. Recreating such a container directly would stop it mid-request over the very connection it provides (hope's process, or the agent tunnel), severing that connection before the recreate completes — the EOF. Keyed on the image marker, not os.Hostname()-based self detection (which is unreliable when the container runs with a custom --hostname or host network).

func (*Client) RedeployContainer

func (c *Client) RedeployContainer(ctx context.Context, id string, pull, force bool, emit func(string)) error

RedeployContainer pulls a container's image (streaming progress to emit) then recreates it, emitting step lines. The terminal "done" frame is the caller's.

func (*Client) RedeployProject

func (c *Client) RedeployProject(ctx context.Context, project string, pull, force bool, emit func(string)) error

RedeployProject pulls every image in a project then recreates each container, streaming progress to emit. With pull off it skips the pull; with force off it leaves containers already running the current image untouched.

func (*Client) RefreshDiskUsage

func (c *Client) RefreshDiskUsage(ctx context.Context) (any, time.Time, error)

RefreshDiskUsage runs a live df, updates the cache, and returns it — for the user-triggered "refresh" button.

func (*Client) RefreshImageStatus

func (c *Client) RefreshImageStatus(ctx context.Context, ref string)

RefreshImageStatus re-checks one image ref and updates the cache. Call it after a pull/redeploy so the freshness tag doesn't go stale.

func (*Client) RefreshProjectStatus

func (c *Client) RefreshProjectStatus(ctx context.Context, project string)

RefreshProjectStatus re-checks every distinct image ref in a project and merges the results into the cache (used after stack-wide pull/redeploy).

func (*Client) RefreshUpdates

func (c *Client) RefreshUpdates(ctx context.Context)

RefreshUpdates runs an immediate cluster-wide crawl (user-triggered) and updates the cache.

func (*Client) RegistryList

func (c *Client) RegistryList() []RegistryEntry

RegistryList returns the credential-free view of every known registry (config + runtime), sorted by server, for the UI. Passwords are never included.

func (*Client) Remove

func (c *Client) Remove(ctx context.Context, id string) error

Remove stops (graceful) then removes a container. Force covers the case where it's already stopped or won't stop in time.

func (*Client) RemoveImage

func (c *Client) RemoveImage(ctx context.Context, id string, force bool) error

RemoveImage deletes a single image (force allows removing a tagged image even if it has stopped containers / multiple tags).

func (*Client) RemoveManagedResources

func (c *Client) RemoveManagedResources(ctx context.Context, project string, emit func(string)) (int, error)

RemoveManagedResources removes the hope-managed networks and volumes belonging to a project (labeled com.docker.compose.project=<project> AND ink.hope.managed=1) — the ones a hope deploy created. Externally-created resources are never touched. Returns the count removed. Errors on individual resources (e.g. still in use) are emitted but not fatal.

func (*Client) RemoveNetwork

func (c *Client) RemoveNetwork(ctx context.Context, id string) error

RemoveNetwork deletes a network by id.

func (*Client) RemoveRegistryCreds

func (c *Client) RemoveRegistryCreds(server string) bool

RemoveRegistryCreds drops a runtime (db) credential for a server and rebuilds the auth map. Config-sourced creds are left untouched (read-only). Reports whether anything was removed.

func (*Client) RemoveVolume

func (c *Client) RemoveVolume(ctx context.Context, name string, force bool) error

RemoveVolume deletes a volume by name (force removes even if referenced).

func (*Client) Restart

func (c *Client) Restart(ctx context.Context, id string) error

Restart restarts a container.

func (*Client) SDK

func (c *Client) SDK() *client.Client

SDK exposes the raw client for streaming callers (logstream plugin).

func (*Client) SelfContainerID

func (c *Client) SelfContainerID(ctx context.Context) string

SelfContainerID resolves the container id of the process on THIS daemon that should join the plugin network — hope on the local daemon, the agent on a tunnel. It tries the hostname/hint first (usually the container id), then falls back to the daemon's HOPE_MANAGED container (hope + agent images bake HOPE_MANAGED=1) — robust even when a custom --hostname hides the id. Returns "" if none is found.

func (*Client) SelfID

func (c *Client) SelfID() string

SelfID is the exported view of selfID — this client's own container id (hope's on the local daemon, the agent's on a tunnel). Used by the plugin host to attach hope to a plugin's network before dialing it.

func (*Client) ServerInfo

func (c *Client) ServerInfo(ctx context.Context) (ServerInfo, error)

ServerInfo returns the daemon version + counts.

func (*Client) SetSelfID

func (c *Client) SetSelfID(id string)

SetSelfID records the container id this client runs as (used by agent clients so self-recreate detection works across the tunnel).

func (*Client) SetUpdateCache

func (c *Client) SetUpdateCache(store UpdateCacheStore, key string)

SetUpdateCache routes freshness-cache persistence through a key/value store (the state db) under the given key, instead of a JSON file. Call before StartUpdateCrawler.

func (*Client) SetUpdateHook

func (c *Client) SetUpdateHook(fn func())

SetUpdateHook registers a callback fired once per crawl when some image newly flips to "outdated". The caller closes over its host id + the event bus to publish an image.update event. Set before StartUpdateCrawler.

func (*Client) Stacks

func (c *Client) Stacks(ctx context.Context) ([]StackSummary, error)

Stacks lists all containers (running and stopped) grouped by compose project. Containers without a compose project land under "(ungrouped)".

func (*Client) Start

func (c *Client) Start(ctx context.Context, id string) error

Start starts a stopped container.

func (*Client) StartCredWatcher

func (c *Client) StartCredWatcher(ctx context.Context, every time.Duration)

StartCredWatcher polls the config.json checksum and reloads credentials when it changes, so a fresh `docker login` takes effect without restarting hope.

func (*Client) StartDiskCrawler

func (c *Client) StartDiskCrawler(ctx context.Context, every time.Duration)

StartDiskCrawler computes docker disk usage on boot, then every `every`. `docker system df` is expensive on big hosts, so the UI reads the cache.

func (*Client) StartUpdateCrawler

func (c *Client) StartUpdateCrawler(ctx context.Context, every time.Duration, cachePath string)

StartUpdateCrawler loads any persisted cache, runs an immediate crawl, then re-crawls every `every`. cachePath (if non-empty, and no state-db backend is set) persists the cache across restarts — mount it to survive recreates.

func (*Client) StatsSnapshot

func (c *Client) StatsSnapshot(ctx context.Context, id string) (ContainerStat, error)

StatsSnapshot reads a single CPU/memory sample for a container. CPU% needs a delta, so it reads two frames of the stats stream (the second carries a valid precpu) and then closes.

func (*Client) Stop

func (c *Client) Stop(ctx context.Context, id string) error

Stop stops a running container with the daemon's default grace period.

func (*Client) Top

func (c *Client) Top(ctx context.Context, id string) (TopResult, error)

Top returns a container's running processes (the `docker top` equivalent): the daemon runs ps in the container's PID namespace and returns the columns + rows. Works over the agent tunnel like every other call.

func (*Client) VerifyRegistry

func (c *Client) VerifyRegistry(ctx context.Context, server, user, pass string) error

VerifyRegistry checks a credential against the registry by performing a login (auth handshake only, no image pull), so the UI can reject bad creds at add time instead of failing silently on the next pull. Returns nil when the creds authenticate.

func (*Client) VolumeExists

func (c *Client) VolumeExists(ctx context.Context, name string) (bool, error)

VolumeExists reports whether a named volume already exists.

func (*Client) Volumes

func (c *Client) Volumes(ctx context.Context) ([]VolumeInfo, error)

Volumes lists Docker volumes with the containers mounting each (the reverse mapping), busiest first.

type ClusterUpdate

type ClusterUpdate struct {
	ID      string `json:"id"`
	Project string `json:"project"`
	Service string `json:"service"`
	Name    string `json:"name"`
	Image   string `json:"image"`
	Status  string `json:"status"`
	Detail  string `json:"detail,omitempty"`
}

ClusterUpdate is a per-container freshness row spanning all projects, for the dashboard. Carries enough identity to render and link the container.

type Connector

type Connector struct {
	ContainerID string   `json:"container_id"`
	Name        string   `json:"name"` // container name (no leading slash)
	TunnelID    string   `json:"tunnel_id"`
	Title       string   `json:"title"`    // friendly label, else name
	Default     bool     `json:"default"`  // the shared/default connector
	Project     string   `json:"project"`  // compose project, if the connector lives in a stack
	Networks    []string `json:"networks"` // user-defined networks it's attached to
	Image       string   `json:"image"`    // the cloudflared image ref
	Running     bool     `json:"running"`
}

Connector is a cloudflared container hope will manage routes for.

type ContainerRef

type ContainerRef struct {
	ID      string
	Name    string
	Service string
}

ContainerRef is a minimal container identity for multiplexed log streaming.

type ContainerStat

type ContainerStat struct {
	ID         string  `json:"id"`
	CPUPercent float64 `json:"cpu_percent"`
	MemUsed    uint64  `json:"mem_used"`
	MemLimit   uint64  `json:"mem_limit"`
}

ContainerStat is a point-in-time CPU/memory reading for one container.

type ContainerSummary

type ContainerSummary struct {
	ID      string            `json:"id"`
	Name    string            `json:"name"`
	Service string            `json:"service"`
	Image   string            `json:"image"`
	State   string            `json:"state"`  // running, exited, restarting, ...
	Status  string            `json:"status"` // human "Up 3 days" / "Restarting (2) ..."
	Health  string            `json:"health"` // healthy/unhealthy/starting/""
	Created int64             `json:"created"`
	Number  int               `json:"number"` // compose container-number
	Ports   []string          `json:"ports"`
	Labels  map[string]string `json:"labels,omitempty"`
}

ContainerSummary is the per-container shape sent to the frontend.

type ImageInfo

type ImageInfo struct {
	ID       string      `json:"id"`
	Tags     []string    `json:"tags"`
	Size     int64       `json:"size"`
	Created  int64       `json:"created"` // unix seconds
	Dangling bool        `json:"dangling"`
	InUse    bool        `json:"in_use"`
	UsedBy   []ImageUser `json:"used_by"`           // containers referencing this image
	Registry string      `json:"registry"`          // where it came from: registry host (docker.io, ghcr.io, ...)
	Digests  []string    `json:"digests,omitempty"` // repo@sha256 refs (the pulled-from source)
}

ImageInfo is a clean, frontend-facing view of a local image.

type ImageLayer

type ImageLayer struct {
	ID        string   `json:"id"`         // layer image id, or "<missing>" for squashed base layers
	Created   int64    `json:"created"`    // unix seconds
	CreatedBy string   `json:"created_by"` // the Dockerfile instruction that built it
	Size      int64    `json:"size"`       // bytes this layer adds
	Comment   string   `json:"comment"`
	Tags      []string `json:"tags"`
	Empty     bool     `json:"empty"` // a metadata-only layer (0 bytes, e.g. ENV/LABEL/CMD)
}

ImageLayer is one entry of an image's build history (`docker history`): the instruction that created the layer, its size, and age. Layers are returned newest-first (as the daemon reports them).

type ImageUpdate

type ImageUpdate struct {
	ID     string `json:"id"`
	Image  string `json:"image"`
	Status string `json:"status"`
	Detail string `json:"detail,omitempty"`
}

ImageUpdate reports whether a container's image is current with its registry. Status is one of: "current", "outdated", "unknown".

type ImageUser

type ImageUser struct {
	ID      string `json:"id"`
	Name    string `json:"name"`
	Service string `json:"service"`
	Project string `json:"project"`
}

ImageUser identifies a container (and its stack) that references an image.

type NetworkInfo

type NetworkInfo struct {
	ID         string            `json:"id"`
	Name       string            `json:"name"`
	Driver     string            `json:"driver"`
	Scope      string            `json:"scope"`
	Internal   bool              `json:"internal"`
	Attachable bool              `json:"attachable"`
	IPv6       bool              `json:"ipv6"`
	Subnet     string            `json:"subnet"`  // first IPAM pool
	Gateway    string            `json:"gateway"` // first IPAM gateway
	Options    map[string]string `json:"options"`
	Labels     map[string]string `json:"labels,omitempty"`
	Created    int64             `json:"created"` // unix seconds
	UsedBy     []ResourceUser    `json:"used_by"`
}

NetworkInfo is a Docker network plus the containers attached to it.

type OriginRef

type OriginRef struct {
	ContainerID string
	Name        string // container name
	Project     string
	Service     string
	Networks    []string // user-defined networks this container is on
	Aliases     []string // per-network aliases (union)
}

OriginRef identifies a tunnel origin (a container) so ingress URLs can be resolved back to a stack/service in the UI.

type PluginContainer

type PluginContainer struct {
	ContainerID string   `json:"container_id"`
	Name        string   `json:"name"` // container name (no leading slash)
	Port        int      `json:"port"`
	Path        string   `json:"path"`
	Title       string   `json:"title"` // pre-manifest hint, else name
	Icon        string   `json:"icon"`  // pre-manifest hint
	Project     string   `json:"project"`
	Service     string   `json:"service"` // compose service — part of the stable identity
	Networks    []string `json:"networks"`
	Image       string   `json:"image"`
	ImageID     string   `json:"image_id"` // image digest — part of the trust fingerprint
	Running     bool     `json:"running"`
}

PluginContainer is a container that declares a hope plugin endpoint. Identity (name/version/icons/capabilities) comes from the plugin's own getSchema; these fields are only what the labels + docker tell us — where to dial and enough to list it before it's trusted.

type PruneResult

type PruneResult struct {
	Deleted   int    `json:"deleted"`
	Reclaimed uint64 `json:"reclaimed"`
}

PruneResult reports the outcome of an image prune.

type RegistryEntry

type RegistryEntry struct {
	Server      string
	Username    string
	HasPassword bool
	Source      RegistrySource
}

RegistryEntry is the credential-free view of a known registry for the UI.

type RegistrySource

type RegistrySource string

RegistrySource records where a credential came from, so the UI can show config-loaded registries as read-only and only let the operator edit/remove the ones they added at runtime.

const (
	// RegistrySourceConfig: from config.json or [[registry]] — read-only in the UI.
	RegistrySourceConfig RegistrySource = "config"
	// RegistrySourceDB: added at runtime (persisted in the state db) — editable.
	RegistrySourceDB RegistrySource = "db"
)

type ResourceUser

type ResourceUser struct {
	ID      string `json:"id"`
	Name    string `json:"name"`
	Service string `json:"service"`
	Project string `json:"project"`
}

ResourceUser is a container that references a network or volume, with its compose identity so the UI can group/link it.

type ServerInfo

type ServerInfo struct {
	Version    string `json:"version"`
	Containers int    `json:"containers"`
	Running    int    `json:"running"`
	Images     int    `json:"images"`
}

ServerInfo is a small typed slice of the daemon info (for the agents view).

type StackSummary

type StackSummary struct {
	Project     string             `json:"project"`
	WorkingDir  string             `json:"working_dir"`
	ConfigFiles []string           `json:"config_files"`
	Containers  []ContainerSummary `json:"containers"`
	Running     int                `json:"running"`
	Total       int                `json:"total"`
	// Restarting flags a stack with any container in a restart loop — the
	// dashboard surfaces these in red.
	Restarting bool `json:"restarting"`
	// ComposeAvailable is true when hope can read this stack's compose file
	// (file-based features like the compose viewer). False over a remote daemon
	// or when the project dir is not mounted — API ops still work regardless.
	ComposeAvailable bool `json:"compose_available"`
}

StackSummary groups a compose project's containers with the on-disk metadata needed to drive its lifecycle.

type TopResult

type TopResult struct {
	Titles    []string   `json:"titles"`
	Processes [][]string `json:"processes"`
}

TopResult is a container's live process list (the `docker top` equivalent): the ps column titles and one row of cells per process.

type UpdateCacheStore

type UpdateCacheStore interface {
	Get(key string) []byte
	Put(key string, value []byte) error
}

UpdateCacheStore is an optional key/value backend for the freshness cache (the embedded state db). When set it supersedes the JSON file path, so the cache lives in hope.db instead of a separate mounted file.

type VolumeInfo

type VolumeInfo struct {
	Name       string            `json:"name"`
	Driver     string            `json:"driver"`
	Mountpoint string            `json:"mountpoint"`
	CreatedAt  string            `json:"created_at"`
	Scope      string            `json:"scope,omitempty"`
	Size       int64             `json:"size"` // bytes; -1 when the daemon didn't compute it
	Options    map[string]string `json:"options,omitempty"`
	Labels     map[string]string `json:"labels,omitempty"`
	UsedBy     []ResourceUser    `json:"used_by"`
}

VolumeInfo is a Docker volume plus the containers mounting it.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL