carlos / platform Public

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

Plain git — no account needed to clone.

Download

Download this file

1//go:build linux
2
3package host
4
5import (
6 "errors"
7 "os"
8 "path/filepath"
9 "testing"
10 "time"
11
12 "github.com/carlosframework/platform/internal/release"
13)
14
15// Final review finding 1: the held-stage lock is a per-host flock that a
16// second holder (another open, as another process makes) waits for and
17// then gives up on; other hosts do not contend; a planted symlink is
18// refused.
19func TestHeldStageLockSerialisesItsHolders(t *testing.T) {
20 prev := heldLockWait
21 t.Cleanup(func() { heldLockWait = prev })
22 heldLockWait = 50 * time.Millisecond
23 data := t.TempDir()
24 in, out, done := make(chan struct{}), make(chan struct{}), make(chan error, 1)
25 go func() {
26 done <- withHeldStageLock(data, hsHost, func() error { close(in); <-out; return nil })
27 }()
28 <-in
29 ran := false
30 if err := withHeldStageLock(data, hsHost, func() error { ran = true; return nil }); !errors.Is(err, errHeldLockBusy) || ran {
31 t.Fatalf("a second holder: %v (ran %v), want errHeldLockBusy", err, ran)
32 }
33 if err := withHeldStageLock(data, "other.example.com", func() error { ran = true; return nil }); err != nil || !ran {
34 t.Fatalf("another host's lock: %v (ran %v)", err, ran)
35 }
36 close(out)
37 if err := <-done; err != nil {
38 t.Fatal(err)
39 }
40 ran = false
41 if err := withHeldStageLock(data, hsHost, func() error { ran = true; return nil }); err != nil || !ran {
42 t.Fatalf("after release: %v (ran %v)", err, ran)
43 }
44 if err := withHeldStageLock(data, "../x", func() error { return nil }); err == nil {
45 t.Fatal("a malformed host was locked")
46 }
47 if err := os.Symlink(filepath.Join(t.TempDir(), "elsewhere"), filepath.Join(data, ".moves", heldStageLockFile("planted.example.com"))); err != nil {
48 t.Fatal(err)
49 }
50 if err := withHeldStageLock(data, "planted.example.com", func() error { return nil }); err == nil {
51 t.Fatal("a planted symlink was followed")
52 }
53}
54
55// The publication runs under the lock: heldStageAuthority's then (the
56// publish closure: link read, bind, swap, re-hash, rollback) holds host's
57// held-stage lock, inside the registry's freeze. The pre-stage check
58// (then nil) takes no lock.
59func TestHeldPublicationHoldsTheHeldStageLock(t *testing.T) {
60 prev := heldLockWait
61 t.Cleanup(func() { heldLockWait = prev })
62 heldLockWait = 50 * time.Millisecond
63 s := release.NewDirStore(t.TempDir())
64 hsClaim(t, s, movingOp())
65 hsReport(t, s, hsOp, "v7")
66 data := t.TempDir()
67 auth := heldStageAuthority(testRegistry(t), s, hsBox, data)
68 if err := auth(hsRequest(), nil); err != nil {
69 t.Fatal(err)
70 }
71 if _, err := os.Stat(filepath.Join(data, ".moves", heldStageLockFile(hsHost))); !errors.Is(err, os.ErrNotExist) {
72 t.Fatalf("the pre-stage check took the lock: %v", err)
73 }
74 published := false
75 err := auth(hsRequest(), func() error {
76 published = true
77 return withHeldStageLock(data, hsHost, func() error { return errors.New("the lock was free during the publication") })
78 })
79 if !published || !errors.Is(err, errHeldLockBusy) {
80 t.Fatalf("publication: %v (ran %v), want the lock held around it", err, published)
81 }
82}
83