Documentation
¶
Overview ¶
Package state locates sind's per-user state on the host and holds the realm lock, which serializes the operations that change a realm.
sind keeps its state in $XDG_STATE_HOME/sind, or ~/.local/state/sind: the MCP stream's generated token (mcp-token) and, per realm, the lock file and the exported SSH configuration. The realm itself (its mesh, DNS records, known_hosts and clusters) lives in the Docker daemon, and so does the second part of the realm lock, which serializes the daemon's clients (LockRealm).
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func Dir ¶
Dir returns sind's state directory, $XDG_STATE_HOME/sind, falling back to ~/.local/state/sind. A relative XDG_STATE_HOME is ignored, as the XDG Base Directory specification requires; the state would otherwise move with the working directory. Adapted from GSI-HPC/clusterctl internal/config/paths.go.
func LockNetworkName ¶
func LockNetworkName(realm string) docker.NetworkName
LockNetworkName returns the name of realm's lock on the Docker daemon, <realm>-lock: a configuration-only network that exists while a sind command changes the realm.
func LockRealm ¶
func LockRealm(ctx context.Context, realm string, opts *LockOptions) (unlock func(), err error)
LockRealm takes the exclusive lock of realm and returns the function that releases it, which the caller must call when its operation ends, failed or not.
cluster.Create, cluster.Delete, cluster.DeleteAll, cluster.WorkerAdd, cluster.WorkerRemove, cluster.PowerOn, cluster.PowerReboot and cluster.PowerCycle change state that all clusters of a realm share: the mesh, its DNS records and known_hosts, and a cluster's Slurm node list. They take no lock themselves. A caller holds the realm lock from before mesh.Manager.EnsureMesh until the call returns, as every sind command that changes a realm does; concurrent calls in one realm otherwise lose each other's DNS records, host keys and node definitions, or remove each other's resources.
The lock has two parts. The first is a flock(2) on the file "lock" in the realm's state directory. It serializes the callers that share that directory, the goroutines of one process as much as sind commands, without a call to the daemon. With opts.Client, LockRealm then takes the realm's lock on that Docker daemon, the configuration-only network LockNetworkName(realm), which serializes all clients of the daemon: other users, CI jobs that share the host's Docker socket, processes with another XDG_STATE_HOME. Without opts.Client, such clients are not serialized against each other.
LockRealm tries each part without waiting first. When another process holds the file lock, it calls opts.OnWait and blocks until the lock is free. When another client holds the daemon lock, it calls opts.OnWait with the holder and tries again, at intervals that grow to two seconds; clients take a freed lock in no particular order. A holder that runs on this host, in this PID namespace and since this boot, but no longer exists, was killed before it could release the lock: LockRealm removes its lock and says so through opts.OnWarning. Whether a holder elsewhere still runs it cannot tell, so it leaves that lock alone, and after a minute names the command that removes it, through opts.OnWarning. The waits have no timeout; when ctx ends, LockRealm returns ctx.Err().
The returned function removes the daemon lock, without ctx, so that it works after Ctrl+C as well, and then releases the file lock.
Types ¶
type LockHolder ¶
type LockHolder struct {
// Command is the holder's command line, e.g. "sind create cluster dev".
Command string
// Host is the host name of the machine, or container, it runs on.
Host string
// PID is its process ID there, 0 when unknown.
PID int
// Since is when it took the lock, by the daemon's clock.
Since time.Time
// contains filtered or unexported fields
}
LockHolder is the process that holds a realm's lock on the Docker daemon, as the lock's labels describe it.
func (*LockHolder) String ¶
func (h *LockHolder) String() string
String describes the holder for a message: its command, process and host, and since when it holds the lock, in local time.
type LockOptions ¶
type LockOptions struct {
// Dir is the directory that holds the lock file; RealmDir(realm) when
// empty.
Dir string
// Client, when set, is the Docker daemon on which LockRealm also takes
// the realm's lock, after the file lock. Every caller that changes a
// realm sets it: the realm lives on the daemon, which clients with
// other state directories share.
Client *docker.Client
// Command is the command line that the daemon lock records for the
// wait notices of other clients; the process's arguments when empty.
Command string
// OnWait, when set, is called once before LockRealm waits for each of
// the two locks: with a nil holder for the file lock, whose holder it
// does not know, and with the holder of the daemon lock. LockRealm logs
// at info level instead when it is nil.
OnWait func(holder *LockHolder)
// OnWarning, when set, receives the warnings meant for the user: a
// daemon lock whose holder no longer runs, which LockRealm removes; a
// holder that LockRealm cannot check and that keeps the lock for long,
// with the command that removes the lock; and a lock that the unlock
// function could not remove. LockRealm logs them at warn level instead
// when it is nil.
OnWarning func(msg string)
// contains filtered or unexported fields
}
LockOptions configures LockRealm.