Documentation
¶
Overview ¶
Package supervisor runs the child processes and keeps them running.
keepalived is three processes: a parent that supervises, and children for VRRP and for the health checkers (plus BFD when configured). The split is not cosmetic — it is what lets a crash in the health-check code, which parses responses from arbitrary backends, avoid taking VRRP down with it.
The parent's whole job is: start the children, restart them when they die, forward reload and dump signals, and shut them down in a bounded time. This package is that logic with the process mechanics behind an interface, so the restart back-off and the shutdown deadline can be tested without spawning anything.
Index ¶
Constants ¶
const ( // BackoffReset is how long a child must run before its back-off is // forgotten. C tests `> 60` seconds, or exactly 60 with the // microseconds no earlier (scheduler.c:496-498). BackoffReset = 60 * time.Second // BackoffMax is the ceiling. BackoffMax = 60 * time.Second )
Back-off for restarting a child that died (calc_restart_delay, lib/scheduler.c:490-527).
The sequence is 0, 1, 2, 4, 8, 16, 32, 60, 60, … seconds. The first restart is immediate, which is the right default: a child that died once is far more likely to have hit something transient than to be in a crash loop, and delaying VRRP's restart by even a second risks a failover.
The doubling exists for the other case. A child that crashes on startup would otherwise be respawned as fast as fork returns, and the log would fill faster than anyone could read why.
const ( // ExitOK is a clean exit. ExitOK = 0 // ExitFatal is a general failure (EXIT_FAILURE). ExitFatal = 1 // ExitConfigError is an unusable configuration (EXIT_INVALIDARGUMENT). // A child returning this tells the parent not to restart it: the same // configuration would fail the same way. ExitConfigError = 2 // ExitMissingPermission is EXIT_NOPERMISSION — the daemon lacks a // capability it needs, typically CAP_NET_RAW or CAP_NET_ADMIN. ExitMissingPermission = 4 // ExitNoConfig is EXIT_NOTCONFIGURED: the file parsed but asked for // nothing to be done. ExitNoConfig = 6 // ExitProgramError is EX_SOFTWARE, an internal inconsistency. ExitProgramError = 70 // ExitNoMemory is EXIT_MEMORY. ExitNoMemory = 204 )
Process exit codes (enum exit_code and enum chk_exit_code, lib/scheduler.h:205-220).
These are a contract, not an implementation detail. systemd reads them — they are the values systemd.exec(5) documents — and every deployment script that runs `keepalived -t` branches on them. A port that returned 1 for everything would leave those scripts unable to tell a syntax error from a permission problem, which is precisely the distinction they exist to make.
const ( // TestExitOK means the configuration is usable. TestExitOK = 0 // TestExitConfig means the file was missing, named an unusable // interface, or hit a fatal error. TestExitConfig = 4 // TestExitConfigTest means the configuration has errors that are not // fatal — the daemon would start, with less than was asked for. TestExitConfigTest = 5 // TestExitConfigSecurity means a security check failed: a script or key // file with permissions that make it untrustworthy. It is separate // because it is the one an automated deployment must never ignore. TestExitConfigSecurity = 6 )
Configuration-test exit codes (enum chk_exit_code, lib/scheduler.h:215-219).
They are a *separate* numbering from the run-time codes and deliberately do not overlap, so a script can tell "the configuration is bad" from "the daemon failed to start". C's comment on the first value — "Maintain backward compatibility" — is why 4 rather than something adjacent to the others.
const ChildWait = 5 * time.Second
ChildWait is how long the parent waits for children to exit before killing them (CHILD_WAIT_SECS, keepalived/core/main.c:124).
Variables ¶
var ErrChildFailed = errors.New("supervisor: child process failed")
ErrChildFailed is returned by Run when a child exited fatally, or when respawning is disabled and one died.
Functions ¶
This section is empty.
Types ¶
type Backoff ¶
type Backoff struct {
// contains filtered or unexported fields
}
Backoff tracks one child's restart delay.
The value returned is the one *before* the update, which is why the first restart is immediate: C captures `restart_delay = *next_restart_delay` and then advances the state (scheduler.c:492, :526). Reading it the other way round gives a one-second delay on the first crash and shifts the whole sequence, which is the sort of off-by-one that only shows up as a slightly slower failover under load.
func (*Backoff) Next ¶
Next returns how long to wait before restarting a child that started at startedAt and died at now, and advances the back-off.
type Child ¶
type Child interface {
// Name identifies the child in logs.
Name() string
// Start launches it. The returned channel receives exactly once, when
// the child ends.
Start(ctx context.Context) (<-chan Exit, error)
// Signal forwards a signal to a running child.
Signal(sig Signal) error
// Kill terminates it immediately, for the case where the terminate
// deadline passed.
Kill() error
}
Child is one supervised process.
It is an interface so the supervisor's policy — back-off, shutdown ordering, the terminate deadline — is testable without forking. ExecChild is the production implementation.
type Clock ¶
Clock is the supervisor's view of time, so the back-off and the shutdown deadline can be tested without waiting for them.
type Config ¶
type Config struct {
Children []Child
Clock Clock
Logger Logger
// NoRespawn is --dont-respawn: a child that dies takes the parent down
// with it. It exists for debugging, where an automatic restart hides
// the crash being investigated.
NoRespawn bool
// ChildWait overrides the shutdown deadline.
ChildWait time.Duration
}
Config is the supervisor's configuration.
type ExecChild ¶
type ExecChild struct {
// ChildName identifies it in logs and in the supervisor's map.
ChildName string
// Path and Args are the program to run.
Path string
Args []string
// Env is the child's environment. Nil inherits the parent's.
Env []string
// contains filtered or unexported fields
}
ExecChild is a Child backed by a real process.
It is the production implementation; everything the supervisor's policy does is tested against fakes instead, because a back-off sequence and a shutdown deadline are not things to verify by forking.
func (*ExecChild) Start ¶
Start launches the process.
The child gets its own process group so that a signal aimed at it reaches anything it spawns — a VRRP child runs notify scripts, and those must not outlive it. It does *not* get Pdeathsig: the parent's whole job is to supervise, and a child that died because the parent restarted would defeat that. The parent kills them explicitly instead, with a deadline.
type Exit ¶
type Exit struct {
// Name identifies which child.
Name string
// Code is the exit status, valid unless Signal is non-zero.
Code int
// Signal is the signal that killed it, or zero.
Signal int
// Err is set when the child could not be waited for at all.
Err error
}
Exit describes how a child ended.
func (Exit) Fatal ¶
Fatal reports whether this exit means the whole daemon should stop rather than the child be restarted.
C's rule (report_child_status): a child that exits with a status the parent recognises as a configuration failure takes the parent down with it, because restarting it would only reach the same configuration again. A crash — a signal, or an unrecognised status — is respawned.
type Signal ¶
type Signal int
Signal is the subset of signals the parent forwards.
const ( // SigTerminate asks a child to shut down cleanly. For VRRP that means // sending the priority-0 resignation advert, which is the difference // between a 609 ms and a 2923 ms failover. SigTerminate Signal = iota // SigReload asks a child to re-read its configuration. SigReload // SigDumpData and SigDumpStats ask for a diagnostic dump. SigDumpData // SigDumpStats asks for a statistics dump. SigDumpStats )
type Supervisor ¶
type Supervisor struct {
// contains filtered or unexported fields
}
Supervisor runs the children.
func (*Supervisor) Run ¶
func (s *Supervisor) Run(ctx context.Context) error
Run starts the children and supervises them until ctx is cancelled or a child fails fatally.
On return every child has been asked to terminate and either exited or been killed, so a caller that returns from Run can exit the process without leaving a VRRP instance holding addresses.
func (*Supervisor) Signal ¶
func (s *Supervisor) Signal(sig Signal)
Signal forwards a signal to every running child.
Reload is the one that matters: C's parent re-reads nothing itself, it just tells the children to (process_reload_signal, main.c:945). A child that cannot reload keeps running with its old configuration rather than dying, which is the right failure mode — a bad configuration file must not take down a working router.
type SystemClock ¶
type SystemClock struct{}
SystemClock is the real clock.
func (SystemClock) Now ¶
func (SystemClock) Now() time.Time