carlos / platform Public

Clone
git clone https://amadan.net/carlos/platform

Plain git — no account needed to clone.

Download

Download this file

1package host
2
3import (
4 "errors"
5 "fmt"
6 "log"
7 "os"
8 "time"
9
10 "github.com/carlosframework/platform/internal/controlname"
11)
12
13// The held-stage lock (final review finding 1): one flock per host,
14// <DataDir>/.moves/held-<host>.lock, taken through movesDir (O_NOFOLLOW
15// below a data dir root trusts, a regular file only), so root and the
16// sandboxed agent lock the same file whichever created it.
17//
18// A held publication (release.publishHeld, run through heldStageAuthority's
19// guard, by root's relay or by the agent on a writable box) holds it across
20// reading the live link, binding, the swap, the re-hash and any rollback.
21// T7's reading (release.VerifyHeldLive, from heldReleaseReady) takes it to
22// read the link and hash the bytes it names. So T7 never sees a link that a
23// failing publication is about to take back, and never hashes bytes the
24// publication has not finished vouching for.
25//
26// Lock order: the apply flock > floor.lock > the registry's write lock >
27// this one. The publication takes it inside WithHoldsFrozen; T7 takes it
28// holding nothing. Its holder takes no other lock and makes no network
29// call (D3's admit, which may read the bucket, runs before it on both
30// sides), so it never inverts the order and never holds the registry's
31// freeze for long.
32//
33// The file is removed when the stage finishes (region HA M-K25, #236), by
34// the holder, while it still holds the flock (unlockRemoving). Main's
35// empty-installation census refuses any file naming the host being
36// installed, so a lock left behind would refuse every later from-empty
37// install of that host on this box. A waiter that opened the removed file
38// re-checks the name after its flock and starts again on the new one
39// (lockNamed), so two holders never exist at once. A holder killed
40// mid-stage never removes it: the file stays, the census refuses that host
41// (region HA's CensusTree(".moves") fallback refuses only that host), and
42// the next stage of the host locks the same file and then removes it.
43
44// heldLockWait bounds the wait for a held-stage lock. The publication waits
45// for it inside the registry's freeze, after a claim read bounded by
46// heldStageFrozenReadTimeout, so the two stay well under the registry's
47// 5 s busy_timeout. Its holders keep it for a hash or two of one binary.
48// A variable so a test can make contention time out quickly.
49var heldLockWait = time.Second
50
51// errHeldLockBusy is the held-stage lock still held after heldLockWait:
52// a publication, or a reading, is under way. Never a reason to go ahead
53// without it.
54var errHeldLockBusy = errors.New("another process holds this host's held-stage lock")
55
56// heldStageLockFile is host's lock file in .moves.
57func heldStageLockFile(host string) string { return "held-" + host + ".lock" }
58
59// withHeldStageLock runs fn holding host's held-stage lock, making .moves
60// when it is missing, and removes the lock file before releasing it,
61// whatever fn returns. An error opening or locking is returned without
62// running fn. A removal that fails is logged and leaves the file as a crash
63// would; fn's result stands. Off Linux it always refuses (openMovesDir
64// does), which fails T7 and the publication closed; production boxes are
65// Linux.
66func withHeldStageLock(dataDir, host string, fn func() error) error {
67 if !controlname.ValidHostShape(host) {
68 return fmt.Errorf("held-stage lock: malformed host %q", host)
69 }
70 d, err := openMovesDir(dataDir, false)
71 if errors.Is(err, os.ErrNotExist) {
72 d, err = openMovesDir(dataDir, true)
73 }
74 if err != nil {
75 return err
76 }
77 defer d.Close()
78 if err := d.lockNamed(heldStageLockFile(host), heldLockWait, errHeldLockBusy); err != nil {
79 return err
80 }
81 err = fn()
82 if rerr := d.unlockRemoving(); rerr != nil {
83 log.Printf("moves: %s: held-stage lock: %v", host, rerr)
84 }
85 return err
86}
87