One import per image at a time, and run answers 126/127 for a command that cannot start (0.11.0) #13

Merged
mi merged 2 commits from mihoe/import-lock into main 2026-10-05 17:29:21 +02:00
Owner

Found on mih09 with Werkator 1.37.0 (repository werkrevisor, PR#5): a CI build and a pull-request review started together and both imported the same new rootfs.

Imports of one image wait for each other

Store.Import stages in <image>.tmp and removes that directory first, so the next import cleans up after an interrupted one.
A second import at the same time removed the first one's files: one failed with latest.tmp/image.json: no such file or directory, the other's sandbox could not execute /bin/sh.
An import now holds an exclusive flock on $WERKDOCK_HOME/locks/import-<hash>.lock, across goroutines and processes; the kernel drops it when the holder dies.
The import that waited refuses with already exists, as an import after it would.

TestConcurrentImportsOfOneImageLeaveOneCompleteImage: four imports of a 3000-file archive at once leave one success, three refusals and one complete image. It failed 3 of 3 runs without the lock.

werkdock run answers 127 or 126

engine.CheckCommand looks the command up like execvp inside the rootfs (symlinks resolved in the rootfs, PATH in order, a non-executable candidate skipped for a later one).
run refuses a command that cannot start with docker's 127 (not found) or 126 (not executable) before anything is created; until now bwrap's 1 came back.
Paths under a mount, the implicit tmpfs areas included, are not checked. create/start and the Docker API are unchanged.
The resolver moved from store to its own package rootpath, shared with engine.

Smoke-tested with the built binary: run smoke missing exits 127, a file without execute bit 126, a second import 125 with already exists.

Version 0.11.0 with its CHANGELOG section.

🤖 Generated with Claude Code

Found on mih09 with Werkator 1.37.0 (repository werkrevisor, PR#5): a CI build and a pull-request review started together and both imported the same new rootfs. ## Imports of one image wait for each other `Store.Import` stages in `<image>.tmp` and removes that directory first, so the next import cleans up after an interrupted one. A second import at the same time removed the first one's files: one failed with `latest.tmp/image.json: no such file or directory`, the other's sandbox could not execute `/bin/sh`. An import now holds an exclusive `flock` on `$WERKDOCK_HOME/locks/import-<hash>.lock`, across goroutines and processes; the kernel drops it when the holder dies. The import that waited refuses with `already exists`, as an import after it would. `TestConcurrentImportsOfOneImageLeaveOneCompleteImage`: four imports of a 3000-file archive at once leave one success, three refusals and one complete image. It failed 3 of 3 runs without the lock. ## `werkdock run` answers 127 or 126 `engine.CheckCommand` looks the command up like execvp inside the rootfs (symlinks resolved in the rootfs, PATH in order, a non-executable candidate skipped for a later one). `run` refuses a command that cannot start with docker's 127 (not found) or 126 (not executable) before anything is created; until now bwrap's 1 came back. Paths under a mount, the implicit tmpfs areas included, are not checked. `create`/`start` and the Docker API are unchanged. The resolver moved from `store` to its own package `rootpath`, shared with `engine`. Smoke-tested with the built binary: `run smoke missing` exits 127, a file without execute bit 126, a second import 125 with `already exists`. Version 0.11.0 with its CHANGELOG section. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
mi added 1 commit 2026-10-05 16:57:22 +02:00
mi added 1 commit 2026-10-05 17:02:16 +02:00
Two imports of the same image removed each other's staging directory,
seen with two Werkator builds that started together on mih09. An import
now holds an exclusive flock per image below $WERKDOCK_HOME/locks.

werkdock run looks the command up like execvp inside the rootfs and
answers docker's 127 or 126 when it cannot be started, instead of
bwrap's 1. Paths under a mount are not checked. The rootfs path resolver
moved from store into its own package rootpath, shared with engine.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
mi changed title from WIP: One import per image at a time, and run answers 126/127 for a command that cannot start to One import per image at a time, and run answers 126/127 for a command that cannot start (0.11.0) 2026-10-05 17:02:26 +02:00
mi merged commit ea30cbe79c into main 2026-10-05 17:29:21 +02:00
mi deleted branch mihoe/import-lock 2026-10-05 17:29:21 +02:00
Sign in to join this conversation.
No Reviewers
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: mi/werkdock#13