mirror of
https://github.com/Studio-Saelix/sencho.git
synced 2026-08-11 19:26:56 +00:00
fix: keep running containers until stack pull/build succeeds (#1657)
* fix: keep running containers until stack pull/build succeeds Acquire images before reconcile, capture a recovery generation for compensation, and only remove classified orphans after handoff. * fix: address recovery audit blockers for safe stack updates Retire abandoned and expired recovery artifacts, probe compensated runtimes before reporting rollback success, preserve local Docker when deleting a node, validate the exact Compose invocation before capture, and repair updateStack return-contract fixtures. * fix: resolve ESLint errors blocking CI on this branch Unused-import and unused-variable errors left over from the stack deletion refactor: MeshService in stacks.ts (its opt-out cascade moved into DeployedStackDeletionService), a redundant pruneVolumes destructure in deleteDeployedStack (the real one is re-derived from the same input object inside runDeletionBody), and an unused beforeAll import in a Docker-integration test stub. Also scopes the webhook pull-action case body in a block to satisfy no-case-declarations; purely syntactic, no behavior change. * fix: harden recovery probe, cleanup retry, and failed-pull Docker test Reject absent or unhealthy expected replicas before reporting rollback success, keep cleanup records until artifacts are actually removed, fail closed when a mesh override cannot be generated, and assert a real failed pull leaves the original container running. * fix: verify recovery probe image identity and stack-scoped override paths Reject recovered runtimes that use the wrong image or leave scale-zero services running, and confine tombstone override deletion to the intent stack directory so forged cross-stack paths cannot be swept. * test: batch notification cap fixtures in a SQLite transaction Unbatched 1200-row inserts were timing out at the default 30s under CI load even though the same assertions pass in under 2s when green.
This commit is contained in:
@@ -19,6 +19,7 @@ vi.mock('@/components/ui/toast-store', () => ({
|
||||
}));
|
||||
|
||||
import { apiFetch } from '@/lib/api';
|
||||
import { toast } from '@/components/ui/toast-store';
|
||||
|
||||
type EditorState = ReturnType<typeof useEditorViewState>;
|
||||
type StackListState = ReturnType<typeof useStackListState>;
|
||||
@@ -314,6 +315,18 @@ describe('useStackActions policy-block dialog wiring', () => {
|
||||
expect(overlayState.setPolicyBlock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('toasts a deletion-in-progress message when the conflict action is delete', async () => {
|
||||
const inProgress = JSON.stringify({
|
||||
code: 'stack_op_in_progress',
|
||||
inProgress: { action: 'delete', startedAt: Date.now(), user: 'admin' },
|
||||
});
|
||||
vi.mocked(apiFetch).mockResolvedValueOnce(new Response(inProgress, { status: 409 }));
|
||||
const { result, overlayState } = setup();
|
||||
await act(async () => { await result.current.updateStack(mouseEvent); });
|
||||
expect(overlayState.setPolicyBlock).not.toHaveBeenCalled();
|
||||
expect(toast.error).toHaveBeenCalledWith(expect.stringMatching(/already deleting/i));
|
||||
});
|
||||
|
||||
it('opens the dialog with action "update" via the sidebar update entry point', async () => {
|
||||
vi.mocked(apiFetch).mockResolvedValueOnce(new Response(JSON.stringify(policyPayload), { status: 409 }));
|
||||
const { result, overlayState } = setup();
|
||||
|
||||
@@ -107,7 +107,7 @@ const parseFailureClassification = (value: unknown): FailureClassification | und
|
||||
return undefined;
|
||||
};
|
||||
|
||||
type StackOpAction = 'deploy' | 'down' | 'restart' | 'stop' | 'start' | 'update';
|
||||
type StackOpAction = 'deploy' | 'down' | 'restart' | 'stop' | 'start' | 'update' | 'delete';
|
||||
|
||||
interface StackOpInProgressInfo {
|
||||
action: StackOpAction;
|
||||
@@ -122,6 +122,7 @@ const STACK_OP_PRESENT_PARTICIPLE: Record<StackOpAction, string> = {
|
||||
stop: 'stopping',
|
||||
start: 'starting',
|
||||
update: 'updating',
|
||||
delete: 'deleting',
|
||||
};
|
||||
|
||||
const VALID_STACK_OP_ACTIONS: ReadonlySet<string> = new Set(
|
||||
|
||||
@@ -38,7 +38,7 @@ export function classifyOperationPhase(rows: ParsedLogRow[], action: ActionVerb)
|
||||
if (stage === 'BUILD') {
|
||||
return 'Building images';
|
||||
}
|
||||
if (message.includes('Backup created for atomic') || message.includes('Cleaning up existing containers')) {
|
||||
if (message.includes('Backup created for atomic') || message.includes('Validating stack for update') || message.includes('Capturing rollback generation')) {
|
||||
return 'Preparing';
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user