mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-10-16 15:15:35 +02:00
Reconcile superseded Boat stop operations
Co-Authored-By: Paperclip <noreply@paperclip.ing> * commit 'ff26b3af9': Reconcile superseded Boat stop operations
This commit is contained in:
commit
38fee2e4f1
2 files changed
+50
-1
No files matched your search
@@ -191,6 +191,41 @@ describe("computer ownership", () => {
|
||||
(await f.repository.get(f.scope)).ledger.action?.providerStopId,
|
||||
).toBe("stop_1");
|
||||
});
|
||||
it("replaces a superseded stop while preserving the admission fence", async () => {
|
||||
const f = fixture();
|
||||
await f.attach();
|
||||
await f.service.reconcile();
|
||||
vi.mocked(f.backend.stopStatus).mockResolvedValueOnce({ id: "stop_1", status: "superseded" });
|
||||
vi.mocked(f.backend.stop).mockResolvedValueOnce({ id: "stop_2", status: "pending" });
|
||||
await f.service.reconcile();
|
||||
expect((await f.repository.get(f.scope)).ledger.action?.providerStopId).toBe("stop_2");
|
||||
await expect(f.admit()).rejects.toMatchObject({ code: "conflict" });
|
||||
vi.mocked(f.backend.stopStatus).mockResolvedValueOnce({ id: "stop_2", status: "completed" });
|
||||
await f.service.reconcile();
|
||||
expect(f.backend.stopStatus).toHaveBeenLastCalledWith(expect.anything(), "stop_2");
|
||||
await expect(f.admit()).resolves.toHaveProperty("owner");
|
||||
});
|
||||
it("adopts a newer pending stop without issuing a duplicate operation", async () => {
|
||||
const f = fixture();
|
||||
await f.attach();
|
||||
await f.service.reconcile();
|
||||
vi.mocked(f.backend.stopStatus).mockResolvedValueOnce({ id: "stop_1", status: "superseded" });
|
||||
vi.mocked(f.backend.inspect).mockResolvedValueOnce({ state: "ready", snapshots: true, stop: { id: "stop_2", status: "failing" } });
|
||||
await f.service.reconcile();
|
||||
expect((await f.repository.get(f.scope)).ledger.action?.providerStopId).toBe("stop_2");
|
||||
expect(f.backend.stop).toHaveBeenCalledOnce();
|
||||
await expect(f.admit()).rejects.toMatchObject({ code: "conflict" });
|
||||
});
|
||||
it("settles a superseded stop only after physical archival proof", async () => {
|
||||
const f = fixture();
|
||||
await f.attach();
|
||||
await f.service.detach(f.scope);
|
||||
vi.mocked(f.backend.stopStatus).mockResolvedValueOnce({ id: "stop_1", status: "superseded" });
|
||||
vi.mocked(f.backend.inspect).mockResolvedValueOnce({ state: "archived", snapshots: true, stop: null });
|
||||
await f.service.reconcile();
|
||||
expect((await f.repository.get(f.scope)).ledger).toMatchObject({ status: "detached", action: null });
|
||||
expect(f.backend.stop).toHaveBeenCalledOnce();
|
||||
});
|
||||
it("viewer renewals cannot extend the absolute warm deadline or use another user", async () => {
|
||||
const f = fixture();
|
||||
await f.attach();
|
||||
|
||||
@@ -885,9 +885,23 @@ finally:
|
||||
if (record.ledger.action) {
|
||||
const action = record.ledger.action;
|
||||
// A lost stop response is retried only while admission is fenced. Boat returns its same pending ID.
|
||||
const stop = action.providerStopId
|
||||
let stop = action.providerStopId
|
||||
? await backend.stopStatus(record, action.providerStopId)
|
||||
: await backend.stop(record);
|
||||
if (stop.status === "superseded") {
|
||||
// A provider-side resume can supersede the recorded stop. Keep admission
|
||||
// fenced until current physical state proves stopped or a replacement
|
||||
// normal snapshot operation completes; never force or delete the host.
|
||||
const current = await backend.inspect(record);
|
||||
if (["stopped", "archived"].includes(current.state)) {
|
||||
stop = { id: stop.id, status: "completed" };
|
||||
} else if (current.stop && current.stop.id !== stop.id &&
|
||||
["pending", "failing"].includes(current.stop.status)) {
|
||||
stop = current.stop;
|
||||
} else {
|
||||
stop = await backend.stop(record);
|
||||
}
|
||||
}
|
||||
await repository.update(record, (current) => {
|
||||
if (current.ledger.action?.id !== action.id) return;
|
||||
current.ledger.action.providerStopId = stop.id;
|
||||
|
||||
Reference in new issue
Block a user