feat: add uptime runner agent
This commit is contained in:
@@ -0,0 +1,92 @@
|
||||
import { UptimeRunnerApiClient } from './api-client.ts';
|
||||
import { CheckExecutor } from './check-executor.ts';
|
||||
import type {
|
||||
IRunOnceResult,
|
||||
IUptimeCheckResult,
|
||||
IUptimeRunnerConfig,
|
||||
TCheckJob,
|
||||
} from './interfaces.ts';
|
||||
import denoConfig from '../deno.json' with { type: 'json' };
|
||||
|
||||
export class UptimeRunner {
|
||||
private readonly apiClient: UptimeRunnerApiClient;
|
||||
private readonly checkExecutor: CheckExecutor;
|
||||
private running = false;
|
||||
|
||||
constructor(private readonly config: IUptimeRunnerConfig, apiClientArg?: UptimeRunnerApiClient) {
|
||||
this.apiClient = apiClientArg ?? new UptimeRunnerApiClient(config);
|
||||
this.checkExecutor = new CheckExecutor(config.runnerId);
|
||||
}
|
||||
|
||||
public async run(): Promise<void> {
|
||||
this.running = true;
|
||||
console.log(`uptimerunner ${this.config.runnerId} connected to ${this.config.instanceUrl}`);
|
||||
|
||||
while (this.running) {
|
||||
const started = Date.now();
|
||||
try {
|
||||
await this.heartbeat().catch((error) => {
|
||||
console.warn(
|
||||
`heartbeat failed: ${error instanceof Error ? error.message : String(error)}`,
|
||||
);
|
||||
});
|
||||
const result = await this.runOnce();
|
||||
if (result.results.length > 0) {
|
||||
console.log(`reported ${result.results.length} check result(s)`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(
|
||||
`runner iteration failed: ${error instanceof Error ? error.message : String(error)}`,
|
||||
);
|
||||
}
|
||||
|
||||
const elapsed = Date.now() - started;
|
||||
const delayMs = Math.max((this.config.pollIntervalMs ?? 30000) - elapsed, 1000);
|
||||
await delayFor(delayMs);
|
||||
}
|
||||
}
|
||||
|
||||
public stop(): void {
|
||||
this.running = false;
|
||||
}
|
||||
|
||||
public async runOnce(): Promise<IRunOnceResult> {
|
||||
const checks = await this.apiClient.fetchAssignedChecks(
|
||||
this.config.runnerId,
|
||||
this.config.labels ?? [],
|
||||
);
|
||||
const results = await this.executeChecks(checks);
|
||||
await this.apiClient.submitResults(this.config.runnerId, results);
|
||||
return { checks, results };
|
||||
}
|
||||
|
||||
private async heartbeat(): Promise<void> {
|
||||
await this.apiClient.heartbeat({
|
||||
runnerId: this.config.runnerId,
|
||||
labels: this.config.labels,
|
||||
version: denoConfig.version,
|
||||
});
|
||||
}
|
||||
|
||||
private async executeChecks(checksArg: TCheckJob[]): Promise<IUptimeCheckResult[]> {
|
||||
const maxConcurrentChecks = this.config.maxConcurrentChecks ?? 8;
|
||||
const results: IUptimeCheckResult[] = [];
|
||||
let nextIndex = 0;
|
||||
|
||||
const worker = async () => {
|
||||
while (nextIndex < checksArg.length) {
|
||||
const currentIndex = nextIndex++;
|
||||
results[currentIndex] = await this.checkExecutor.execute(checksArg[currentIndex]);
|
||||
}
|
||||
};
|
||||
|
||||
await Promise.all(
|
||||
Array.from({ length: Math.min(maxConcurrentChecks, checksArg.length) }, () => worker()),
|
||||
);
|
||||
return results;
|
||||
}
|
||||
}
|
||||
|
||||
async function delayFor(millisecondsArg: number): Promise<void> {
|
||||
await new Promise((resolve) => setTimeout(resolve, millisecondsArg));
|
||||
}
|
||||
Reference in New Issue
Block a user