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 "bytes"
5 "context"
6 "encoding/json"
7 "errors"
8 "fmt"
9 "os"
10 "strings"
11 "testing"
12)
13
14// putRawRelease writes a release the way a NEWER shipper would: the artifact,
15// then a manifest whose JSON this binary did not marshal. extra is spliced in
16// before "artifacts" and must end with a comma when non-empty, for example
17// `"swap":"hold",`. Returns the manifest bytes exactly as stored.
18func putRawRelease(t *testing.T, s Store, acct, app, ver string, body []byte, extra string) []byte {
19 t.Helper()
20 ctx := context.Background()
21 if _, err := s.Put(ctx, ArtifactKey(acct, app, ver, adoptPlatform), body, ""); err != nil {
22 t.Fatalf("put artifact: %v", err)
23 }
24 raw := []byte(fmt.Sprintf(`{"schema":1,"app":%q,"version":%q,"kind":"binary",%s"artifacts":{%q:{"file":%q,"sha256":%q,"size":%d}}}`,
25 app, ver, extra, adoptPlatform, adoptPlatform, etagOf(body), len(body)))
26 if _, err := s.Put(ctx, ManifestKey(acct, app, ver), raw, ""); err != nil {
27 t.Fatalf("put manifest: %v", err)
28 }
29 return raw
30}
31
32func TestManifestSwapIsAdditive(t *testing.T) {
33 old := []byte(`{"schema":1,"app":"km","version":"de2766f8","kind":"binary","artifacts":{"linux-arm64":{"file":"linux-arm64","sha256":"` +
34 strings.Repeat("a", 64) + `","size":1}}}`)
35 var m Manifest
36 if err := json.Unmarshal(old, &m); err != nil {
37 t.Fatalf("a manifest written before swap existed no longer parses: %v", err)
38 }
39 if m.Swap != "" {
40 t.Fatalf("absent swap decoded as %q", m.Swap)
41 }
42 out, err := json.MarshalIndent(m, "", " ")
43 if err != nil {
44 t.Fatal(err)
45 }
46 // Byte-identical marshal is what keeps Ship's idempotent re-ship of an
47 // old release working.
48 if bytes.Contains(out, []byte(`"swap"`)) {
49 t.Fatalf("an empty swap leaked into the marshal:\n%s", out)
50 }
51}
52
53func TestShipRecordsAHoldDemand(t *testing.T) {
54 s := NewDirStore(t.TempDir())
55 ctx := context.Background()
56 m := Manifest{App: "km", Version: "v1", Kind: "binary", Swap: DemandHold,
57 Artifacts: map[string]Artifact{adoptPlatform: {}}}
58 if err := Ship(ctx, s, "bab", m, map[string][]byte{adoptPlatform: []byte("bin")}); err != nil {
59 t.Fatalf("Ship: %v", err)
60 }
61 got, err := GetManifest(ctx, s, "bab", "km", "v1")
62 if err != nil || got.Swap != DemandHold {
63 t.Fatalf("GetManifest = %+v, %v; want swap %q", got, err, DemandHold)
64 }
65}
66
67func TestShipRefusesAnUnknownSwapDemand(t *testing.T) {
68 s := NewDirStore(t.TempDir())
69 ctx := context.Background()
70 m := Manifest{App: "km", Version: "v1", Kind: "binary", Swap: "drain",
71 Artifacts: map[string]Artifact{adoptPlatform: {}}}
72 err := Ship(ctx, s, "bab", m, map[string][]byte{adoptPlatform: []byte("bin")})
73 if err == nil || !strings.Contains(err.Error(), `"drain"`) {
74 t.Fatalf("Ship with swap drain = %v; want a refusal naming the value", err)
75 }
76 if _, _, gerr := s.Get(ctx, ManifestKey("bab", "km", "v1")); gerr == nil {
77 t.Fatal("a refused ship still wrote its manifest")
78 }
79 if _, _, gerr := s.Get(ctx, ArtifactKey("bab", "km", "v1", adoptPlatform)); gerr == nil {
80 t.Fatal("a refused ship still wrote its artifact")
81 }
82}
83
84// Review Focus 2: a newer shipper's value must not stop a box reading the
85// manifest, and must act as hold.
86func TestReadsAcceptAnUnknownSwapDemand(t *testing.T) {
87 s := NewDirStore(t.TempDir())
88 putRawRelease(t, s, "bab", "km", "v1", []byte("bin"), `"swap":"drain",`)
89 m, err := GetManifest(context.Background(), s, "bab", "km", "v1")
90 if err != nil {
91 t.Fatalf("a manifest from a newer shipper must still read: %v", err)
92 }
93 if got := NormalizeDemand(m.Swap); got != DemandHold {
94 t.Fatalf("NormalizeDemand(%q) = %q; an unknown demand must act as hold", m.Swap, got)
95 }
96}
97
98func TestNormalizeDemand(t *testing.T) {
99 for in, want := range map[string]string{"": "", "hold": "hold", "overlap": "hold", "HOLD": "hold", "drain": "hold"} {
100 if got := NormalizeDemand(in); got != want {
101 t.Errorf("NormalizeDemand(%q) = %q, want %q", in, got, want)
102 }
103 }
104}
105
106// unreadableStore answers every Get with an error that is not "not found": a
107// bucket this box cannot read right now.
108type unreadableStore struct{ Store }
109
110func (unreadableStore) Get(context.Context, string) ([]byte, string, error) {
111 return nil, "", errors.New("bucket unreachable")
112}
113
114// Spec §12 "Swap mode source": a staged copy without swap, from an older
115// root, cannot produce an overlap for a hold build.
116func TestCanonicalDemandReadsTheCanonicalManifestNotTheSibling(t *testing.T) {
117 s := NewDirStore(t.TempDir())
118 ctx := context.Background()
119 opt := t.TempDir()
120 putRawRelease(t, s, "bab", "km", "v1", []byte("bin"), `"swap":"hold",`)
121 if err := StageArtifact(opt, "bab", "v1", "km", []byte("bin")); err != nil {
122 t.Fatal(err)
123 }
124 canon, err := GetManifest(ctx, s, "bab", "km", "v1")
125 if err != nil {
126 t.Fatal(err)
127 }
128 lossy := *canon
129 lossy.Swap = "" // what an older root's re-marshal wrote
130 b, err := json.MarshalIndent(lossy, "", " ")
131 if err != nil {
132 t.Fatal(err)
133 }
134 if err := StageManifest(opt, "bab", "v1", "km", b); err != nil {
135 t.Fatal(err)
136 }
137 demand, digest, err := CanonicalDemand(ctx, s, "bab", "km", "v1", adoptPlatform, VersionPath(opt, "bab", "v1", "km"))
138 if err != nil || demand != DemandHold || digest != etagOf([]byte("bin")) {
139 t.Fatalf("CanonicalDemand = %q, %q, %v; want hold from the canonical manifest", demand, digest, err)
140 }
141}
142
143func TestCanonicalDemandWithNoDemand(t *testing.T) {
144 s := NewDirStore(t.TempDir())
145 opt := t.TempDir()
146 putRawRelease(t, s, "bab", "km", "v1", []byte("bin"), "")
147 if err := StageArtifact(opt, "bab", "v1", "km", []byte("bin")); err != nil {
148 t.Fatal(err)
149 }
150 demand, digest, err := CanonicalDemand(context.Background(), s, "bab", "km", "v1", adoptPlatform, VersionPath(opt, "bab", "v1", "km"))
151 if err != nil || demand != "" || digest != etagOf([]byte("bin")) {
152 t.Fatalf("CanonicalDemand = %q, %q, %v; want no demand", demand, digest, err)
153 }
154}
155
156// Spec §12 "Artifact binding": a re-shipped name with different bytes, plus
157// an old staged copy, is a staging error.
158func TestCanonicalDemandRefusesBytesTheReleaseDoesNotName(t *testing.T) {
159 s := NewDirStore(t.TempDir())
160 opt := t.TempDir()
161 putRawRelease(t, s, "bab", "km", "v1", []byte("new bytes"), "")
162 if err := StageArtifact(opt, "bab", "v1", "km", []byte("old bytes")); err != nil {
163 t.Fatal(err)
164 }
165 demand, digest, err := CanonicalDemand(context.Background(), s, "bab", "km", "v1", adoptPlatform, VersionPath(opt, "bab", "v1", "km"))
166 if !errors.Is(err, ErrStagedDigestMismatch) {
167 t.Fatalf("err = %v, want ErrStagedDigestMismatch", err)
168 }
169 if demand != "" || digest != etagOf([]byte("old bytes")) {
170 t.Fatalf("CanonicalDemand = %q, %q; want no demand and the staged file's own digest", demand, digest)
171 }
172}
173
174// Spec §4.2: if the canonical manifest cannot be read, or the digests cannot
175// be compared, the attempt runs in hold. Review Focus 2 is the last case.
176func TestCanonicalDemandIsHoldWhenItCannotCompare(t *testing.T) {
177 ctx := context.Background()
178 staged := func(t *testing.T, opt string) string {
179 if err := StageArtifact(opt, "bab", "v1", "km", []byte("bin")); err != nil {
180 t.Fatal(err)
181 }
182 return VersionPath(opt, "bab", "v1", "km")
183 }
184 t.Run("manifest missing", func(t *testing.T) {
185 opt := t.TempDir()
186 d, sum, err := CanonicalDemand(ctx, NewDirStore(t.TempDir()), "bab", "km", "v1", adoptPlatform, staged(t, opt))
187 if err != nil || d != DemandHold || sum != etagOf([]byte("bin")) {
188 t.Fatalf("= %q, %q, %v; want hold", d, sum, err)
189 }
190 })
191 t.Run("store unreadable", func(t *testing.T) {
192 opt := t.TempDir()
193 s := NewDirStore(t.TempDir())
194 putRawRelease(t, s, "bab", "km", "v1", []byte("bin"), "")
195 d, _, err := CanonicalDemand(ctx, unreadableStore{s}, "bab", "km", "v1", adoptPlatform, staged(t, opt))
196 if err != nil || d != DemandHold {
197 t.Fatalf("= %q, %v; want hold", d, err)
198 }
199 })
200 t.Run("no build for this platform", func(t *testing.T) {
201 opt := t.TempDir()
202 s := NewDirStore(t.TempDir())
203 putRawRelease(t, s, "bab", "km", "v1", []byte("bin"), "")
204 d, _, err := CanonicalDemand(ctx, s, "bab", "km", "v1", "plan9-mips", staged(t, opt))
205 if err != nil || d != DemandHold {
206 t.Fatalf("= %q, %v; want hold", d, err)
207 }
208 })
209 t.Run("staged file missing", func(t *testing.T) {
210 s := NewDirStore(t.TempDir())
211 putRawRelease(t, s, "bab", "km", "v1", []byte("bin"), "")
212 missing := VersionPath(t.TempDir(), "bab", "v1", "km")
213 if _, err := os.Stat(missing); err == nil {
214 t.Fatal("setup: the path exists")
215 }
216 d, sum, err := CanonicalDemand(ctx, s, "bab", "km", "v1", adoptPlatform, missing)
217 if err != nil || d != DemandHold || sum != "" {
218 t.Fatalf("= %q, %q, %v; want hold and no digest", d, sum, err)
219 }
220 })
221 t.Run("unknown demand", func(t *testing.T) {
222 opt := t.TempDir()
223 s := NewDirStore(t.TempDir())
224 putRawRelease(t, s, "bab", "km", "v1", []byte("bin"), `"swap":"drain",`)
225 d, _, err := CanonicalDemand(ctx, s, "bab", "km", "v1", adoptPlatform, staged(t, opt))
226 if err != nil || d != DemandHold {
227 t.Fatalf("= %q, %v; want hold", d, err)
228 }
229 })
230}
231