Skip to content

Commit

Permalink
add script to ping monitors
Browse files Browse the repository at this point in the history
  • Loading branch information
BenoitSerrano committed Dec 6, 2024
1 parent f8df389 commit 2264516
Show file tree
Hide file tree
Showing 12 changed files with 190 additions and 3 deletions.
4 changes: 4 additions & 0 deletions cron.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@
{
"command": "*/10 * * * * npm run script:checkSystemPulses",
"size": "S"
},
{
"command": "*/10 * * * * npm run script:pingMonitors",
"size": "S"
}
]
}
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@
"test:back": "jest",
"typeorm": "typeorm-ts-node-esm",
"script:importDb": "npm run buildServer && node dist/src/scripts/importDb.js",
"script:checkSystemPulses": "npm run buildServer && node dist/src/scripts/checkSystemPulses.js"
"script:checkSystemPulses": "npm run buildServer && node dist/src/scripts/checkSystemPulses.js",
"script:pingMonitors": "npm run buildServer && node dist/src/scripts/pingMonitors.js"
},
"author": "",
"license": "ISC",
Expand Down
3 changes: 2 additions & 1 deletion src/dataSource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { config } from './config';
import { SystemPulse } from './modules/systemPulse';
import { Event } from './modules/event';
import { Monitor } from './modules/monitor';
import { MonitorEvent } from './modules/monitorEvent';

const dataSource = new DataSource({
type: 'postgres',
Expand All @@ -14,7 +15,7 @@ const dataSource = new DataSource({
database: config.DATABASE_NAME,
logging: ['warn', 'error'],
connectTimeoutMS: 20000,
entities: [SystemPulse, Event, Monitor],
entities: [SystemPulse, Event, Monitor, MonitorEvent],
subscribers: [],
migrations: ['**/migrations/*.js'],
});
Expand Down
14 changes: 14 additions & 0 deletions src/migrations/1733506046286-add-last-call.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { MigrationInterface, QueryRunner } from "typeorm";

export class AddLastCall1733506046286 implements MigrationInterface {
name = 'AddLastCall1733506046286'

public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "monitor" ADD "lastCall" TIMESTAMP`);
}

public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "monitor" DROP COLUMN "lastCall"`);
}

}
16 changes: 16 additions & 0 deletions src/modules/genericEvent/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
interface genericEvent {
id: number;

title: string;

kind: eventKindType;

createdAt: string;
}

const EVENT_KINDS = ['up', 'down'] as const;

type eventKindType = (typeof EVENT_KINDS)[number];

export { EVENT_KINDS };
export type { eventKindType, genericEvent };
3 changes: 3 additions & 0 deletions src/modules/monitor/Monitor.entity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,7 @@ export class Monitor {

@Column({ type: 'timestamp', nullable: true })
lastSuccessfulCall: string | null;

@Column({ type: 'timestamp', nullable: true })
lastCall: string | null;
}
3 changes: 2 additions & 1 deletion src/modules/monitor/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Monitor } from './Monitor.entity';
import { buildMonitorController } from './monitor.controller';
import { buildMonitorService } from './monitor.service';

export { Monitor, buildMonitorController };
export { Monitor, buildMonitorController, buildMonitorService };
61 changes: 61 additions & 0 deletions src/modules/monitor/monitor.service.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { dataSource } from '../../dataSource';
import { buildMonitorEventService } from '../monitorEvent';
import { Monitor } from './Monitor.entity';

export { buildMonitorService };
Expand All @@ -7,6 +8,9 @@ function buildMonitorService() {
const monitorRepository = dataSource.getRepository(Monitor);
const monitorService = {
createMonitor,
getMonitors,
computeShouldPingMonitor,
pingMonitor,
};

return monitorService;
Expand All @@ -23,4 +27,61 @@ function buildMonitorService() {
});
return { monitorId: result.identifiers[0].id };
}

async function getMonitors() {
return monitorRepository.find({});
}

