Skip to content

Latest commit

 

History

History
99 lines (78 loc) · 5.52 KB

File metadata and controls

99 lines (78 loc) · 5.52 KB

Exit Code Matrix

This page maps Gula's internal failure modes to the process exit code of the gula binary. Supervisor commands mostly use 0/1; local operator clients such as status, start, stop, and restart also reserve specific codes for supervisor availability and protocol failures. The table below explains which conditions land in which bucket.

Top-level CLI exit codes

Exit code Meaning
0 All managed processes finished successfully, a graceful shutdown signal (SIGTERM/SIGINT/SIGHUP/SIGQUIT) stopped otherwise-healthy process groups inside the cleanup window, a read-only operator query rendered successfully, or a requested process-control operation succeeded/was accepted with --no-wait.
1 One or more managed processes failed (non-zero exit, timeout, readiness timeout, policy stop/kill, or restart budget exhausted), the supervisor itself failed before it could start (config load error, validation error, I/O error), the supervisor returned an error envelope, or a process-control operation reached failed.
2 Local operator client could not locate or connect to a running supervisor. Used by gula status and gula start / stop / restart.
3 Local operator client reached a supervisor with an incompatible control-plane API version.

For gula run, the 0/1 split is driven by the supervisor outcome:

  • Ok(RunOutcome::AllProcessesSucceeded)ExitCode::SUCCESS (0).
  • Ok(RunOutcome::OneOrMoreProcessesFailed)ExitCode::FAILURE (1). At least one process reported a failure outcome but Gula itself ran to completion.
  • Err(_) → propagated by main, also 1.

What triggers OneOrMoreProcessesFailed

RunOutcome::OneOrMoreProcessesFailed is emitted when supervision ran end-to-end but at least one process did not finish cleanly. Concretely, any of the following per-process outcomes promotes the whole run to OneOrMoreProcessesFailed:

Per-process outcome Internal reporting path
Spawn syscall failed (e.g. binary missing, EACCES, scheduling rejection) SpawnError { name, hint, source }
timeout_seconds exceeded Timeout(name, seconds)
readiness_probe.timeout_seconds exceeded ReadinessTimeout(name, seconds)
Process exited non-zero with no retry left ProcessStats.exit_code != 0 when no restart_policy is configured; RestartRequested(name) after the final restart attempt
memory_threshold_action: restart or iceoryx.on_dead: restart fired and exhausted the restart budget RestartRequested(name)
memory_threshold_action: kill/stop or iceoryx.on_dead: kill/stop fired PolicyViolation { process_name, policy_name, action }
Per-process I/O error (log-file creation, metrics CSV write, /proc read) Io(_)

A SKIPPED process (its dependency failed) also contributes to OneOrMoreProcessesFailed: the run is not considered "all green" if any process never got to start. An operator stop does not contribute to OneOrMoreProcessesFailed by itself; the process supervisor remains alive for a later start/restart until the whole supervisor is shut down.

What triggers Err(_)

Err(_) is reserved for failures that prevent Gula from supervising at all. These come from the supervisor Error enum used by top-level dispatch:

Failure Error variant
YAML failed to parse (unknown field, bad type) surfaces as Error::Other(anyhow) from serde_yaml
GulaConfig::validate() rejected the config (duplicate names, cycles, invalid affinity, out-of-range timer, ...) Error::Config { .. }
GulaSystemConfig::validate() rejected the system config Error::Config { .. }
Per-process error escalated to a hard error during setup (e.g. cannot create logs_dir) Error::Process(ProcessError::Io) or Error::Io
Generic I/O error during setup (signal handler install, /proc access, ...) Error::Io(_)
Anything else Error::Other(anyhow::Error)

Both Err(_) and RunOutcome::OneOrMoreProcessesFailed produce CLI exit code 1. The difference is operational:

  • Err(_) ⇒ Gula never supervised anything; check the message printed to stderr.
  • RunOutcome::OneOrMoreProcessesFailed ⇒ Gula supervised normally; per-process logs in logs_dir/ contain the per-process failure detail, and gula_metrics.csv will have any pre-failure samples.

Signal-driven shutdown is a success for shutdown-caused exits

Receiving SIGTERM, SIGINT, SIGHUP, or SIGQUIT is treated as a deliberate shutdown. Per-process exits caused by the forwarded signal are classified as a clean shutdown, not as a failure, so a normal Ctrl-C does not turn otherwise healthy services into exit code 1.

The shutdown signal does not erase failures Gula already observed before the signal arrived. If one process exits non-zero, times out, fails readiness, or hits a terminal policy action, and a later Ctrl-C or SIGTERM stops the remaining processes, the run still returns RunOutcome::OneOrMoreProcessesFailed and the CLI exits 1. Likewise, a group that had already entered leader-failure cleanup before the shutdown signal remains a failed process result.

Suggested CI pattern

gula validate --config gula.yaml --sys-config gula_config.yaml || exit 1

if ! gula run --config gula.yaml --sys-config gula_config.yaml; then
    echo "supervised stack failed; see logs/"
    exit 1
fi

For programmatic integration, call the gula binary and branch on its exit code. The 0.1.x line does not expose a supported Rust embedding API.