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 hostexec
4
5import (
6 "bytes"
7 "context"
8 "encoding/json"
9 "strings"
10 "testing"
11 "time"
12
13 "github.com/carlosframework/platform/internal/hostbox"
14 "github.com/carlosframework/platform/internal/moves"
15 "github.com/carlosframework/platform/internal/park"
16)
17
18type scopeLease struct {
19 bucket, scope string
20 releases, closes int
21}
22
23func (l *scopeLease) Check(context.Context) error { return nil }
24func (l *scopeLease) Release(context.Context) error { l.releases++; return nil }
25func (l *scopeLease) Close() { l.closes++ }
26func (l *scopeLease) LeaseScope() (string, string) { return l.bucket, l.scope }
27
28type scopePool struct{ acquired []string }
29
30func (p *scopePool) Acquire(_ context.Context, bucket, scope string) (park.HeldLease, error) {
31 p.acquired = append(p.acquired, bucket+":"+scope)
32 return &scopeLease{bucket: bucket, scope: scope}, nil
33}
34
35func TestPlan14ExecutionKeepsOtherLeaseScopes(t *testing.T) {
36 t.Run("staging", func(t *testing.T) {
37 b, _, remote, clock, q, peer := brokerFixture(t)
38 if moves.StagingLeaseTTL != 45*time.Minute {
39 t.Fatal("staging horizon changed")
40 }
41 wall, _, _ := clock.Now()
42 key := moves.StagingLeasePrefix("ie", q.Host) + "/lock.json"
43 raw, _ := json.Marshal(Record{Op: q.Op, Expires: wall.Add(moves.StagingLeaseTTL)})
44 tag, err := remote.Put(t.Context(), key, raw, "")
45 if err != nil {
46 t.Fatal(err)
47 }
48 permit, err := b.Execute(t.Context(), peer, q)
49 if err != nil {
50 t.Fatal(err)
51 }
52 q.Operation, q.ID, q.Token = "executionSettle", "settle", permit.Token
53 if _, err := b.Execute(t.Context(), peer, q); err != nil {
54 t.Fatal(err)
55 }
56 got, next, err := remote.Get(t.Context(), key)
57 if err != nil || next != tag || !bytes.Equal(got, raw) {
58 t.Fatal("execution settlement touched staging", err)
59 }
60 })
61 t.Run("borrowed", func(t *testing.T) {
62 own := &scopeLease{bucket: "data", scope: "moves/shop.example/op"}
63 pool := &scopePool{}
64 borrower, err := (hostbox.AggregateLeases{Leases: pool, Resolve: hostbox.ReplicaParkLeaseSet}).AcquireHeld(t.Context(), own.bucket, own.scope, own)
65 if err != nil {
66 t.Fatal(err)
67 }
68 if err := borrower.Release(t.Context()); err != nil {
69 t.Fatal(err)
70 }
71 borrower.Close()
72 if own.releases != 0 || own.closes != 0 || len(pool.acquired) != 0 {
73 t.Fatal("borrower released or reacquired lender's lease")
74 }
75 if err := own.Release(t.Context()); err != nil {
76 t.Fatal(err)
77 }
78 own.Close()
79 if own.releases != 1 || own.closes != 1 {
80 t.Fatal("lender could not release")
81 }
82 })
83 t.Run("replica-data-bucket", func(t *testing.T) {
84 for _, scope := range []string{"moves/shop.example/op", "instances/shop.example", "instances/shop.example/" + strings.Repeat("a", 64)} {
85 pool := &scopePool{}
86 held, err := (hostbox.AggregateLeases{Leases: pool, Resolve: hostbox.ReplicaParkLeaseSet}).Acquire(t.Context(), "data", scope)
87 if err != nil {
88 t.Fatal(err)
89 }
90 held.Close()
91 if len(pool.acquired) != 1 || pool.acquired[0] != "data:"+scope {
92 t.Fatalf("broadened scope: %v", pool.acquired)
93 }
94 }
95 if _, err := hostbox.ReplicaParkLeaseSet(t.Context(), "data", moves.StagingLeasePrefix("ie", "shop.example")); err == nil {
96 t.Fatal("deployment staging became data-bucket scope")
97 }
98 })
99}
100