| 1 | package hostops |
| 2 | |
| 3 | import ( |
| 4 | "encoding/json" |
| 5 | "github.com/carlosframework/platform/internal/hostclaim" |
| 6 | "github.com/carlosframework/platform/internal/park" |
| 7 | "github.com/carlosframework/platform/internal/provision" |
| 8 | "testing" |
| 9 | ) |
| 10 | |
| 11 | func TestPlan13LossSourceRegionIsEngineName(t *testing.T) { |
| 12 | for _, tc := range []struct{ from, to, want string }{ |
| 13 | {"", "uk", park.HomeRegion}, {"ie", "uk", "ie"}, {"uk", "", "uk"}, |
| 14 | } { |
| 15 | t.Run(tc.from+"-"+tc.to, func(t *testing.T) { |
| 16 | in := moveLossIntent{Move: lossOp, Incarnation: "inc", Target: "target", TargetRegion: park.PlacementRegion(tc.to)} |
| 17 | rec := provision.Record{Region: tc.to, Move: &provision.MoveState{ID: lossOp, Incarnation: "inc", From: tc.from, To: tc.to, State: provision.MoveLanding}} |
| 18 | op := hostclaim.Op{ID: lossOp, Kind: hostclaim.KindLanding} |
| 19 | if err := recordLossIntent(&in, rec, "etag", 1, op, "claim-etag", nil); err != nil { |
| 20 | t.Fatal(err) |
| 21 | } |
| 22 | if in.SourceRegion != tc.want || rec.Move.From != tc.from || rec.Region != tc.to { |
| 23 | t.Fatalf("%+v %+v", in, rec) |
| 24 | } |
| 25 | raw, err := json.Marshal(in) |
| 26 | if err != nil { |
| 27 | t.Fatal(err) |
| 28 | } |
| 29 | var persisted map[string]any |
| 30 | if err = json.Unmarshal(raw, &persisted); err != nil { |
| 31 | t.Fatal(err) |
| 32 | } |
| 33 | if persisted["source_region"] != tc.want { |
| 34 | t.Fatal(persisted) |
| 35 | } |
| 36 | }) |
| 37 | } |
| 38 | } |
| 39 | func TestPlan13HomeLossDeclarationKeepsPlacement(t *testing.T) { |
| 40 | w := newLossWorld(t, "landing", func(c *hostclaim.Claim, r *provision.Record, _ *AppAdmission) { |
| 41 | r.Move.From = "" |
| 42 | c.Op.Intent["home_region"] = "ie" |
| 43 | }) |
| 44 | w.control.homeRegion = "ie" |
| 45 | inspect := w.inspect(t, "uk-label") |
| 46 | after, err := w.declare(w.submission(inspect)) |
| 47 | if err != nil { |
| 48 | t.Fatal(err) |
| 49 | } |
| 50 | rec, _ := w.record(t) |
| 51 | if rec.Region != "" || rec.Move.From != "" { |
| 52 | t.Fatalf("record leaked engine token: %+v", rec) |
| 53 | } |
| 54 | loss, err := hostclaim.ReadLossRecord(t.Context(), w.raw, w.pub, inspect.Details.ID) |
| 55 | if err != nil { |
| 56 | t.Fatal(err) |
| 57 | } |
| 58 | in, _, err := lossIntentOf(loss.Record) |
| 59 | if err != nil { |
| 60 | t.Fatal(err) |
| 61 | } |
| 62 | if in.SourceRegion != park.HomeRegion || after.Claim.Op.Kind != hostclaim.KindRestoring { |
| 63 | t.Fatalf("%+v", in) |
| 64 | } |
| 65 | } |
| 66 | |
| 67 | func TestPlan13LossRecoveryNamedHomeAdmission(t *testing.T) { |
| 68 | for _, home := range []string{"ie", "", "uk"} { |
| 69 | in := moveLossIntent{Kind: moveLossIntentKind, Move: "move", Incarnation: "inc", Rollback: "rb_abcdefghijklmnopqrstuvwxyz", Target: "target", Source: "source", TargetRegion: "uk", SourceRegion: park.HomeRegion, RecordKey: "record", RecordETag: "etag", RecordState: moveLossRecordLanding} |
| 70 | raw, _ := in.encode() |
| 71 | loss := hostclaim.LossRecord{ID: "loss_1", OpID: "move", Box: "target", HolderIncarnation: "inc", ConfirmedConsequences: hostclaim.LossConsequences{RecoveryIntent: raw}} |
| 72 | c := hostclaim.Claim{Holder: &hostclaim.Holder{Incarnation: "inc", Key: "record", Stamp: 7}, Op: hostclaim.Op{Kind: hostclaim.KindRestoring, ID: "move", Phase: hostclaim.PhaseProbing, Exec: "source", Intent: map[string]string{"source": "source", "target": "target", "to": "uk", "rollback": in.Rollback, "home_region": home}, Journal: map[string]string{hostclaim.JournalSourceStamp: "7", "lost_target_released": "loss_1"}}} |
| 73 | err := MoveLossRecoveryOwner().ValidateLossRecovery(c, loss, hostclaim.Admission{Box: "source", Region: "ie", Mode: "cold"}) |
| 74 | if (err == nil) != (home == "ie") { |
| 75 | t.Fatal(home, err) |
| 76 | } |
| 77 | } |
| 78 | } |
| 79 | |