Skip to content

1.1 Step | PrepareOptRoot

Omisen edited this page Aug 14, 2026 · 5 revisions

The installer's first real step: it creates /opt/odoo (the ctx.odoo_home value) when missing, reversibly. It lives in src/steps/prepare_opt_root.rs. It is the simplest possible mutation — a directory — chosen deliberately as the reference model for the steps that follow (user, packages, postgres…): all of them are richer variants of the same snapshot → run → undo shape.

It comes out of correction C4: the mkdir that Bash hid inside check_disk becomes a step with an undo here. See 1. Checks and 0. Engine.


Life cycle

Phase Behaviour
snapshot ctx.odoo_home exists → PreState::Preexisting; otherwise it stays Untracked (run has not created it yet)
run Preexisting → no-op (not ours: neither owner nor permissions); Untrackedmkdir + 0755, then PreState::CreatedByUs. If the user already exists, the directory is handed over at once (chown + 0750). In dry_run it only logs
undo acts only on CreatedByUs: removes the directory only if empty (rmdir, never rm -rf). Preexisting/Untracked → no-op

All three PreStates are handled and tested. The post-run PreState is serialised (snapshot_value) and persisted, so the rollback knows whether the directory is ours — including a rollback run days later from the manifest, which rebuilds this step through rehydrate.


Details that matter

Owned by root, for the moment

At this point the odoo user does not exist yet — it is created by the next step. So the directory is created owned by root with 0755 permissions, and the chown odoo:odoo happens in the user-creation step, which is the correct order. The dependency is documented in the code.

mkdir, not mkdir -p

Only the missing level is created (create_dir, not create_dir_all). The rollback must restore exactly what we added: if we also created /opt we would not remove it again, leaving a residue. The parent (/opt) is assumed to exist on the system.

undo is best-effort, never destructive

  • Idempotent: if the directory is already gone → no-op.
  • It removes only if empty. If it contains artifacts from later steps, those steps have their own undos which run first (reverse order), so by the time PrepareOptRoot's undo runs the directory should be empty. If it is not → a warning, and it is not forced. Never rm -rf.
  • A Preexisting directory is never removed: it is not ours to destroy.

Place in the flow

parse config
  └─▶ preflight checks (non-mutating)
       └─▶ Installer::execute([ PrepareOptRoot, … ])   ← first mutation, reversible

It is the first of the 25 steps, which means its undo is the last one to run in a rollback — the reason its directory should be empty by then.


Design notes

  • An exemplary, deliberately minimal implementation: if the pattern is clean here, the complex steps copy it.
  • No .unwrap()/.expect(): every failure is a Result.
  • Round-trip tests: CreatedByUs (creates → removes), Preexisting (survives the undo), non-empty directory (the undo does not force), dry_run (creates nothing, undo is a no-op).

If the odoo user already exists, the home is handed over here

owned root is not the right state for the home: it is a waiting state, and it makes sense only while the user does not exist. When the user is already there — a distro package, the leftover of a manual setup, a company convention — there is nothing to wait for, and leaving it to root broke the installation three steps later: CreateOdooUser sees a Preexisting user and returns immediately without the chown (a deliberate choice: we do not touch what is not ours), and SetupCacheDir then runs sudo -u odoo mkdir -p /opt/odoo/.cache on a root-owned directory → Permission denied, with an error that says nothing about the cause.

The handover belongs here and not there because here is where the information is: this step knows it created the directory itself. CreateOdooUser cannot know that — at its snapshot the home always exists, because we just created it — so any attempt to infer it from there would be a check that in production always answers the same way.

The opposite case — a pre-existing root-owned home with an already-existing user — stays out: that directory is not ours and does not get chowned. There the installation stops with an explicit precondition in CreateOdooUser.

Clone this wiki locally