Documentation
¶
Overview ¶
Package netmove holds the host-side logic for moving a standalone StackKits server to another network: observing the current address and wireless link, deciding whether the recorded site address is stale, pre-staging destination WiFi, and keeping a watcher installed that re-binds the stack by itself.
Everything that touches the host goes through Sys so the decisions stay unit-testable with fakes.
Index ¶
- Constants
- Variables
- func AcquireLock(workspace string, now time.Time) (release func(), ok bool, err error)
- func NMKeyfile(credential Credential) []byte
- func NetplanAdd(existing []byte, credential Credential) ([]byte, error)
- func NetplanList(existing []byte) ([]string, error)
- func NetplanRemove(existing []byte, ssid string) (out []byte, found bool, err error)
- func ParseDefaultRoute(raw []byte) (iface, gateway string)
- func ParseIWLinkSSID(out []byte) string
- func ParseNMCLIActiveSSID(out []byte) string
- func RenderWatchService(cfg WatchConfig) string
- func RenderWatchTimer() string
- func SaveState(workspace string, state State) error
- func SetWatchOptOut(workspace string, optedOut bool) error
- func Verdict(current string, recorded ...string) string
- func WatchOptedOut(workspace string) bool
- func WirelessInterfaces(sys Sys) []string
- type Backend
- type Credential
- type NMBackend
- type NetplanBackend
- type Network
- type Observation
- type State
- type Sys
- type WatchConfig
- type WatchInstaller
- type WatchStatus
Constants ¶
const ( // VerdictInPlace means every recorded address equals the current one. VerdictInPlace = "in-place" // VerdictMoved means a recorded address differs from the current one. VerdictMoved = "moved" // VerdictUnknown means there is no current or no recorded address to compare. VerdictUnknown = "unknown" )
const ( // WatchService and WatchTimer are the systemd units of the network watcher. WatchService = "kombify-stackkit-network-watch.service" WatchTimer = "kombify-stackkit-network-watch.timer" // RetryBackoff is how long a failed automatic rebind waits before the // next attempt, so a broken apply does not loop every minute. RetryBackoff = 5 * time.Minute )
const ( // NetplanFile is the only netplan file this package writes. NetplanFile = "/etc/netplan/90-kombify-wifi.yaml" // NMConnectionDir holds NetworkManager keyfile connections. NMConnectionDir = "/etc/NetworkManager/system-connections" )
Variables ¶
var ErrNoBackend = errors.New("netmove: neither a running NetworkManager nor netplan was found; install one of them or configure the WiFi through the distribution's own tooling")
ErrNoBackend reports that neither NetworkManager nor netplan can stage WiFi.
var ErrNoWirelessInterface = errors.New("netmove: this host has no wireless interface; use --force to stage the network anyway")
ErrNoWirelessInterface reports that WiFi cannot be staged on this host.
Functions ¶
func AcquireLock ¶
AcquireLock takes the watcher lock. ok is false when another run holds it. A lock older than staleLockAge belongs to a killed process and is replaced.
func NMKeyfile ¶
func NMKeyfile(credential Credential) []byte
NMKeyfile renders the NetworkManager connection for a staged network. DHCP is requested for both families and the connection autoconnects on any wireless device, so it only ever takes effect where its SSID is in range.
func NetplanAdd ¶
func NetplanAdd(existing []byte, credential Credential) ([]byte, error)
NetplanAdd merges one access point into an existing netplan document (nil for a new file) and keeps every other key, including ethernets.
func NetplanList ¶
NetplanList returns the staged SSIDs of a netplan document.
func NetplanRemove ¶
NetplanRemove drops one access point. out is nil when nothing but the version header remains and the file can be deleted.
func ParseDefaultRoute ¶
ParseDefaultRoute returns the interface and gateway of the lowest-metric default IPv4 route in the /proc/net/route format.
func ParseIWLinkSSID ¶
ParseIWLinkSSID returns the SSID line of `iw dev <if> link`.
func ParseNMCLIActiveSSID ¶
ParseNMCLIActiveSSID returns the SSID of the active row of `nmcli -t -f active,ssid dev wifi` (terse mode escapes ':' as '\:').
func RenderWatchService ¶
func RenderWatchService(cfg WatchConfig) string
RenderWatchService renders the one-shot service the timer starts.
func RenderWatchTimer ¶
func RenderWatchTimer() string
RenderWatchTimer renders the timer: shortly after boot, then every minute.
func SetWatchOptOut ¶
SetWatchOptOut records or clears the owner's opt-out.
func Verdict ¶
Verdict compares the current address with every recorded one. Empty recorded values are ignored. It is "moved" as soon as one recorded address differs, because every recorded copy must be re-issued.
func WatchOptedOut ¶
WatchOptedOut reports whether the owner disabled the watcher for this stack, so a later Apply does not enable it again.
func WirelessInterfaces ¶
WirelessInterfaces lists the host's wireless interface names.
Types ¶
type Backend ¶
type Backend interface {
Name() string
Add(ctx context.Context, credential Credential, applyNow bool) error
List() ([]Network, error)
Remove(ctx context.Context, ssid string, applyNow bool) (bool, error)
}
Backend stages WiFi networks through one host network manager.
type Credential ¶
Credential is a network to stage.
func (Credential) Validate ¶
func (c Credential) Validate() error
Validate checks the SSID and WPA passphrase shapes.
type NMBackend ¶
NMBackend writes keyfile connections and asks NetworkManager to reload them. A keyfile keeps the passphrase out of any process argument list.
type NetplanBackend ¶
NetplanBackend owns one netplan file with a single wireless device that matches every wl* interface. It never writes an ethernet definition, and it sets the networkd renderer on that device only, so the global renderer and the host's existing wired DHCP stay as they are.
func (*NetplanBackend) Add ¶
func (b *NetplanBackend) Add(ctx context.Context, credential Credential, applyNow bool) error
func (*NetplanBackend) List ¶
func (b *NetplanBackend) List() ([]Network, error)
func (*NetplanBackend) Name ¶
func (b *NetplanBackend) Name() string
type Network ¶
type Network struct {
SSID string `json:"ssid"`
Priority int `json:"priority,omitempty"`
Backend string `json:"backend"`
}
Network is one staged WiFi network. The password is never part of it.
type Observation ¶
type Observation struct {
Address string `json:"address,omitempty"`
Interface string `json:"interface,omitempty"`
Gateway string `json:"gateway,omitempty"`
Wireless bool `json:"wireless"`
SSID string `json:"ssid,omitempty"`
}
Observation is what the host reports about its current network position.
type State ¶
type State struct {
LastAttempt time.Time `json:"lastAttempt"`
LastFailed bool `json:"lastFailed"`
LastError string `json:"lastError,omitempty"`
LastAddress string `json:"lastAddress,omitempty"`
// ApplyPending is set once the custody was re-issued and cleared when the
// regenerate-and-apply that follows succeeds, so a failed apply is retried
// even though the recorded addresses already match.
ApplyPending bool `json:"applyPending,omitempty"`
}
State is the watcher's persisted attempt record under the stack's .stackkit.
type Sys ¶
type Sys struct {
Discover func() (netip.Addr, error)
ReadFile func(string) ([]byte, error)
Exists func(string) bool
Glob func(string) ([]string, error)
LookPath func(string) (string, error)
Run func(ctx context.Context, name string, args ...string) ([]byte, error)
}
Sys is the narrow host boundary used by this package.
type WatchConfig ¶
type WatchConfig struct {
Binary string // absolute path of the stackkit executable
Workspace string // absolute stack directory
Spec string // StackSpec path as the stack's commands take it
// Home and Path carry the environment the stack was applied with. Apply
// trust and the kit catalog live under the installing user's home, which
// is not root's when the installer ran as a sudo-capable user.
Home string
Path string
}
WatchConfig describes the stack the watcher re-binds.
type WatchInstaller ¶
WatchInstaller installs and removes the watcher units.
func DefaultWatchInstaller ¶
func DefaultWatchInstaller() WatchInstaller
DefaultWatchInstaller targets /etc/systemd/system on the running host.
func (WatchInstaller) Disable ¶
func (w WatchInstaller) Disable(ctx context.Context) (removed bool, err error)
Disable stops the timer and removes both units.
func (WatchInstaller) Enable ¶
func (w WatchInstaller) Enable(ctx context.Context, cfg WatchConfig) (changed bool, err error)
Enable writes both units and starts the timer. changed is false when the installed units already match and the timer is enabled.
func (WatchInstaller) Status ¶
func (w WatchInstaller) Status(ctx context.Context) WatchStatus
Status reports the installed, enabled and active state of the timer.
func (WatchInstaller) WatchSupported ¶
func (w WatchInstaller) WatchSupported() bool
WatchSupported reports whether systemd is the running init.