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 hostbox
4
5import (
6 "context"
7 "errors"
8 "os"
9 "path/filepath"
10 "testing"
11)
12
13func TestTypedParkerMovesExactFileAndSymlinkWithoutFollowing(t *testing.T) {
14 root := t.TempDir()
15 data := filepath.Join(root, "data")
16 if err := os.Mkdir(data, 0o700); err != nil {
17 t.Fatal(err)
18 }
19 if err := os.WriteFile(filepath.Join(data, "db"), []byte("db"), 0o600); err != nil {
20 t.Fatal(err)
21 }
22 if err := os.Symlink("db", filepath.Join(data, "live")); err != nil {
23 t.Fatal(err)
24 }
25 p := TypedParker{Roots: map[string]string{"data": data}, ParkedRoot: root, Incarnation: "inc", Host: "shop.example", Retirement: "ret"}
26 file, err := p.CaptureArtifact("data", "db", "owner", ArtifactPath)
27 if err != nil {
28 t.Fatal(err)
29 }
30 link, err := p.CaptureArtifact("data", "live", "owner", ArtifactLiveLink)
31 if err != nil {
32 t.Fatal(err)
33 }
34 if file.Ino == link.Ino || file.Type == link.Type {
35 t.Fatal("capture followed symlink")
36 }
37 if err := p.Park(context.Background(), "ignored", file); err != nil {
38 t.Fatal(err)
39 }
40 if err := p.Park(context.Background(), "ignored", link); err != nil {
41 t.Fatal(err)
42 }
43 parked := filepath.Join(root, ".parked", "inc", "shop.example", "ret", "data")
44 if got, err := os.Readlink(filepath.Join(parked, "live")); err != nil || got != "db" {
45 t.Fatalf("parked symlink=%q err=%v", got, err)
46 }
47 // Missing source replay accepts only the exact recorded destination inode.
48 if err := p.Park(context.Background(), "ignored", file); err != nil {
49 t.Fatalf("idempotent replay: %v", err)
50 }
51}
52
53func TestTypedParkerRefusesSuccessorAndSymlinkIntermediate(t *testing.T) {
54 root := t.TempDir()
55 data := filepath.Join(root, "data")
56 other := filepath.Join(root, "other")
57 os.Mkdir(data, 0o700)
58 os.Mkdir(other, 0o700)
59 if err := os.WriteFile(filepath.Join(data, "db"), []byte("old"), 0o600); err != nil {
60 t.Fatal(err)
61 }
62 p := TypedParker{Roots: map[string]string{"data": data}, ParkedRoot: root, Incarnation: "inc", Host: "shop.example", Retirement: "ret"}
63 a, err := p.CaptureArtifact("data", "db", "owner", ArtifactPath)
64 if err != nil {
65 t.Fatal(err)
66 }
67 held, err := os.Open(filepath.Join(data, "db"))
68 if err != nil {
69 t.Fatal(err)
70 }
71 defer held.Close()
72 if err := os.Remove(filepath.Join(data, "db")); err != nil {
73 t.Fatal(err)
74 }
75 if err := os.WriteFile(filepath.Join(data, "db"), []byte("successor"), 0o600); err != nil {
76 t.Fatal(err)
77 }
78 if err := p.Park(context.Background(), "ignored", a); !errors.Is(err, ErrChanged) {
79 t.Fatalf("successor error=%v", err)
80 }
81 if err := os.Symlink(other, filepath.Join(data, "escape")); err != nil {
82 t.Fatal(err)
83 }
84 if _, err := p.CaptureArtifact("data", "escape/file", "owner", ArtifactPath); err == nil {
85 t.Fatal("symlink intermediate accepted")
86 }
87}
88
89func TestTypedParkerNoReplacePreservesExistingDestination(t *testing.T) {
90 root := t.TempDir()
91 data := filepath.Join(root, "data")
92 dest := filepath.Join(root, ".parked", "inc", "shop.example", "ret", "data")
93 os.Mkdir(data, 0o700)
94 os.MkdirAll(dest, 0o700)
95 os.WriteFile(filepath.Join(data, "db"), []byte("old"), 0o600)
96 os.WriteFile(filepath.Join(dest, "db"), []byte("occupied"), 0o600)
97 p := TypedParker{Roots: map[string]string{"data": data}, ParkedRoot: root, Incarnation: "inc", Host: "shop.example", Retirement: "ret"}
98 a, err := p.CaptureArtifact("data", "db", "owner", ArtifactPath)
99 if err != nil {
100 t.Fatal(err)
101 }
102 if err := p.Park(context.Background(), "ignored", a); !errors.Is(err, ErrChanged) {
103 t.Fatalf("error=%v", err)
104 }
105 if string(mustReadTyped(t, filepath.Join(data, "db"))) != "old" || string(mustReadTyped(t, filepath.Join(dest, "db"))) != "occupied" {
106 t.Fatal("no-replace modified source or destination")
107 }
108}
109
110func TestTypedParkerRetryAfterRenameFsyncFailureSyncsBothParents(t *testing.T) {
111 root := t.TempDir()
112 data := filepath.Join(root, "data")
113 os.Mkdir(data, 0o700)
114 os.WriteFile(filepath.Join(data, "db"), []byte("old"), 0o600)
115 calls := 0
116 p := TypedParker{Roots: map[string]string{"data": data}, ParkedRoot: root, Incarnation: "inc", Host: "shop.example", Retirement: "ret", syncParents: func(a, b int) error {
117 calls++
118 if calls == 1 {
119 return errors.New("injected fsync failure")
120 }
121 return syncFDs(a, b)
122 }}
123 a, err := p.CaptureArtifact("data", "db", "owner", ArtifactPath)
124 if err != nil {
125 t.Fatal(err)
126 }
127 if err := p.Park(context.Background(), "ignored", a); err == nil {
128 t.Fatal("first park did not report fsync failure")
129 }
130 if _, err := os.Stat(filepath.Join(data, "db")); !errors.Is(err, os.ErrNotExist) {
131 t.Fatalf("rename did not happen: %v", err)
132 }
133 if err := p.Park(context.Background(), "ignored", a); err != nil {
134 t.Fatalf("retry: %v", err)
135 }
136 if calls != 2 {
137 t.Fatalf("sync calls=%d, want initial and replay", calls)
138 }
139}
140
141func TestTypedParkerKeepsRecordedNestedMount(t *testing.T) {
142 p := TypedParker{CapturePersistent: true, Roots: map[string]string{"data": "/data"}, ParkedRoots: map[string]string{"data": "/data"}}
143 for _, tc := range []struct{ name, mount, artifact, want string }{
144 {"bind mount", "/data/instances", "instances/shop.example", "/data/instances"},
145 {"root mount", "/data", "instances/shop.example", "/data"},
146 {"ancestor mount", "/", "instances/shop.example", "/data"},
147 {"absent legacy mount", "", "instances/shop.example", "/data"},
148 {"mount itself", "/data/instances", "instances", ""},
149 {"foreign mount", "/elsewhere", "instances/shop.example", ""},
150 {"prefix collision", "/data/instance", "instances/shop.example", ""},
151 {"unclean mount", "/data/../data/instances", "instances/shop.example", ""},
152 } {
153 t.Run(tc.name, func(t *testing.T) {
154 a := Artifact{Root: "data", Name: tc.artifact}
155 a.Identity.MountPoint = tc.mount
156 got, err := p.ParkedArtifactRoot(a)
157 if got != tc.want || (err != nil) != (tc.want == "") {
158 t.Fatalf("root=%q error=%v, want %q", got, err, tc.want)
159 }
160 })
161 }
162}
163
164func TestTypedParkerLegacyDestinationUnchanged(t *testing.T) {
165 p := TypedParker{Roots: map[string]string{"data": "/data"}, ParkedRoots: map[string]string{"data": "/data"}}
166 a := Artifact{Root: "data", Name: "instances/shop.example"}
167 a.Identity.MountPoint = "/data/instances"
168 root, err := p.ParkedArtifactRoot(a)
169 if err != nil || root != "/data" {
170 t.Fatalf("legacy destination=%q error=%v", root, err)
171 }
172}
173