| index 82963fc45..d4efb3f95 100644 |
| --- a/internal/release/adopt_test.go |
| +++ b/internal/release/adopt_test.go |
| @@ -3880,6 +3880,75 @@ func TestRefusedPromotionIsNeitherStagedNorRelayed(t *testing.T) { |
| } |
| } |
| |
| +// TestChannelBackOnTheRestoredBuildClearsTheRefusal (final review F8): v2 |
| +// was refused and rolled back to v1, then the channel was promoted back to |
| +// v1. The pass finds the box up to date, restarts nothing, and removes the |
| +// v2 record that no longer blocks anything. |
| +func TestChannelBackOnTheRestoredBuildClearsTheRefusal(t *testing.T) { |
| + host := "console.example.com" |
| + s := NewDirStore(t.TempDir()) |
| + ctx := context.Background() |
| + priv, pub := testKeys(t) |
| + opt := t.TempDir() |
| + reg := testRegistry(t) |
| + shipRelease(t, s, "bab", "console", "v1", adoptPlatform, []byte("good build")) |
| + shipRelease(t, s, "bab", "console", "v2", adoptPlatform, []byte("bad build")) |
| + if err := StageArtifact(opt, "bab", "v1", "console", []byte("good build")); err != nil { |
| + t.Fatal(err) |
| + } |
| + addRoute(t, reg, host, "console", "canary/x", false) |
| + if err := PointLive(opt, host, "bab", "v1", "console"); err != nil { |
| + t.Fatal(err) |
| + } |
| + if err := Promote(ctx, s, priv, "bab", "console", "canary/x", "v1"); err != nil { |
| + t.Fatal(err) |
| + } |
| + bake := t.TempDir() |
| + if err := WriteRefusal(bake, Refusal{Host: host, App: "console", Version: "v2", |
| + PointerPromotedAt: time.Unix(1, 0).UTC(), Reason: "exited: status=2", RestoredVersion: "v1", Recovered: true}); err != nil { |
| + t.Fatal(err) |
| + } |
| + restart := func(h string) error { t.Fatalf("restart consulted for up-to-date %s", h); return nil } |
| + if err := Pass(ctx, reg, s, pub, opt, restart, BakeDir(bake)); err != nil { |
| + t.Fatalf("Pass: %v", err) |
| + } |
| + if v, _ := LiveVersion(opt, host); v != "v1" { |
| + t.Fatalf("live = %q, want v1", v) |
| + } |
| + if r, _ := ReadRefusal(bake, host); r != nil { |
| + t.Fatalf("stale v2 refusal survived the channel's return to v1: %+v", r) |
| + } |
| +} |
| + |
| +// TestUpToDateKeepsARefusalThatStillBlocks: the link is on the refused build |
| +// only when its rollback failed; that record still names the pointer and |
| +// stays for the watcher. |
| +func TestUpToDateKeepsARefusalThatStillBlocks(t *testing.T) { |
| + host := "console.example.com" |
| + ctx, reg, s, pub, opt := startUnitFixture(t, host) |
| + if err := StageArtifact(opt, "bab", "new", "console", []byte("bad build")); err != nil { |
| + t.Fatal(err) |
| + } |
| + if err := PointLive(opt, host, "bab", "new", "console"); err != nil { |
| + t.Fatal(err) |
| + } |
| + ptr, err := Channel(ctx, s, pub, "bab", "console", "canary/x") |
| + if err != nil { |
| + t.Fatal(err) |
| + } |
| + bake := t.TempDir() |
| + if err := WriteRefusal(bake, Refusal{Host: host, App: "console", Version: ptr.Version, |
| + PointerPromotedAt: ptr.PromotedAt, Reason: "exited: status=2", RecoveryError: "could not roll the symlink back"}); err != nil { |
| + t.Fatal(err) |
| + } |
| + if err := Pass(ctx, reg, s, pub, opt, func(string) error { return nil }, BakeDir(bake)); err != nil { |
| + t.Fatalf("Pass: %v", err) |
| + } |
| + if r, _ := ReadRefusal(bake, host); r == nil { |
| + t.Fatal("a refusal that still blocks the pointer was removed") |
| + } |
| +} |
| + |
| // TestRePromotionClearsTheBlock: a pointer re-signed for the same version |
| // (what promote --retry does) is adopted, and the stale record is removed. |
| func TestRePromotionClearsTheBlock(t *testing.T) { |