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
|
daemon is unavailable, the installer reports the Docker error and stops before
|
||||||
enrollment; inspect `systemctl status docker` and `journalctl -u docker`.
|
enrollment; inspect `systemctl status docker` and `journalctl -u docker`.
|
||||||
|
|
||||||
The controller reconciles automatic setup on agent heartbeats and operation
|
The controller reconciles automatic setup on agent heartbeats. Command polls only
|
||||||
polls. It first pulls `PLUTAINER_IMAGE` (default
|
deliver work already queued. Setup first pulls `PLUTAINER_IMAGE` (default
|
||||||
`ghcr.io/ayymoss/plutainer:latest`), then prepares the latest verified upload for
|
`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
|
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,
|
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 {
|
export function reconcileHostPreparation(hostId: string): HostPreparation {
|
||||||
return db().transaction(() => {
|
return db().transaction(() => {
|
||||||
const host = getHost(hostId);
|
const host = getHost(hostId);
|
||||||
@@ -203,5 +203,5 @@ export function reconcileHostPreparation(hostId: string): HostPreparation {
|
|||||||
scheduleAvailabilityRefresh();
|
scheduleAvailabilityRefresh();
|
||||||
}
|
}
|
||||||
return status;
|
return status;
|
||||||
}).immediate();
|
})();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,8 @@
|
|||||||
import { pendingHostOperationRows, waitForHostWork } from '$lib/server/hosts/index.js';
|
import { pendingHostOperationRows, waitForHostWork } from '$lib/server/hosts/index.js';
|
||||||
import { authenticateAgent } from '$lib/server/hosts/http.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';
|
import { hostOperationsResponse } from '$lib/server/hosts/delivery.js';
|
||||||
export async function GET({ request }: { request: Request }) {
|
export async function GET({ request }: { request: Request }) {
|
||||||
const host = authenticateAgent(request);
|
const host = authenticateAgent(request);
|
||||||
reconcileHostPreparation(host.id);
|
|
||||||
const params = new URL(request.url).searchParams;
|
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';
|
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);
|
let operations = pendingHostOperationRows(host.id, lane, false);
|
||||||
|
|||||||
@@ -183,6 +183,14 @@ describe('automatic host preparation', () => {
|
|||||||
});
|
});
|
||||||
expect(operations(host.id)).toEqual([]);
|
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();
|
const firstPoll = await (await pollOperations({ request: request('operations') })).json();
|
||||||
expect(firstPoll.operations).toHaveLength(1);
|
expect(firstPoll.operations).toHaveLength(1);
|
||||||
const runtimeOperation = firstPoll.operations[0];
|
const runtimeOperation = firstPoll.operations[0];
|
||||||
|
|||||||
Reference in New Issue
Block a user