Architecture¶
How Nops works inside. The states of a deployment are in deployment lifecycle; the rules every part keeps are in philosophy.
Components¶
| Package | Role |
|---|---|
cmd/nops |
Runs the server (nops serve): wires everything together and runs the loops and the dashboard. Any other command goes to internal/cli. |
internal/config |
Flags, environment variables, and the configuration file. |
internal/gitwatch |
Keeps each repository in memory and polls it. |
internal/nomadx |
Nomad client: parse, plan, CAS register, dispatch. |
internal/meta |
Reads and validates the nops_* keys. |
internal/acl |
Parses ACL policies and binding rules and decides what a token may do. |
internal/store |
SQLite, the only place state lives. |
internal/engine |
Detection and apply: the state machine. |
internal/hooks |
Runs a hook job and waits for its outcome. |
internal/cli |
The command line: a client of the API, and a few offline utilities. |
internal/web |
The dashboard, the login, and the git webhook. |
internal/metrics |
/metrics. Each scrape reads the store and the engine. |
internal/notify |
Notification adapters. |
internal/redact |
Removes secrets from the plan diff. |
internal/secret |
Generates random secrets. |
internal/version |
The build's version. |
Loops¶
Nops moves deployments forward on its own:
- Detection runs at startup, on every new commit, every
-drift-interval, and after anything that can change a job's state (a deployment ending, a reject, a retry, a pause). - Apply runs every
-engine-intervaland advances active deployments. - Recovery is the first apply cycle after a start: it resumes whatever a restart interrupted (deployment lifecycle).
Reading the repositories¶
gitwatch keeps a shallow, in-memory clone of each repository's branch and
fetches it again when its head moves. Nops reads every *.nomad.hcl or
*.nomad file under the repository's path, with its optional
<name>.vars.hcl. Nomad parses each file. Its meta decides what it is:
nops_role = "hook" makes a hook, nops_managed = "true" a managed job;
Nops ignores anything else. Detection reads the files of every repository as
one set, so a hook serves the jobs of its namespace in any repository.
If a fetch fails, Nops keeps working on that repository's last good snapshot and shows the error on the Overview.
Detection¶
Each cycle, for every managed job, Nops:
- plans the job against the cluster;
- freezes the hooks it declares (hook revisions);
- revalidates its waiting deployment: Nops supersedes it when git or the live job changed (revalidation);
- creates a deployment if there is drift and the policy, a block, or a hold don't stop it.
A job's drift, hooks and state live in memory and are rebuilt every cycle; only deployments go to SQLite. A deployment stores the full spec to register, so apply registers exactly what was planned and approved. The dashboard never shows that spec, only the redacted diff.
Rules detection keeps¶
- A job is identified by namespace and ID. Nops ignores jobs outside the managed namespaces.
- A new commit supersedes a waiting deployment only if it changes the job's spec or hooks.
- A task group with a
scalingblock keeps its live count, so Nops doesn't fight the autoscaler. - Two files that declare the same job, in one repository or in two, are both ignored with an ERROR.
- A missing or invalid hook fails the deployment at detection.
- While any file doesn't parse, Nops doesn't treat missing jobs as removed: an unparsed file looks the same as a deleted one.
Orphan jobs¶
Nops never stops a job it deployed. If you remove a deployed job from git and it still runs in Nomad, the dashboard lists it as an orphan until you stop it or put the file back.
Apply¶
Each step saves its state before it calls Nomad, so recovery is the same step
run again. Only Approve and Reject move a deployment out of
pending_approval.
- Pre-hooks run in order (hooks).
- Register: Nops plans again, and registers only if the plan still shows the difference it showed at detection. If someone changed the job, the deployment fails. A count Nops leaves to an autoscaler isn't a change.
- Wait for health, within the apply timeout.
- Post-hooks run in order, then the deployment completes.
Nomad carries out the update according to the job's update block, so
rolling updates, canaries and auto_revert work as usual. A deployment that
doesn't become healthy fails; Nops doesn't roll back.
Healthy¶
- With a Nomad deployment for this register: healthy when it's
successful. - Without one, as for batch jobs: healthy when every allocation of the new
version is
runningorcomplete. - A job with no allocations, like a periodic or parameterized job: healthy once registered.
The apply timeout¶
-apply-timeout counts from the start of the apply and covers the register
and the wait for health. It pauses while canaries wait for someone to promote
them, and restarts in full after the promotion. Promote from the deployment's
page or in Nomad.
Hook revisions¶
A deployment runs its hooks exactly as they were when it was created, so an
approval covers them. At detection, Nops hashes each hook's spec and includes
it in the deployment's spec hash. Before running a hook, Nops registers it as
its own job, <hook-id>-<first 8 hex digits of the hash>. After each cycle,
it stops the revisions no active deployment needs.
Each hook run is dispatched with an idempotency token, so a dispatch repeated after a crash returns the same run instead of starting a second one.
What Nops does not do¶
- Write to git, or write meta into a live job.
- Roll back, or stop the jobs it deploys.
- Pick nodes for hooks: the scheduler does (hooks).