test(molecule): #75 review follow-ups, and a host-wide lock on the suite - #76
Merged
Merged
Conversation
host-env: - read the unit with a bare `systemd_service: name=` (a read-only `systemctl show`); the earlier claim that the module rejects a bare name was false, so `state: started` + check_mode are gone - document NRestarts correctly: a manual restart resets it, which is why "crash, then handler" passes and "crash, no handler" fails - assert Restart=always, which that split depends on: without it a crashed unit is started by the converge's `state: started` and a missing handler again shows a new PID with NRestarts 0 - split "daemon running" from "PID changed", so a dead daemon is not blamed on the restart handler - compare NRestarts as a string; `| int` turns garbage into 0 host-env and schema: tie the metrics listener to the unit's MainPID (`ss -ltnp`), and say what the check is: liveness of the daemon under the rendered unit. The stub hardcodes 127.0.0.1:9090 and ignores node.toml. validation: create the per-run tempdirs before their blocks, so a controller-side failure stops the play with its own error rather than being rescued and reported as a validation regression; sweep tempdirs older than a day that interrupted runs leaked; correct the comment on what a fixed path shared. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Overlapping `make molecule` runs are not isolated: container names are fixed per scenario on the one Docker daemon (across checkouts and worktrees too), every scenario starts by destroying its container, and lifecycle/cloud-init stage files in molecule's ephemeral dir, which is keyed on scenario, not run. `molecule` and `molecule-serial` now take a host-wide flock ($XDG_RUNTIME_DIR, else /tmp) and a second run exits 75 with a message instead of corrupting the first. `flock -o` keeps the lock out of anything molecule leaves running. scripts-test covers the refusal for both targets. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
Dependency installation remains outside the lock, the default lock path is not host-wide, and test failure cleanup can leak the holder process.
Get a fresh assessment by requesting another Copilot review.
Review effort: Lite
Findings: 2
Open (2)
What changed in this PR
This PR hardens Molecule validation and adds locking to prevent overlapping suite runs from corrupting shared Docker state.
Changes:
- Strengthens restart and metrics assertions.
- Isolates and cleans validation tempdirs.
- Adds suite locking, documentation, and refusal tests.
| File | Summary |
|---|---|
tests/scripts-test.sh |
Tests lock refusal behavior. |
CONTRIBUTING.md |
Documents Molecule locking requirements. |
ansible/molecule/validation/converge.yml |
Isolates and cleans validation tempdirs. |
ansible/molecule/schema/verify.yml |
Ties metrics checks to the daemon process. |
ansible/molecule/host-env/verify.yml |
Strengthens restart and metrics checks. |
ansible/molecule/host-env/side_effect.yml |
Validates clean pre-edit service state. |
ansible/Makefile |
Adds Molecule locking and fan-out targets. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Review of #76: - $XDG_RUNTIME_DIR is per user, so two users of one Docker daemon got different locks and could still destroy each other's fixed-name containers. The default is now /tmp/decdn-devops-molecule.lock, and it is a DIRECTORY: with fs.protected_regular (the Ubuntu/Debian default, 2 here) another user cannot open(O_CREAT) a regular file someone else created in sticky /tmp, so a file lock would fail the second user even with no suite running. flock(1) falls back to a read-only open on a directory, so 0755 is enough. - `deps` ran as a prerequisite, before the lock, so a second run could rewrite collections/ under a running suite. It is now a prerequisite of the unlocked bodies (molecule-fanout, and the new molecule-all for the serial target), which only run under the lock. scripts-test now holds a directory lock like the default, drops `-o deps`, and fails if deps runs while another suite holds the lock (it does against the previous Makefile). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Follow-up to #75, which was merged before its review findings were applied. This PR applies all of them.
host-env / schema (
test(molecule): act on the #75 review…)systemd_servicerejects a barename. It doesn't: default values satisfy that argument check, and on ansible-core 2.21.3-a name=…returnschanged: falseplus the fullstatus. The unit is now read with justname:.state: startedandcheck_modeare gone.Restart=alwaysis now asserted. The NRestarts check depends on it. Without it, a crashed unit is started by the converge'sstate: started, so a missing handler again shows a new PID with NRestarts 0.| intturns anything unparsable into 0.MainPIDviass -ltnp, in both host-env and schema. The comments now say what the check is: a liveness check on the daemon under the rendered unit. The stub hardcodes127.0.0.1:9090and ignoresnode.toml, so the bind address itself is still only checked indefault.validation
rescue:swallowed it and the final tally reported it as a validation regression.Suite lock (
build(molecule): refuse to overlap two suites on one host)#75 only removed the controller-side path collision. Overlapping suites still collided on:
destroy;make moleculeandmake molecule-serialnow take aflockshared by every user on the host: the directory/tmp/decdn-devops-molecule.lock. It's a directory becausefs.protected_regularstops users opening each other's regular files in/tmp.depsruns under the lock too, since it rewritesansible/collections, which a running suite reads. A second suite exits 75 with a message instead of breaking the first.flock -okeeps the lock out of anything molecule leaves running.tests/scripts-test.shcovers the refusal for both targets. CI's ubuntu runners already have util-linuxflock.Verification
make lint-ansible,make lint, andtests/scripts-test.shall pass (including the two new lock cases).make moleculepasses: 11/11 scenarios, run through the new locked path.make moleculestarted during that run was refused: "another molecule suite holds /run/user/1000/decdn-devops-molecule.lock".notifyand killed the daemon inside_effect. "running" and "PID changed" both still passed, which is the old false pass. The new assert failed withRestart=always, NRestarts=1.decdn-molecule-badbin.*dir and a freshdecdn-molecule-checkbin.*dir.validationremoved the old one and left the fresh one alone.initializing, all jobs waiting, andgrafana-cloud's container died. The role'sRestart decdn-nodehandler then waited indefinitely. The re-run passed cleanly. A suite that hangs instead of failing is a separate problem worth an issue: a restart timeout, or a boot-complete check inprepare.🤖 Generated with Claude Code