feat: background remote agent updates survive SAC navigation (0.20.19)
This commit is contained in:
@@ -0,0 +1,120 @@
|
||||
import { reactive } from "vue";
|
||||
import {
|
||||
ApiError,
|
||||
fetchHost,
|
||||
fetchHostRemoteJob,
|
||||
runHostAgentUpdateFallback,
|
||||
updateHostAgentViaSsh,
|
||||
type HostDetail,
|
||||
type HostRemoteActionJobStatus,
|
||||
} from "../api";
|
||||
import { emitHostPatch } from "../utils/hostPatchBus";
|
||||
|
||||
export type HostRemoteActionKind = "fallback" | "ssh-update";
|
||||
|
||||
export const hostRemoteActionLog = reactive({
|
||||
open: false,
|
||||
title: "",
|
||||
loading: false,
|
||||
ok: null as boolean | null,
|
||||
message: "",
|
||||
output: "",
|
||||
hostId: 0,
|
||||
});
|
||||
|
||||
const POLL_MS = 1500;
|
||||
|
||||
function sleep(ms: number): Promise<void> {
|
||||
return new Promise((resolve) => setTimeout(resolve, ms));
|
||||
}
|
||||
|
||||
function applyJobToModal(job: HostRemoteActionJobStatus) {
|
||||
hostRemoteActionLog.title = job.title || hostRemoteActionLog.title;
|
||||
hostRemoteActionLog.message = job.message || hostRemoteActionLog.message;
|
||||
hostRemoteActionLog.output = job.output || job.stdout || "";
|
||||
}
|
||||
|
||||
function finishModal(job: HostRemoteActionJobStatus) {
|
||||
hostRemoteActionLog.loading = false;
|
||||
hostRemoteActionLog.ok = job.ok ?? job.status === "success";
|
||||
applyJobToModal(job);
|
||||
}
|
||||
|
||||
async function pollRemoteJob(hostId: number): Promise<HostRemoteActionJobStatus> {
|
||||
for (;;) {
|
||||
const job = await fetchHostRemoteJob(hostId);
|
||||
applyJobToModal(job);
|
||||
if (!job.active && job.status !== "running") {
|
||||
return job;
|
||||
}
|
||||
await sleep(POLL_MS);
|
||||
}
|
||||
}
|
||||
|
||||
async function patchHostFromJob(hostId: number, job: HostRemoteActionJobStatus) {
|
||||
try {
|
||||
const detail: HostDetail = await fetchHost(hostId);
|
||||
if (job.product_version) {
|
||||
detail.product_version = job.product_version;
|
||||
}
|
||||
if (job.agent_update_state) {
|
||||
detail.agent_update_state = job.agent_update_state;
|
||||
}
|
||||
if (job.ok === false && job.message) {
|
||||
detail.agent_update_last_error = job.message;
|
||||
}
|
||||
emitHostPatch(detail);
|
||||
} catch {
|
||||
/* list refresh on next open is enough */
|
||||
}
|
||||
}
|
||||
|
||||
async function startRemoteAction(hostId: number, kind: HostRemoteActionKind) {
|
||||
if (kind === "fallback") {
|
||||
await runHostAgentUpdateFallback(hostId);
|
||||
return;
|
||||
}
|
||||
await updateHostAgentViaSsh(hostId);
|
||||
}
|
||||
|
||||
export async function runHostRemoteAction(
|
||||
hostId: number,
|
||||
title: string,
|
||||
kind: HostRemoteActionKind,
|
||||
): Promise<HostRemoteActionJobStatus | null> {
|
||||
hostRemoteActionLog.open = true;
|
||||
hostRemoteActionLog.loading = true;
|
||||
hostRemoteActionLog.ok = null;
|
||||
hostRemoteActionLog.title = title;
|
||||
hostRemoteActionLog.message = "Запуск на сервере SAC…";
|
||||
hostRemoteActionLog.output = "";
|
||||
hostRemoteActionLog.hostId = hostId;
|
||||
|
||||
try {
|
||||
try {
|
||||
await startRemoteAction(hostId, kind);
|
||||
} catch (e) {
|
||||
if (e instanceof ApiError && e.status === 409) {
|
||||
/* already running — poll existing job */
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
hostRemoteActionLog.message =
|
||||
"Выполняется на удалённом хосте… Можно перейти в другой раздел SAC.";
|
||||
const job = await pollRemoteJob(hostId);
|
||||
finishModal(job);
|
||||
await patchHostFromJob(hostId, job);
|
||||
return job;
|
||||
} catch (e) {
|
||||
hostRemoteActionLog.loading = false;
|
||||
hostRemoteActionLog.ok = false;
|
||||
hostRemoteActionLog.message = e instanceof Error ? e.message : "Ошибка выполнения";
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export function closeHostRemoteActionLog() {
|
||||
if (hostRemoteActionLog.loading) return;
|
||||
hostRemoteActionLog.open = false;
|
||||
}
|
||||
Reference in New Issue
Block a user