| index 4cbcc731d..56d460c49 100644 |
| --- a/internal/host/movespolicy_test.go |
| +++ b/internal/host/movespolicy_test.go |
| @@ -749,10 +749,11 @@ func rpRestoringRecord(phase string) *provision.Record { |
| } |
| |
| // rpRevertingRecord is the revert's record: reverting, the region still the |
| -// source's. |
| +// source's, with the inputs its probe recorded (MoveRecordRevertInputs). |
| func rpRevertingRecord() *provision.Record { |
| rec := mpRecord() |
| rec.Region, rec.Move.State, rec.Move.LandingPhase = "ie", provision.MoveReverting, "" |
| + rec.Move.RecoveryInputs = rpRecovery() |
| return rec |
| } |
| |
| @@ -908,6 +909,43 @@ func TestMovesClaimPolicyRevertingRows(t *testing.T) { |
| }) |
| } |
| |
| +// M11 (Q11): a revert's held render, held release and spawn are bound to the |
| +// inputs its probe recorded before rendering them (Move.RecoveryInputs, |
| +// MoveRecordRevertInputs at probing), as a rollback's are bound to |
| +// Rollback.RecoveryInputs: never to the move's pins, never by shape alone, |
| +// and nothing before the record. |
| +func TestMovesClaimPolicyRevertBindsRecordedInputs(t *testing.T) { |
| + recovered, pinned := rpRecovery(), mpPins() |
| + c := rpRevertingClaim(hostclaim.PhaseProbing) |
| + rec := rpRevertingRecord() |
| + moves := MovesClaimPolicy((&mpReader{rec: rec}).read) |
| + try := func(action string, id hostwrite.Identity) error { |
| + return moves(t.Context(), c, rpSelection(c, action), id) |
| + } |
| + cfgOf := func(in *provision.MoveInputs) hostwrite.Identity { |
| + return HeldConfigIdentity(in.BundleETags, in.OverlayETag) |
| + } |
| + for action, id := range map[string]hostwrite.Identity{"held-config": cfgOf(recovered), "held-release": {Release: recovered.Release}, "spawn": rpLaunch(recovered)} { |
| + if err := try(action, id); err != nil { |
| + t.Fatalf("%s with the recorded inputs: %v", action, err) |
| + } |
| + } |
| + rpRefused(t, try("held-config", cfgOf(pinned)), "the config is not the recovery's") |
| + rpRefused(t, try("held-release", hostwrite.Identity{Release: pinned.Release}), "the release is not the recovery's") |
| + rpRefused(t, try("spawn", rpLaunch(pinned)), "the launch is not the recovery's") |
| + rpRefused(t, try("spawn", hostwrite.Identity{Config: cfgOf(recovered).Config, Release: pinned.Release}), "the launch is not the recovery's") |
| + |
| + // Nothing recorded: the probe records before it renders, so every row |
| + // refuses; the move's pins never stand in. |
| + rec.Move.RecoveryInputs = nil |
| + for action, id := range map[string]hostwrite.Identity{"held-config": cfgOf(pinned), "held-release": {Release: pinned.Release}, "spawn": rpLaunch(pinned)} { |
| + rpRefused(t, try(action, id), "no recorded recovery inputs") |
| + } |
| + rec.Move.RecoveryInputs = &provision.MoveInputs{BundleETags: recovered.BundleETags, OverlayETag: recovered.OverlayETag} |
| + rpRefused(t, try("held-release", hostwrite.Identity{Release: recovered.Release}), "no recorded recovery release") |
| + rpRefused(t, try("spawn", rpLaunch(recovered)), "no recorded recovery release") |
| +} |
| + |
| // Only the claim op's executor, the move's source, in the source's region, |
| // for this rollback's record: the target (the cleaning's ReplaceTimers |
| // included, which is a plain registry call and never a policy row), a third |
| @@ -1171,26 +1209,25 @@ func TestMovesClaimPolicyBindsRecoveryInputs(t *testing.T) { |
| } |
| rpRefused(t, try(provision.RbSourceProbing, "held-config", rec, cfgOf(pinned)), "the config is not the recovery's") |
| }) |
| - t.Run("reverting: never the pins", func(t *testing.T) { |
| + t.Run("reverting: the recorded inputs, never the pins (M11)", func(t *testing.T) { |
| moves := MovesClaimPolicy((&mpReader{rec: rpRevertingRecord()}).read) |
| c := rpRevertingClaim(hostclaim.PhaseProbing) |
| - for _, in := range []*provision.MoveInputs{recovered, pinned} { |
| - if err := moves(t.Context(), c, rpSelection(c, "held-config"), cfgOf(in)); err != nil { |
| - t.Fatal(err) |
| - } |
| - if err := moves(t.Context(), c, rpSelection(c, "spawn"), rpLaunch(in)); err != nil { |
| - t.Fatal(err) |
| - } |
| + if err := moves(t.Context(), c, rpSelection(c, "held-config"), cfgOf(recovered)); err != nil { |
| + t.Fatal(err) |
| + } |
| + if err := moves(t.Context(), c, rpSelection(c, "spawn"), rpLaunch(recovered)); err != nil { |
| + t.Fatal(err) |
| } |
| + rpRefused(t, moves(t.Context(), c, rpSelection(c, "held-config"), cfgOf(pinned)), "the config is not the recovery's") |
| + rpRefused(t, moves(t.Context(), c, rpSelection(c, "spawn"), rpLaunch(pinned)), "the launch is not the recovery's") |
| for _, id := range []hostwrite.Identity{{}, {Config: cfgOf(recovered).Config}, {Release: "v1.3.0"}} { |
| rpRefused(t, moves(t.Context(), c, rpSelection(c, "spawn"), id), "the identity does not fit the action") |
| } |
| rpRefused(t, moves(t.Context(), c, rpSelection(c, "held-config"), rpLaunch(recovered)), "the identity does not fit the action") |
| - for _, in := range []*provision.MoveInputs{recovered, pinned} { |
| - if err := moves(t.Context(), c, rpSelection(c, "held-release"), hostwrite.Identity{Release: in.Release}); err != nil { |
| - t.Fatal(err) |
| - } |
| + if err := moves(t.Context(), c, rpSelection(c, "held-release"), hostwrite.Identity{Release: recovered.Release}); err != nil { |
| + t.Fatal(err) |
| } |
| + rpRefused(t, moves(t.Context(), c, rpSelection(c, "held-release"), hostwrite.Identity{Release: pinned.Release}), "the release is not the recovery's") |
| rpRefused(t, moves(t.Context(), c, rpSelection(c, "held-release"), rpLaunch(recovered)), "the identity does not fit the action") |
| }) |
| t.Run("a landing: the same difference is refused", func(t *testing.T) { |
| @@ -1259,7 +1296,7 @@ func TestMovesClaimPolicyRestoringThroughPolicyCheck(t *testing.T) { |
| // The release is materialized before every recovery probe (§7.7; Task 3b |
| // fix round 2), through hostbox.Policy.Check with each recovery's own hold: |
| // held-release at probing is granted to the executing source, bound to the |
| -// recorded recovery inputs on a return and by shape on a revert, and |
| +// recorded recovery inputs on a return and on a revert (M11), and |
| // refused at another phase, to another box, and for a release that is not |
| // the recorded one. |
| func TestMovesClaimPolicyRecoveryReleaseAtProbing(t *testing.T) { |
| @@ -1320,11 +1357,10 @@ func TestMovesClaimPolicyRecoveryReleaseAtProbing(t *testing.T) { |
| c := rpRevertingClaim(hostclaim.PhaseProbing) |
| s := rpSelection(c, "held-release") |
| rec := rpRevertingRecord() |
| - for _, v := range []string{recovered.Release, pinned.Release} { |
| - if err := through(mpOp, rec, c, s, hostwrite.Identity{Release: v}); err != nil { |
| - t.Fatalf("the executor at probing, release %s: %v", v, err) |
| - } |
| + if err := through(mpOp, rec, c, s, hostwrite.Identity{Release: recovered.Release}); err != nil { |
| + t.Fatalf("the executor at probing: %v", err) |
| } |
| + rpRefused(t, through(mpOp, rec, c, s, hostwrite.Identity{Release: pinned.Release}), "the release is not the recovery's") |
| rpRefused(t, through(mpOp, rec, c, s, hostwrite.Identity{Release: recovered.Release, Config: "x"}), "the identity does not fit the action") |
| for _, rev := range []string{hostclaim.RevPhaseEvidence, hostclaim.RevPhaseVerify, hostclaim.PhaseProbed, hostclaim.PhaseOpened} { |
| c := rpRevertingClaim(rev) |