Documentation
¶
Overview ¶
Package runtime owns the local OVDB server's process lifecycle and the files in the runtime directory: server.json, the instance secret, the home lock and server.log. It starts the server detached, proves a running server is this home's instance (authenticated whoami), and stops it without ever signalling a process it cannot identify.
It knows nothing about HTTP routes beyond the two lifecycle endpoints (whoami and shutdown) and nothing about configuration files: callers pass the resolved port and the command that runs the server.
See spec/features/local-server-and-web-console (Lifecycle, Port) and decision 0007.
Index ¶
- Constants
- Variables
- func BaseURL(port int) string
- func CheckMatch(record *Record, dirs paths.Dirs, port int, explicitPort bool) *envelope.Error
- func CheckPort(ctx context.Context, port int, listen ListenFunc) *envelope.Error
- func IsUnconfirmedProcess(err error) bool
- func Listen(port int, listen ListenFunc) ([]net.Listener, *envelope.Error)
- func LogPath(runtimeDir string) string
- func NewHTTPClient(timeout time.Duration) *http.Client
- func PortInUse(port int) *envelope.Error
- func PortUnavailable(port int, cause error) *envelope.Error
- func PrepareDirs(dirs paths.Dirs, failure string) (warnings []string, err *envelope.Error)
- func ReadSecret(runtimeDir string) (string, error)
- func WithHomeLock(ctx context.Context, dirs paths.Dirs, failure string, fn func() error) ([]string, error)
- type Instance
- type ListenFunc
- type Record
- type StartOptions
- type StartResult
- type State
- type StopResult
- type Whoami
Constants ¶
const ( WhoamiPath = "/api/local/v1/whoami" ShutdownPath = "/api/local/v1/server/shutdown" )
The lifecycle endpoints of the local API.
const ( RecordFile = "server.json" SecretFile = "secret" LockFile = "home.lock" LogFile = "server.log" // StartLockFile serializes starts and locked writes (see lockStart). StartLockFile = "start.lock" )
File names inside the runtime directory.
const DefaultPort = 6832
DefaultPort spells OVDB on a phone keypad.
const DefaultTimeout = 10 * time.Second
DefaultTimeout bounds readiness after start and port release after stop.
Variables ¶
var ErrAlreadyRunning = errors.New("another OVDB server holds the home lock")
ErrAlreadyRunning reports that another server holds this home's lock.
Functions ¶
func BaseURL ¶
BaseURL is the address clients use: always 127.0.0.1, never a name that needs resolving (REQ:client-values-and-mismatch).
func CheckMatch ¶
CheckMatch fails with server_config_mismatch when a client whose home or explicitly chosen port differs from the running server would otherwise silently talk to — or start next to — the wrong server.
func CheckPort ¶
CheckPort reports whether a server could start on port, without keeping it. A port with anything answering on either loopback address is in use even where the OS would let a more specific bind succeed next to a wildcard listener (macOS with SO_REUSEADDR).
func IsUnconfirmedProcess ¶
IsUnconfirmedProcess reports whether err is Stop refusing to touch a pid it could not identify; for a restart that means "not running".
func Listen ¶
Listen binds 127.0.0.1:port and [::1]:port (REQ:loopback-bind). When ::1 cannot be bound because IPv6 is unavailable the server continues on IPv4; when either address is taken it is a conflict, never a silent IPv4-only server next to an impostor.
func NewHTTPClient ¶
NewHTTPClient returns a client for the local API that never follows a redirect: the local API does not redirect, and following one would resend the bearer secret to wherever an impostor points.
func PortInUse ¶
PortInUse is the port_in_use failure with both fixes, never an automatic port change (REQ:deterministic-port-conflict).
func PortUnavailable ¶
PortUnavailable is the port_unavailable failure for a bind the OS refuses with no listener.
func PrepareDirs ¶
PrepareDirs creates the OVDB home and runtime directory owner-only when they are missing. An existing directory is never changed: when it is accessible to other users, a warning with the fix is returned, and for the runtime directory — where the instance secret lives — the result is also a forbidden error, so no secret is ever written there.
failure is the message of the resulting error ("Couldn't start the OVDB server", "Couldn't change the setting").
See REQ:owner-only-state.
func ReadSecret ¶
ReadSecret returns the instance secret, or "" when there is none.
func WithHomeLock ¶
func WithHomeLock(ctx context.Context, dirs paths.Dirs, failure string, fn func() error) ([]string, error)
WithHomeLock runs fn as the home's single writer while no server runs. It returns ErrAlreadyRunning when a server holds home.lock; the caller then goes through that server instead. failure is the message of a directory error; warnings about open directories are returned in every case.
Types ¶
type Instance ¶
type Instance struct {
Dirs paths.Dirs
InstanceID string
Secret string
Version string
// contains filtered or unexported fields
}
Instance is the server side of the runtime directory: it holds home.lock for the server's lifetime and owns this run's instance id and secret.
func Acquire ¶
Acquire takes this home's lock and writes a fresh instance secret, replacing any stale server.json and secret a crashed server left behind (REQ:single-server-home-lock, REQ:stale-runtime-state). Warnings about directories accessible to other users are returned even on success.
type ListenFunc ¶
ListenFunc binds a listener; net.Listen satisfies it. Tests inject one to simulate an OS that refuses a bind (for example a Windows reserved range).
type Record ¶
type Record struct {
Schema int `json:"schema"`
InstanceID string `json:"instance_id"`
Home string `json:"home"`
PID int `json:"pid"`
// ProcessIdentity is daemonlifecycle.ProcessIdentity(PID) at start; stop
// signals PID only while it still matches.
ProcessIdentity string `json:"process_identity"`
Port int `json:"port"`
Version string `json:"version"`
StartedAt time.Time `json:"started_at"`
}
Record is server.json: written by a server once it is listening, it tells clients where the server is and which process it is.
func ReadRecord ¶
ReadRecord returns the runtime record, or nil when there is none.
type StartOptions ¶
type StartOptions struct {
Dirs paths.Dirs
Port int
ExplicitPort bool // --port or OVDB_PORT, as opposed to config or default
// Command returns the template of the process that serves port. Start
// adds the working directory (OVDB home) and the directory variables.
Command func(port int) *exec.Cmd
Timeout time.Duration // DefaultTimeout when zero
Listen ListenFunc // net.Listen when nil
}
StartOptions describe one start.
type StartResult ¶
type StartResult struct {
State State
AlreadyRunning bool
Warnings []string // directories other users can access
}
StartResult reports a successful start or an already running server.
func Start ¶
func Start(ctx context.Context, opts StartOptions) (StartResult, error)
Start starts the local server detached and waits for authenticated readiness (REQ:background-start). It succeeds without starting anything when this home's server already runs, fails with server_config_mismatch when a different home or port runs, and fails with port_in_use, port_unavailable, forbidden or server_start_failed otherwise. Warnings are returned on failure too.
type State ¶
type State struct {
Running bool
Record *Record // nil when there is no readable server.json
Secret string
Whoami *Whoami // the running server's answer
// Unreadable is set when server.json exists but cannot be read; it is
// treated as stale, and the next start replaces it.
Unreadable error
}
State is what the runtime directory says about the server, confirmed by an authenticated whoami.
type StopResult ¶
type StopResult struct {
WasRunning bool
Forced bool // the server did not answer and its verified process was terminated
Stale bool // runtime files named a server that is gone; they were cleared when possible
Record *Record
}
StopResult reports what Stop did.
func Stop ¶
Stop shuts the server down through the authenticated API and waits until its process is gone. If the server does not answer, the recorded pid is terminated only while its process identity still matches server.json. Otherwise no process is touched: when home.lock is free no server can be alive, so the stale runtime files are cleared and the result is "not running"; when something holds the lock, the failure says the process could not be confirmed (REQ:authenticated-stop, AC:stop-never-kills-reused-pid, REQ:stale-runtime-state).
type Whoami ¶
type Whoami struct {
Schema int `json:"schema"`
InstanceID string `json:"instance_id"`
Version string `json:"version"`
}
Whoami is the body of GET /api/local/v1/whoami.