-
Notifications
You must be signed in to change notification settings - Fork 0
1.6 Step | SetupPostgres
Installs PostgreSQL (when absent), enables it and starts it, reversibly. It lives in
src/steps/setup_postgres.rs. A port ofsetup_postgresfromlib/postgres.sh.This is the case where a single
PreStateis not enough: PostgreSQL has four orthogonal state axes, each to be restored separately (decision D4).
One PreState would describe “PostgreSQL is there or not”; but the service can be installed-but-stopped,
running-but-not-enabled, and so on. Four axes are needed:
struct PostgresSnapshot {
installed: PreState, // was the package already there?
enabled: PreState, // was the service already enabled?
active: PreState, // was the service already running?
cluster_initialized: PreState, // was the data directory already initialised?
}The fourth axis exists for the heaviest divergence between the families: on Debian/Ubuntu the package's
postinst creates and starts a cluster, so installing is enough; on Fedora postgresql-server
initialises nothing, and without postgresql-setup --initdb the service does not start.
Initialising is a mutation that produces an artifact — the data directory — so it gets a PreState of
its own, or it would be something coming into existence with nobody recording it. On families that do
not need it, it stays Untracked: nothing to create, nothing to remove.
| Phase | Behaviour |
|---|---|
| snapshot | for each axis: already true before us → Preexisting; otherwise Untracked
|
| run | installs/enables/starts only the Untracked axes, each → CreatedByUs. Final check: it must end up active, otherwise an error |
| undo | restores each axis to its snapshot state, in the order stop → disable → (purge) |
The undo touches an axis only if we changed it (CreatedByUs). A service that was already
running/enabled before us is left as it was.
| Axis | If CreatedByUs
|
If Preexisting
|
|---|---|---|
| active | systemctl stop |
leave it running |
| enabled | systemctl disable |
leave it enabled |
| installed | see below (gated purge) | leave it installed |
A firm decision: by default the undo does stop + disable (both reversible) but does not purge the
package, even when installed == CreatedByUs.
Purging PostgreSQL is too destructive for an automatic rollback on a customer machine: it could remove clusters holding data. Stop and disable can be undone; a purge cannot.
The purge happens only with --aggressive-rollback, and with an explicit warning.
Cluster caution (implemented). Even with --aggressive-rollback the purge is declined if the
cluster hosts databases beyond ours and the postgres maintenance one — and if that list cannot be
obtained, nothing is purged: fail-safe. The same question protects the removal of the data
directory, for the same reason: a PGDATA contains all the cluster's databases, not only ours.
The data directory's path has two possible sources, and they must not diverge:
- a constant in the code (
/var/lib/pgsql/dataon Fedora), which is what drives the snapshot and the undo — and it must stay a constant, because it feeds aremove_dir_all: a path arriving from a file is not trusted data; - the systemd unit, which is what
postgresql-setupconsults to know where to initialise, and which an administrator can move with a drop-in.
If they diverge, the damage is not theoretical: initdb would create the cluster where the unit says,
while the undo with --aggressive-rollback would remove the constant — that is, a directory we did not
create, and which on precisely such a machine is where a previous customer cluster may live.
So the step refuses when the two answers do not match, naming both, and stops before touching
anything. The path read from the unit drives no removal: it exists only to say “I do not know how to
work here”. If the unit says nothing — package not yet installed, systemctl not queryable — nothing is
concluded and we carry on: blindness is not divergence.
Exercised for real since 3.2.0, where before it lived only on mocks: a CI job installs PostgreSQL, moves
PGDATAwith a drop-in, and demands the refusal — asserting the message, not merely a non-zero exit, because a run can fail for a dozen reasons and look identical.Running it on a real machine also settled the branch nobody had checked: with the package not installed,
systemctl show -p Environment postgresql.serviceexits 0 with an empty value — it does not fail and does not say “no such unit”. That is what makes “blindness is not divergence” the common case rather than a rare one, and had systemd answered differently, every virgin machine would have been refused.
Services (service_is_enabled/is_active/enable/disable/start/stop) and the package manager sit
behind the SystemOps boundaries. In tests a stateful mock (start and
stop update an internal flag) makes it possible to verify that the post-start check works and that the
undo restores the axes, without a real PostgreSQL and without root.
- The final check (
service_is_active) turns a failed start into a typed error rather than a silent carry-on. - Best-effort undo: every failed action logs a
warnand carries on. - Tests: installed-but-stopped (start → stop, no disable, no purge), all-absent (install+enable+start →
stop+disable, no purge),
--aggressive-rollback(purges) vs default (does not), already-running (D4: does not stop).
Start here
Key concepts
References
For developers
Technical detail — how it works inside
Steps:
- 1.1 PrepareOptRoot
- 1.2 CreateOdooUser
- 1.3 SetupLogDir
- 1.3b SetupCacheDir
- 1.4 AptPackages (delta)
- 1.5 InstallWkhtmltopdf
- 1.6 SetupPostgres
- 1.7 CreateDbRole
- 1.8 CreateDatabase
- 1.9 CloneOdooRepo
- 1.10 CreateVirtualenv
- 1.11 InstallPythonRequirements
- 1.12 GenerateConfig
- 1.12b SetupDataDir
- 1.13 InitializeOdooDatabase
- 1.14 SetupSystemd
- 1.15 Nginx (6 sub-steps)
- 1.16 WriteControlScript + PatchBashrc
Cross-cutting: