package hostops import ( "context" "testing" "github.com/carlosframework/platform/internal/hostclaim" "github.com/carlosframework/platform/internal/moves" "github.com/carlosframework/platform/internal/park" "github.com/carlosframework/platform/internal/provision" ) func TestMoveRecoveryCommitUsesSharedSealValidation(t *testing.T) { for _, unknownField := range []bool{false, true} { t.Run(map[bool]string{false: "valid", true: "unknown-seal-field"}[unknownField], func(t *testing.T) { w := newLossWorld(t, "landing", func(c *hostclaim.Claim, r *provision.Record, _ *AppAdmission) { c.Op.Phase = hostclaim.PhaseLandingProbed r.Move.State = provision.MoveCommitted r.Move.CommittedAt = r.Move.FlippedAt if unknownField { raw := c.Op.Journal[hostclaim.JournalSeal] c.Op.Journal[hostclaim.JournalSeal] = `{"unknown":true,` + raw[1:] } }) before := w.claim(t) rec, tag := w.record(t) err := w.control.RunRecovery(context.Background(), func(ctx context.Context) error { return runMoveRecovery(ctx, before, func(ctx context.Context, g *MoveRecoveryGrant) error { return g.Claim(ctx, RecoveryMoveCommit) }) }) after := w.claim(t) if unknownField { if err == nil || after.ETag != before.ETag { t.Fatalf("unsupported seal changed claim: err=%v, before=%s, after=%s", err, before.ETag, after.ETag) } return } if err != nil || after.Claim.Op.Phase != hostclaim.PhaseCommitted { t.Fatalf("commit: %v, claim=%+v", err, after.Claim) } source, staging, err := moves.CommitScopes(before.Claim, rec.Move.FlippedAt) if err != nil { t.Fatal(err) } found := map[string]bool{} for _, chore := range after.Claim.Obligations { if chore.Kind == "park-replica" && chore.Park != nil { if *chore.Park == source || *chore.Park == staging { found[chore.Park.Location] = true } } } if !found[source.Location] || !found[staging.Location] { t.Fatalf("commit parks differ from shared scopes: %+v", after.Claim.Obligations) } if _, nextTag := w.record(t); nextTag != tag { t.Fatal("claim commit wrote the record") } }) } } // Home-region parks, part 1: the console's commit of a move out of home // queues the source's location park in park.HomeRegion, exactly as the // box's commit does (K9), and covers the home generation's admission. The // record names home by its code, "", which the claim would refuse. func TestMoveRecoveryCommitFromHome(t *testing.T) { w := newLossWorld(t, "landing", func(c *hostclaim.Claim, r *provision.Record, _ *AppAdmission) { c.Op.Phase = hostclaim.PhaseLandingProbed r.Move.State = provision.MoveCommitted r.Move.CommittedAt = r.Move.FlippedAt r.Move.From = "" for i := range c.Holder.Admissions { a := &c.Holder.Admissions[i] if a.Region == "ie" { a.Region = park.HomeRegion } if a.PredecessorRegion == "ie" { a.PredecessorRegion = park.HomeRegion } } }) before := w.claim(t) err := w.control.RunRecovery(context.Background(), func(ctx context.Context) error { return runMoveRecovery(ctx, before, func(ctx context.Context, g *MoveRecoveryGrant) error { return g.Claim(ctx, RecoveryMoveCommit) }) }) after := w.claim(t) if err != nil || after.Claim.Op.Phase != hostclaim.PhaseCommitted { t.Fatalf("commit: %v, claim=%+v", err, after.Claim.Op) } found := false for _, o := range after.Claim.Obligations { if o.Region == "" { t.Fatalf("a chore in the unset region: %+v", o) } if o.Kind == "park-replica" && o.Park != nil && o.Park.Location == "instances/"+lossHost { if o.Region != park.HomeRegion || o.Owner != "region:"+park.HomeRegion { t.Fatalf("the source's location park %+v, want it in %s", o, park.HomeRegion) } found = true } } if !found { t.Fatalf("no location park: %+v", after.Claim.Obligations) } for _, a := range after.Claim.Holder.Admissions { if a.PredecessorRegion == park.HomeRegion && !a.Covered { t.Fatalf("the home generation's admission is not covered: %+v", a) } } } // Home part 2, Task 3 (the inventory's witness for this Commit site): the // console's commit of a move into home, from clean claim state. The record // names the target by the home's code, ""; the start CAS wrote the op's to // as @home with home_region uk, the roster name of uk-box, whose admissions // keep their engine region uk. The commit queues the lane park in @home, // owned by region:@home, and the source's location park in ie; nothing is // queued under uk or "". func TestMoveRecoveryCommitIntoHome(t *testing.T) { w := newLossWorld(t, "landing", func(c *hostclaim.Claim, r *provision.Record, _ *AppAdmission) { c.Op.Phase = hostclaim.PhaseLandingProbed c.Op.Intent["to"], c.Op.Intent["home_region"] = park.HomeRegion, "uk" r.Move.State = provision.MoveCommitted r.Move.CommittedAt = r.Move.FlippedAt r.Move.To, r.Region = "", "" }) // The console names the same home (CONSOLE_HOME_REGION), as the // operation's binding requires. w.control.homeRegion = "uk" before := w.claim(t) if len(before.Claim.Obligations) != 0 { t.Fatalf("fixture: the claim already owes %+v", before.Claim.Obligations) } rec, _ := w.record(t) err := w.control.RunRecovery(context.Background(), func(ctx context.Context) error { return runMoveRecovery(ctx, before, func(ctx context.Context, g *MoveRecoveryGrant) error { return g.Claim(ctx, RecoveryMoveCommit) }) }) after := w.claim(t) if err != nil || after.Claim.Op.Phase != hostclaim.PhaseCommitted { t.Fatalf("commit: %v, claim=%+v", err, after.Claim.Op) } source, staging, err := moves.CommitScopes(before.Claim, rec.Move.FlippedAt) if err != nil { t.Fatal(err) } var lane, loc bool for _, o := range after.Claim.Obligations { if o.Region == "" || o.Region == "uk" { t.Fatalf("a chore named %q: %+v", o.Region, o) } switch { case o.Kind == "park-replica" && o.Park != nil && *o.Park == staging: if o.Region != park.HomeRegion || o.Owner != "region:"+park.HomeRegion { t.Fatalf("the lane park %+v, want it in %s", o, park.HomeRegion) } lane = true case o.Kind == "park-replica" && o.Park != nil && *o.Park == source: if o.Region != "ie" || o.Owner != "region:ie" { t.Fatalf("the source's location park %+v, want it in ie", o) } loc = true } } if !lane || !loc { t.Fatalf("the commit's parks: lane %v location %v in %+v", lane, loc, after.Claim.Obligations) } if after.Claim.Op.Intent["to"] != park.HomeRegion { t.Fatalf("the op's to after the commit: %+v", after.Claim.Op.Intent) } }