Skip to content

1.12 Step | GenerateConfig

Omisen edited this page Aug 15, 2026 · 4 revisions

Generates odoo<N>.conf from the template, reversibly. It lives in src/steps/generate_config.rs. A port of generate_config from lib/config.sh. The template is embedded in the binary (include_str!): no dependency on external files at runtime.

Two novelties compared with the earlier steps: the first undo that restores instead of removing, and the care taken so that the master password is never world-readable.


The first “restoring” undo

Until now, the undo of a Preexisting step was a no-op (not ours, we do not touch it). Here it is different: run overwrites a pre-existing odoo.conf, so the undo has to restore the original.

Phase Behaviour
snapshot does the file already exist? → Preexisting/Untracked
run if Preexisting, back it up to odoo<N>.conf.bak.<timestamp> before overwriting (the path is saved in the snapshot); then render and write
undo CreatedByUsrm -f (we created it); Preexisting → restore the backup over the destination file

In the Preexisting case the undo does NOT leave the overwritten file and does NOT delete it: it puts exactly the customer's original back in place, from the backup. It is the first undo that restores rather than destroys.

A test proves it with a round trip on a real disk: after the undo, the file is byte-for-byte the original.


The master password is never world-readable

The file contains admin_passwd (and db_password). It must never pass through a file readable by other users, not for an instant. The pattern (faithful to Bash, confirmed in analysis):

1. render → write to a PRIVATE temp (mode 0600, owned by root)   ← password never readable by others
2. move   → move the temp to its destination (same filesystem, atomic)
3. chown odoo:odoo
4. chmod 0640                                                     ← readable only by odoo and root

The plaintext password enters only render_config (the Secret's single expose()) and from there the file's contents; it is never logged nor printed in the summary. The mock in the tests records the write as “private” without capturing its contents.


Rendering + normalisation

  • ${VAR} placeholders are substituted with the Context's values (version, port, gevent port, db_user/name, addons_path, data_dir, limits, proxy_mode, logfile, …). The data_dir is no longer a plain template value: the formula lives in generate_config::data_dir and the same function is used by 1.12b SetupDataDir to create that directory reversibly. Two identical format!s in two files would be the premise of a rollback cleaning the wrong directory.
  • gevent_port is derived, not hardwired. It used to be the literal 8072 in the template, which meant two instances wrote the same number into their own config and fought over one socket the moment both ran with more than one worker — at startup, long after the installation reported success. It is now --port + 3 by default, overridable, and persisted in the manifest, so the next installation's preflight can refuse a port that is already claimed.
  • data_dir follows the user's home, which is what keeps two instances' attachments apart: for a named instance the home is its install dir, so the filestore is its own without that derivation changing at all. The unnamed instance keeps /opt/odoo/.local/share/Odoo, where it has always been — moving it would make an existing installation start against an empty directory, with no error.
  • Empty directives → False: every key = left without a value becomes key = False (Odoo rejects empty values). So empty db_host, db_port, db_password and logfile end up as False rather than dangling.
  • Validation after rendering: the [options] section is present, addons_path and http_port have values, and no ${...} placeholder is left. Otherwise → error, and no incoherent file.

render_config, normalize_empty_directives and validate_rendered are pure functions, tested separately.


Design notes

  • The temp file is written in the same directory as the destination, so move is an atomic rename on one filesystem.
  • Best-effort undo: if the backup is missing, it logs a warning and carries on without destroying anything.
  • Final permissions 0640 odoo:odoo.
  • Tests: CreatedByUs (generates 640, undo removes), Preexisting (the undo restores the backup), rendering (empty→False, no leftovers), validation (leftover placeholder or missing section → error).

Clone this wiki locally