carlos / platform Public

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

Plain git — no account needed to clone.

Download

Download this file

1package hostclaim
2
3import (
4 "errors"
5 "reflect"
6 "strconv"
7 "testing"
8 "time"
9)
10
11func TestPlan12TargetFenceRelease(t *testing.T) {
12 for _, kind := range []string{KindLanding, KindRestoring} {
13 for _, mode := range []string{"ok", "wrong-op", "wrong-target", "wrong-enrollment", "wrong-region", "unverified", "zero-proof", "stale", "committed", "partial-return", "parks", "no-grant"} {
14 t.Run(kind+"/"+mode, func(t *testing.T) {
15 s, o, ref, a := c7Fixture(t)
16 var err error
17 o, err = Update(t.Context(), s, o, func(c *Claim) error {
18 c.Obligations = nil
19 if mode == "parks" {
20 for _, owner := range []string{"box:adi-knitting", "box:other", "region:ie"} {
21 id := "existing-" + strconv.Itoa(len(c.Obligations))
22 c.Obligations = append(c.Obligations, Obligation{ID: id, Op: c.Op.ID, Kind: "park-generation", Generation: id, Region: "ie", Owner: owner, Retirement: id})
23 }
24 }
25 c.Op.Kind, c.Op.Phase, c.Op.Exec = kind, PhaseProbing, "adi-knitting"
26 c.Op.Participants = []string{"bri-knitting", "adi-knitting"}
27 c.Op.Intent["target"], c.Op.Intent["source"], c.Op.Intent["to"], c.Op.Intent["target_enrollment"] = "adi-knitting", "bri-knitting", "ie", ref.Enrollment
28 setJournal(c, JournalSourceStamp, "1")
29 setJournal(c, JournalSrcGen, "src")
30 if kind == KindRestoring {
31 c.Op.Phase = PhaseReleasing
32 c.Op.Intent["rollback"] = lostRB
33 setJournal(c, JournalTargetStamp, strconv.FormatUint(c.Holder.Stamp, 10))
34 }
35 if mode == "wrong-target" {
36 c.Op.Intent["target"] = "other"
37 }
38 if mode == "wrong-enrollment" {
39 c.Op.Intent["target_enrollment"] = "bad"
40 }
41 if mode == "wrong-region" {
42 c.Op.Intent["to"] = "uk"
43 }
44 if mode == "committed" {
45 c.Op.Phase = PhaseCommitted
46 }
47 return nil
48 })
49 if err != nil {
50 t.Fatal(err)
51 }
52 if mode == "unverified" {
53 a.err = ErrFence
54 }
55 f, err := VerifyMoveTargetFence(t.Context(), s, o, ref, a)
56 n := 0
57 scope := func(loc string) ParkScope {
58 return ParkScope{Location: loc, Incarnation: o.Claim.Holder.Incarnation, EvidenceAt: f.ObservedAt(), RequestLimit: 2 * time.Minute}
59 }
60 r := FencedTargetRelease{OpID: o.Claim.Op.ID, RollbackID: lostRB, Target: "adi-knitting", TargetRegion: "ie", SourceRegion: "uk", Location: scope(o.Claim.Holder.Location), Lane: scope("moves/" + o.Host + "/" + o.Claim.Op.ID), Fence: f, NewID: func() string { n++; return "fence-" + strconv.Itoa(n) }}
61 if mode == "wrong-op" {
62 r.OpID = "other"
63 }
64 if mode == "zero-proof" {
65 r.Fence = ReturnFence{}
66 }
67 if mode == "partial-return" {
68 lane := scope("moves/" + o.Host + "/" + lostRB)
69 r.ReturnLane = &lane
70 }
71 if mode == "stale" {
72 if _, e := Update(t.Context(), s, o, func(*Claim) error { return nil }); e != nil {
73 t.Fatal(e)
74 }
75 }
76 s.puts = 0
77 var got Observed
78 if err == nil {
79 transition := releaseFencedTarget(r)
80 if mode == "no-grant" {
81 transition = ReleaseFencedTarget(r)
82 }
83 got, err = Update(t.Context(), s, o, transition)
84 }
85 if mode != "ok" && mode != "partial-return" && mode != "parks" {
86 if err == nil {
87 t.Fatal("accepted", mode)
88 }
89 if mode == "stale" && !errors.Is(err, ErrConflict) {
90 t.Fatal(err)
91 }
92 return
93 }
94 if err != nil {
95 t.Fatal(err)
96 }
97 if s.puts != 1 || got.Claim.Op.Phase != PhaseReleased || got.Claim.Op.Exec != "none" || stringPresent(got.Claim.Op.Participants, r.Target) || len(got.Claim.Obligations) < 2 {
98 t.Fatal(got.Claim)
99 }
100 if mode == "parks" {
101 gotLoss := got.Claim.EvidenceRefs[len(got.Claim.EvidenceRefs)-1]
102 want := []ObligationRef{{ID: "existing-0", Op: o.Claim.Op.ID, Retirement: "existing-0"}}
103 if !reflect.DeepEqual(gotLoss.Reassigned, want) {
104 t.Fatalf("reassigned %+v", gotLoss.Reassigned)
105 }
106 for _, ob := range got.Claim.Obligations {
107 if ob.ID == "existing-1" && ob.Owner != "box:other" {
108 t.Fatal("stole another box's park")
109 }
110 }
111 }
112 clone, _ := cloneClaim(got.Claim)
113 if err := releaseFencedTarget(r)(clone); err != nil || !reflect.DeepEqual(clone, got.Claim) {
114 t.Fatal("replay", err)
115 }
116 if err := ReturnToSource(r.OpID, "bri-knitting", false, ReturnFence{})(clone); err != nil {
117 t.Fatal("source needs fence authority", err)
118 }
119 })
120 }
121 }
122}
123
124func TestPlan12FenceRefusesCleanCoverage(t *testing.T) {
125 c := lostTargetClaim(t, KindRestoring, PhaseProbing)
126 c.LossRefs = nil
127 delete(c.Op.Journal, "loss."+lostID)
128 c.Op.Exec = "ie-box"
129 c.Op.Journal["fence.uk-box"] = "verified-in-release"
130 h := c.Holder
131 in := Admission{ObservedOpID: c.Op.ID, ObservedPhase: c.Op.Phase, ObservedSession: h.Session, ObservedGeneration: h.Generation, Box: "ie-box", Mode: "cold", NewGeneration: "return", Region: "ie", PredecessorRegion: "uk", Coverage: "clean-return"}
132 if err := AdmitWriter(in)(c); !errors.Is(err, ErrFence) {
133 t.Fatal(err)
134 }
135}
136