| 1 | package hostops |
| 2 | |
| 3 | import ( |
| 4 | "context" |
| 5 | "strings" |
| 6 | "testing" |
| 7 | |
| 8 | "github.com/carlosframework/platform/internal/hostclaim" |
| 9 | "github.com/carlosframework/platform/internal/park" |
| 10 | "github.com/carlosframework/platform/internal/provision" |
| 11 | ) |
| 12 | |
| 13 | // Home-region parks, part 1, after claim Plan 12: the recovery grant's |
| 14 | // release of a fenced target (ReleaseFencedTarget) for a move out of home. |
| 15 | // The record names the source home by its code, ""; the release hands the |
| 16 | // claim the source's engine region, park.HomeRegion, which the claim |
| 17 | // requires to be a region and not the target's. Every chore it queues |
| 18 | // names a region, and the lost target's parks are found under the |
| 19 | // target's engine region (LostMoveTargetParked). |
| 20 | func TestPlan12RecoveryFenceGrantFromHome(t *testing.T) { |
| 21 | w := newC7World(t, func(c *hostclaim.Claim, r *provision.Record, _ *AppAdmission) { |
| 22 | c.Op.Kind, c.Op.Phase, c.Op.Exec = hostclaim.KindLanding, hostclaim.PhaseProbing, "source" |
| 23 | c.Op.Intent["source"], c.Op.Intent["target"], c.Op.Intent["to"], c.Op.Intent["target_enrollment"] = "target", "source", "ie", strings.Repeat("a", 32) |
| 24 | c.Op.Participants = []string{"target", "source"} |
| 25 | c.Obligations = nil |
| 26 | c.Op.Journal = map[string]string{hostclaim.JournalSourceStamp: "1", hostclaim.JournalSrcGen: "src"} |
| 27 | r.Region = "" |
| 28 | r.Move.From, r.Move.To, r.Move.State = "", "ie", provision.MoveRollingBack |
| 29 | at := r.CreatedAt |
| 30 | r.Move.Rollback = &provision.MoveRollback{ID: "rb_abcdefghijklmnopqrstuvwxyz", Reason: provision.RollbackTargetFenced, Evidence: "control/box-fences/" + strings.Repeat("a", 32) + ".json", RequestedAt: &at, TargetPhase: provision.RbTargetLost, LossRef: "control/box-fences/" + strings.Repeat("a", 32) + ".json", LostAt: &at, SourcePhase: provision.RbSourceWaiting} |
| 31 | }) |
| 32 | authority := &plan12BumpFence{c7ReplayAuthority: w.authority} |
| 33 | act := func(fn func(context.Context, *MoveRecoveryGrant) error) error { |
| 34 | return w.control.RunRecovery(t.Context(), func(ctx context.Context) error { return runMoveRecovery(ctx, w.claim(t), fn, authority) }) |
| 35 | } |
| 36 | if err := act(func(ctx context.Context, g *MoveRecoveryGrant) error { |
| 37 | _, err := g.ReleaseFencedTarget(ctx, w.ref) |
| 38 | return err |
| 39 | }); err != nil { |
| 40 | t.Fatalf("the fenced target of a move out of home was not released: %v", err) |
| 41 | } |
| 42 | o := w.claim(t) |
| 43 | if o.Claim.Op.Phase != hostclaim.PhaseReleased || len(o.Claim.Obligations) == 0 { |
| 44 | t.Fatalf("the release: %+v", o.Claim) |
| 45 | } |
| 46 | for _, ob := range o.Claim.Obligations { |
| 47 | if ob.Region == "" && ob.Kind != "park-local" { |
| 48 | t.Fatalf("a chore in the unset region: %+v", ob) |
| 49 | } |
| 50 | if ob.Region == park.HomeRegion { |
| 51 | t.Fatalf("a target's chore in home: %+v", ob) |
| 52 | } |
| 53 | } |
| 54 | // The replay writes the record's half (the target released). |
| 55 | if err := act(func(ctx context.Context, g *MoveRecoveryGrant) error { |
| 56 | _, err := g.ReleaseFencedTarget(ctx, w.ref) |
| 57 | return err |
| 58 | }); err != nil { |
| 59 | t.Fatalf("the release's replay: %v", err) |
| 60 | } |
| 61 | if rec, err := provision.GetRecord(t.Context(), w.raw, "bab", "app", w.host); err != nil || rec.Move.Rollback.TargetPhase != provision.RbTargetReleased { |
| 62 | t.Fatalf("the record after the replay: %+v %v", rec, err) |
| 63 | } |
| 64 | o = w.claim(t) |
| 65 | if hostclaim.LostMoveTargetParked(o.Claim, park.PlacementRegion("ie")) { |
| 66 | t.Fatal("parked with the target's parks owed") |
| 67 | } |
| 68 | current := o |
| 69 | for _, ob := range o.Claim.Obligations { |
| 70 | var err error |
| 71 | if current, err = hostclaim.Update(t.Context(), w.raw, current, hostclaim.CompleteObligation(ob.Op, ob.ID)); err != nil { |
| 72 | t.Fatal(err) |
| 73 | } |
| 74 | } |
| 75 | if err := act(func(ctx context.Context, g *MoveRecoveryGrant) error { return g.StampLostMoveTargetParked(ctx) }); err != nil { |
| 76 | t.Fatalf("the lost target's parked stamp: %v", err) |
| 77 | } |
| 78 | if rec, err := provision.GetRecord(t.Context(), w.raw, "bab", "app", w.host); err != nil || rec.Move.Rollback.TargetParkedAt == nil { |
| 79 | t.Fatalf("not stamped: %+v %v", rec, err) |
| 80 | } |
| 81 | } |
| 82 | |