Reconcile host preparation on heartbeats instead of polls
This commit is contained in:
@@ -52,8 +52,8 @@ An already available Docker installation (including Docker CE) is reused. If the
|
||||
daemon is unavailable, the installer reports the Docker error and stops before
|
||||
enrollment; inspect `systemctl status docker` and `journalctl -u docker`.
|
||||
|
||||
The controller reconciles automatic setup on agent heartbeats and operation
|
||||
polls. It first pulls `PLUTAINER_IMAGE` (default
|
||||
The controller reconciles automatic setup on agent heartbeats. Command polls only
|
||||
deliver work already queued. Setup first pulls `PLUTAINER_IMAGE` (default
|
||||
`ghcr.io/ayymoss/plutainer:latest`), then prepares the latest verified upload for
|
||||
T5 and for T6 when available, one setup operation at a time. Uploading new files
|
||||
or enrolling another host needs no manual propagation. Setup waits for an active, connected,
|
||||
|
||||
@@ -173,7 +173,7 @@ function supersedeUndeliveredDownloads(hostId: string): void {
|
||||
}
|
||||
}
|
||||
|
||||
/** Agent heartbeats and polls drive durable preparation; no open UI or in-memory worker is needed. */
|
||||
/** Heartbeats drive durable preparation; idle checks do not reserve the SQLite writer. */
|
||||
export function reconcileHostPreparation(hostId: string): HostPreparation {
|
||||
return db().transaction(() => {
|
||||
const host = getHost(hostId);
|
||||
@@ -203,5 +203,5 @@ export function reconcileHostPreparation(hostId: string): HostPreparation {
|
||||
scheduleAvailabilityRefresh();
|
||||
}
|
||||
return status;
|
||||
}).immediate();
|
||||
})();
|
||||
}
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
import { pendingHostOperationRows, waitForHostWork } from '$lib/server/hosts/index.js';
|
||||
import { authenticateAgent } from '$lib/server/hosts/http.js';
|
||||
import { reconcileHostPreparation } from '$lib/server/hosts/preparation.js';
|
||||
import { hostOperationsResponse } from '$lib/server/hosts/delivery.js';
|
||||
export async function GET({ request }: { request: Request }) {
|
||||
const host = authenticateAgent(request);
|
||||
reconcileHostPreparation(host.id);
|
||||
const params = new URL(request.url).searchParams;
|
||||
const lane = params.get('lane') === 'health' ? 'health' : params.get('lane') === 'read' ? 'read' : params.get('lane') === 'write' ? 'write' : 'all';
|
||||
let operations = pendingHostOperationRows(host.id, lane, false);
|
||||
|
||||
@@ -183,6 +183,14 @@ describe('automatic host preparation', () => {
|
||||
});
|
||||
expect(operations(host.id)).toEqual([]);
|
||||
|
||||
// Poll lanes only deliver queued work. One inventory heartbeat drives setup.
|
||||
const reconcile = vi.spyOn(preparation, 'reconcileHostPreparation');
|
||||
expect((await (await pollOperations({ request: request('operations') })).json()).operations).toEqual([]);
|
||||
expect(reconcile).not.toHaveBeenCalled();
|
||||
expect((await reportHeartbeat({ request: request('heartbeat', { inventory }) })).status).toBe(200);
|
||||
expect(reconcile).toHaveBeenCalledOnce();
|
||||
reconcile.mockRestore();
|
||||
|
||||
const firstPoll = await (await pollOperations({ request: request('operations') })).json();
|
||||
expect(firstPoll.operations).toHaveLength(1);
|
||||
const runtimeOperation = firstPoll.operations[0];
|
||||
|
||||
Reference in New Issue
Block a user