carlos / platform Public

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

Plain git — no account needed to clone.

Download

Download this file

1package release
2
3import (
4 "context"
5 "errors"
6 "fmt"
7 "io/fs"
8 "os"
9 "path/filepath"
10 "runtime"
11 "syscall"
12 "testing"
13)
14
15// An already-staged release still needs a privileged pass when the agent's
16// sandbox refuses the live symlink swap. The old build must remain recorded
17// and running until that pass can complete the adoption.
18func TestPassRelaysBlockedLiveSwapForStagedRelease(t *testing.T) {
19 if os.Geteuid() == 0 || runtime.GOOS == "windows" {
20 t.Skip("requires Unix directory permissions enforced for a non-root user")
21 }
22 ctx := context.Background()
23 s := NewDirStore(t.TempDir())
24 priv, pub := testKeys(t)
25 opt := t.TempDir()
26 reg := testRegistry(t)
27 t.Cleanup(func() { reg.Close() })
28 const host = "blocked.example.com"
29 for _, ver := range []string{"v1", "v2"} {
30 shipRelease(t, s, "bab", "myapp", ver, adoptPlatform, []byte(ver))
31 }
32 if err := Promote(ctx, s, priv, "bab", "myapp", "canary/x", "v1"); err != nil {
33 t.Fatal(err)
34 }
35 addRoute(t, reg, host, "myapp", "canary/x", false)
36 if err := Pass(ctx, reg, s, pub, opt, nil); err != nil {
37 t.Fatal(err)
38 }
39 if _, err := ensureStaged(ctx, s, opt, "bab", "myapp", "v2", host); err != nil {
40 t.Fatal(err)
41 }
42 if err := Promote(ctx, s, priv, "bab", "myapp", "canary/x", "v2"); err != nil {
43 t.Fatal(err)
44 }
45 live := filepath.Dir(LiveLink(opt, host))
46 t.Cleanup(func() { os.Chmod(live, 0o755) })
47 if err := os.Chmod(live, 0o555); err != nil {
48 t.Fatal(err)
49 }
50 var requests, restarts int
51 restart := func(string) error { restarts++; return nil }
52 request := func(gotHost, ver string, err error) {
53 requests++
54 if gotHost != host || ver != "v2" || !errors.Is(err, os.ErrPermission) {
55 t.Errorf("relay = (%q, %q, %v), want blocked v2 live swap", gotHost, ver, err)
56 }
57 }
58 if err := Pass(ctx, reg, s, pub, opt, restart, OnStageBlocked(request)); err != nil {
59 t.Fatal(err)
60 }
61 if requests != 1 || restarts != 0 {
62 t.Fatalf("requests=%d restarts=%d, want 1 and 0", requests, restarts)
63 }
64 if ver, err := LiveVersion(opt, host); err != nil || ver != "v1" {
65 t.Fatalf("live version = %q, %v; want v1", ver, err)
66 }
67 if rt, err := reg.Get(host); err != nil || rt == nil || rt.Version != "v1" {
68 t.Fatalf("registry route = %+v, %v; want v1", rt, err)
69 }
70
71 // Model the requested privileged pass: once the live directory is
72 // writable, the same staged release adopts normally.
73 if err := os.Chmod(live, 0o755); err != nil {
74 t.Fatal(err)
75 }
76 if err := Pass(ctx, reg, s, pub, opt, restart, OnStageBlocked(request)); err != nil {
77 t.Fatal(err)
78 }
79 if ver, err := LiveVersion(opt, host); err != nil || ver != "v2" || requests != 1 || restarts != 1 {
80 t.Fatalf("after retry: version=%q err=%v requests=%d restarts=%d", ver, err, requests, restarts)
81 }
82}
83
84// stageBlocked is deliberately narrow: it must match "this uid cannot write
85// the version store" and nothing else. A callback that fired on any staging
86// error would relay a bucket outage into a root pass on every tick — and none
87// of those failures is fixed by re-running as root.
88func TestStageBlockedMatchesOnlyWriteRefusals(t *testing.T) {
89 for _, tc := range []struct {
90 name string
91 err error
92 want bool
93 }{
94 // The production shape: mkdir under a ProtectSystem=strict sandbox.
95 {"EROFS via PathError", &os.PathError{Op: "mkdir", Path: "/opt/carlos/versions/abc", Err: syscall.EROFS}, true},
96 {"EACCES via PathError", &os.PathError{Op: "openat", Path: "/opt/carlos/versions/abc", Err: syscall.EACCES}, true},
97 {"EPERM via PathError", &os.PathError{Op: "mkdir", Path: "/opt/carlos/versions/abc", Err: syscall.EPERM}, true},
98 // Wrapped, because the pass wraps before it reports.
99 {"wrapped EROFS", fmt.Errorf("stage abc: %w", &os.PathError{Op: "mkdir", Err: syscall.EROFS}), true},
100
101 // Everything a root pass would fail at identically.
102 {"not exist", fs.ErrNotExist, false},
103 {"bad signature", errors.New("manifest signature does not verify"), false},
104 {"sha mismatch", errors.New("artifact sha256 mismatch"), false},
105 {"bucket 403", errors.New("operation error S3: GetObject, api error AccessDenied"), false},
106 {"nil", nil, false},
107 } {
108 t.Run(tc.name, func(t *testing.T) {
109 if got := stageBlocked(tc.err); got != tc.want {
110 t.Errorf("stageBlocked(%v) = %v, want %v", tc.err, got, tc.want)
111 }
112 })
113 }
114}
115
116// The option must be opt-in: every existing caller and test passes no
117// OnStageBlocked, and those calls must behave exactly as they always have.
118func TestOnStageBlockedUnsetByDefault(t *testing.T) {
119 var c passConfig
120 if c.stageBlocked != nil {
121 t.Fatal("passConfig.stageBlocked must default to nil")
122 }
123 called := false
124 OnStageBlocked(func(string, string, error) { called = true })(&c)
125 if c.stageBlocked == nil {
126 t.Fatal("OnStageBlocked did not set the hook")
127 }
128 c.stageBlocked("h", "v", syscall.EROFS)
129 if !called {
130 t.Error("hook not invoked")
131 }
132}
133