async function pingMonitor(monitor: Monitor) {
const monitorEventService = buildMonitorEventService();
await monitorRepository.update({ id: monitor.id }, { lastCall: () => 'CURRENT_TIMESTAMP' });
const lastMonitorEvent = await monitorEventService.getLastMonitorEvent(monitor.id);

try {
await fetch(monitor.url);
await monitorRepository.update(
{ id: monitor.id },
{ lastSuccessfulCall: () => 'CURRENT_TIMESTAMP' },
);
if (!lastMonitorEvent) {
return monitorEventService.createMonitorEvent(monitor.id, {
title: 'Service en route !',
kind: 'up',
});
} else {
if (lastMonitorEvent.kind === 'down') {
await monitorEventService.createMonitorEvent(monitor.id, {
title: 'Le service est revenu',
kind: 'up',
});
}
}
} catch (error) {
console.error(error);
if (!lastMonitorEvent) {
return monitorEventService.createMonitorEvent(monitor.id, {
title: 'Service en panne...',
kind: 'down',
});
} else {
if (lastMonitorEvent.kind === 'up') {
await monitorEventService.createMonitorEvent(monitor.id, {
title: 'Le service est tombé !',
kind: 'down',
});
}
}
}
}

function computeShouldPingMonitor(monitor: Monitor, now: Date) {
if (monitor.lastCall === null) {
return true;
}
const lastCallDate = new Date(monitor.lastCall);
if (now.getTime() - lastCallDate.getTime() > monitor.frequency * 60 * 1000) {
return true;
}
return false;
}
}
21 changes: 21 additions & 0 deletions src/modules/monitorEvent/MonitorEvent.entity.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { Column, CreateDateColumn, Entity, ManyToOne, PrimaryGeneratedColumn } from 'typeorm';
import { Monitor } from '../monitor';
import { EVENT_KINDS, eventKindType, genericEvent } from '../genericEvent/types';

@Entity()
export class MonitorEvent implements genericEvent {
@PrimaryGeneratedColumn()
id: number;

@Column()
title: string;

@Column('enum', { enum: EVENT_KINDS })
kind: eventKindType;

@ManyToOne(() => Monitor, { onDelete: 'CASCADE', nullable: false })
monitor: Monitor;

@CreateDateColumn({ type: 'timestamp' })
createdAt: string;
}
3 changes: 3 additions & 0 deletions src/modules/monitorEvent/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { MonitorEvent } from './MonitorEvent.entity';
import { buildMonitorEventService } from './monitorEvent.service';
export { MonitorEvent, buildMonitorEventService };
36 changes: 36 additions & 0 deletions src/modules/monitorEvent/monitorEvent.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { dataSource } from '../../dataSource';
import { Monitor } from '../monitor';
import { MonitorEvent } from './MonitorEvent.entity';

export { buildMonitorEventService };

function buildMonitorEventService() {
const monitorMonitorEventRepository = dataSource.getRepository(MonitorEvent);

const monitorMonitorEventService = {
createMonitorEvent,
getLastMonitorEvent,
};

return monitorMonitorEventService;

async function getLastMonitorEvent(monitorId: Monitor['id']) {
return monitorMonitorEventRepository.findOne({
where: { monitor: { id: monitorId } },
order: { createdAt: 'DESC' },
relations: ['monitor'],
});
}

async function createMonitorEvent(
monitorId: Monitor['id'],
params: { title: MonitorEvent['title']; kind: MonitorEvent['kind'] },
) {
await monitorMonitorEventRepository.insert({
monitor: { id: monitorId },
title: params.title,
kind: params.kind,
});
return true;
}
}
26 changes: 26 additions & 0 deletions src/scripts/pingMonitors.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { dataSource } from '../dataSource';
import { buildMonitorService } from '../modules/monitor';

async function pingMonitors() {
const monitorService = buildMonitorService();
console.log('Initializing database...');
await dataSource.initialize();
console.log('Database initialized!');
console.log('Getting monitors...');
const monitors = await monitorService.getMonitors();
console.log(`${monitors.length} monitors found!`);
console.log('Pinging monitors...');
await Promise.all(
monitors.map(async (monitor) => {
const shouldPingMonitor = monitorService.computeShouldPingMonitor(monitor, new Date());
if (!shouldPingMonitor) {
return;
}
return monitorService.pingMonitor(monitor);
}),
);

console.log('Done!');
}

pingMonitors();

0 comments on commit 2264516

Please sign in to comment.