Skip to content

Config correctness: rename references, atomic writes, no silent settings, daemon port - #94

Merged
sequoiap merged 2 commits into
masterfrom
fix/config-correctness
Oct 5, 2026
Merged

sequoiap merged 2 commits into
masterfrom
fix/config-correctness

Conversation

@sequoiap

@sequoiap sequoiap commented Oct 4, 2026 •

Copy link
Copy Markdown
Collaborator

Finishes hardening configuration handling. Closes #47, #67, #69, #72. Targets master and depends on nothing else.

pyrolab rename updates references (#67)

  • References follow the rename: renaming a nameserver or daemon now rewrites every reference to it: each service's daemon and nameservers, each daemon's nameservers, and the autolaunch lists. That includes services that use the implicit default daemon. The output lists what changed, for example Updated references in: service 'sample.echo', daemon 'lockable', autolaunch.. Since Make daemon startup reliable: validated config, atomic lockfile, waiting up #92, the command refused these renames instead.
  • Entry order is kept, so the saved file changes as little as possible.
  • Existing names are refused unless --force is given. With --force, the existing entity is replaced and duplicate references collapse into one. Previously the existing entry was silently destroyed.
  • Correct error messages: Daemon 'x' not found. and Service 'x' not found. replace the copy-pasted "Nameserver not found". Renaming to the same name does nothing.
  • Persistent storage note: for a nameserver using sql or dbm storage, the command notes that its database file name changes with its name, so the file needs moving to keep existing registrations.
  • Accurate follow-up hint: for a renamed nameserver or daemon in autolaunch, plain pyrolab reload is enough (see below). For one that isn't, the message gives the exact pyrolab start <kind> <new> command, since reload can't tell that the renamed entity is the same one.
  • Reusable logic: the work is done by the new configure.rename_entity(), which returns the validated, renamed config plus the list of changes.

Atomic config writes (#69)

  • One shared helper: update_config, export_config, GlobalConfiguration.save_config, pyrolab config export and the lockfile all use the new utils.atomic_write_text(). It writes a temporary file in the same directory, fsyncs it, then swaps it in with os.replace. If anything fails, the previous file is left intact and no temporary file remains.
  • Typo fixed: "File does not:" now reads "File does not exist:".

No silently ignored or injected settings (#72)

  • Environment variables were leaking into the config (found while checking this issue): the config models were pydantic BaseSettings, which fill any field the YAML leaves out from an environment variable with the same name, ignoring case. With PORT=8080 in the environment, every daemon would bind port 8080; HOST and SERVERTYPE would silently change those settings too. The models are now plain BaseModels, so the config file is the only source. They keep the same strictness: unknown keys are rejected and defaults are validated. Nothing in PyroLab used environment variables this way on purpose.
  • Unknown keys were already rejected: the issue assumed misspelled keys were silently ignored, but BaseSettings already rejected them. They're still rejected, and regression tests now cover several misspellings.
  • update_pyro_config reports what it does: it logs the settings it applies at debug level, and logs a warning for any it can't apply. The exception is fields a model lists in NOT_PYRO_OPTIONS because they're used elsewhere, such as storage, broadcast, module and port.
  • ServiceConfiguration no longer inherits the Pyro-settings mixin. Nothing called it, and services have no Pyro settings.

Daemon port and unixsocket take effect (#47)

  • Passed to the constructor: DaemonRunner.setup_daemon now passes host (with public resolved), port, unixsocket, nathost and natport straight to the daemon. Before, they were only applied through Pyro5.config, which has no PORT or UNIXSOCKET setting, so a configured port was ignored.
  • NAT settings: natport is passed only together with nathost, because Pyro requires both or neither.

reload starts autolaunch entries

reload used to restart only what was already running, under names still in the config. Now it also starts every autolaunch entry, nameservers first, and counts them toward its success result. After a reload you get what a fresh pyrolab up would start, plus anything you'd started by hand. This also means an autolaunched entity you'd stopped deliberately comes back on reload.

This came out of the rename work. Renaming an autolaunched nameserver used to leave it stopped after reload, which an end-to-end run showed. Now rename followed by reload brings it back under its new name, and the daemon re-registers with it right away.

Also fixed

PyroLabConfiguration now names its nameservers whenever it's constructed. Before, only GlobalConfiguration.load_config() did, so a config loaded with from_file() computed storage paths like ns_.sql. The rename storage note exposed this.

Checks run

  • Suite: 382 passed, 9 skipped. pre-commit passes on every file.
  • The tests catch the original bugs: against master, the new rename, atomic-write, placement, environment-variable, logging and nameserver-name tests all fail.
  • End-to-end run with real processes, from a scratch copy with isolated runtime files:
    • with PORT=12345 set in the environment, a daemon configured with its own port listened on that port;
    • rename nameserver testns labns rewrote all three references;
    • after reload and then start nameserver labns, the daemon registered with labns on its next retry;
    • down left no processes behind.

🤖 Generated with Claude Code

sequoiap and others added 2 commits October 4, 2026 16:42
- `pyrolab rename` (#67) renames an entity together with every
  reference to it (service daemon/nameservers, daemon nameservers,
  autolaunch, including the implicit "default" daemon), keeps entry
  order, refuses an existing name unless --force, reports what changed,
  says "Daemon/Service ... not found" correctly, and notes when a
  persistent nameserver's storage file will change. New
  configure.rename_entity() does the work.
- Atomic writes (#69): update_config, export_config, save_config,
  `config export`, and the lockfile all go through the new
  utils.atomic_write_text (temp file, fsync, os.replace), so an
  interrupted write leaves the previous file intact. Fixes the
  "File does not:" message.
- No silent settings (#72): the config models are plain pydantic models
  instead of BaseSettings, which filled unset fields from same-named
  environment variables (PORT=8080 moved every daemon). Unknown keys
  stay errors (now covered by tests). update_pyro_config logs what it
  applies and warns about anything it can't, unless the model declares
  the field as used elsewhere (NOT_PYRO_OPTIONS).
- Daemon placement (#47): host, port, unixsocket, nathost, and natport
  are passed to the daemon's constructor, so `port` and `unixsocket`
  finally take effect.
- PyroLabConfiguration names its nameservers on construction, so a
  config loaded with from_file() no longer computes storage paths like
  "ns_.sql".

Closes #47
Closes #67
Closes #69
Closes #72

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
reload restarted only what was running under names still in the
configuration, so an autolaunched nameserver or daemon that had been
renamed (or added to autolaunch) stayed stopped until started by hand.
reload now also starts every autolaunch entry, nameservers first, and
counts them in its result, so it matches a fresh `pyrolab up` plus
anything started by hand. The rename hint now says plain `reload` is
enough for autolaunched entities.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@sequoiap
sequoiap merged commit e25472d into master Oct 5, 2026
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Daemon port and unixsocket configuration is silently ignored

1 participant