package hostops import ( "context" "strings" "testing" "github.com/carlosframework/platform/internal/hostclaim" "github.com/carlosframework/platform/internal/park" "github.com/carlosframework/platform/internal/provision" ) // Home-region parks, part 1, after claim Plan 12: the recovery grant's // release of a fenced target (ReleaseFencedTarget) for a move out of home. // The record names the source home by its code, ""; the release hands the // claim the source's engine region, park.HomeRegion, which the claim // requires to be a region and not the target's. Every chore it queues // names a region, and the lost target's parks are found under the // target's engine region (LostMoveTargetParked). func TestPlan12RecoveryFenceGrantFromHome(t *testing.T) { w := newC7World(t, func(c *hostclaim.Claim, r *provision.Record, _ *AppAdmission) { c.Op.Kind, c.Op.Phase, c.Op.Exec = hostclaim.KindLanding, hostclaim.PhaseProbing, "source" c.Op.Intent["source"], c.Op.Intent["target"], c.Op.Intent["to"], c.Op.Intent["target_enrollment"] = "target", "source", "ie", strings.Repeat("a", 32) c.Op.Participants = []string{"target", "source"} c.Obligations = nil c.Op.Journal = map[string]string{hostclaim.JournalSourceStamp: "1", hostclaim.JournalSrcGen: "src"} r.Region = "" r.Move.From, r.Move.To, r.Move.State = "", "ie", provision.MoveRollingBack at := r.CreatedAt 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} }) authority := &plan12BumpFence{c7ReplayAuthority: w.authority} act := func(fn func(context.Context, *MoveRecoveryGrant) error) error { return w.control.RunRecovery(t.Context(), func(ctx context.Context) error { return runMoveRecovery(ctx, w.claim(t), fn, authority) }) } if err := act(func(ctx context.Context, g *MoveRecoveryGrant) error { _, err := g.ReleaseFencedTarget(ctx, w.ref) return err }); err != nil { t.Fatalf("the fenced target of a move out of home was not released: %v", err) } o := w.claim(t) if o.Claim.Op.Phase != hostclaim.PhaseReleased || len(o.Claim.Obligations) == 0 { t.Fatalf("the release: %+v", o.Claim) } for _, ob := range o.Claim.Obligations { if ob.Region == "" && ob.Kind != "park-local" { t.Fatalf("a chore in the unset region: %+v", ob) } if ob.Region == park.HomeRegion { t.Fatalf("a target's chore in home: %+v", ob) } } // The replay writes the record's half (the target released). if err := act(func(ctx context.Context, g *MoveRecoveryGrant) error { _, err := g.ReleaseFencedTarget(ctx, w.ref) return err }); err != nil { t.Fatalf("the release's replay: %v", err) } if rec, err := provision.GetRecord(t.Context(), w.raw, "bab", "app", w.host); err != nil || rec.Move.Rollback.TargetPhase != provision.RbTargetReleased { t.Fatalf("the record after the replay: %+v %v", rec, err) } o = w.claim(t) if hostclaim.LostMoveTargetParked(o.Claim, park.PlacementRegion("ie")) { t.Fatal("parked with the target's parks owed") } current := o for _, ob := range o.Claim.Obligations { var err error if current, err = hostclaim.Update(t.Context(), w.raw, current, hostclaim.CompleteObligation(ob.Op, ob.ID)); err != nil { t.Fatal(err) } } if err := act(func(ctx context.Context, g *MoveRecoveryGrant) error { return g.StampLostMoveTargetParked(ctx) }); err != nil { t.Fatalf("the lost target's parked stamp: %v", err) } if rec, err := provision.GetRecord(t.Context(), w.raw, "bab", "app", w.host); err != nil || rec.Move.Rollback.TargetParkedAt == nil { t.Fatalf("not stamped: %+v %v", rec, err) } }