Parcourir la source

http check params data structure, typescript db migrations

Christian Kahlau il y a 2 ans
Parent
commit
c385c6f740

+ 4 - 1
common/types/http-check-config.d.ts

@@ -1,3 +1,6 @@
+type CheckDisjunction = Array<string | string[] | CheckConjunction>;
+type CheckConjunction = { and: string[] };
+
 type HttpCheckConfig = {
   id: number;
   serverId?: number;
@@ -9,5 +12,5 @@ type HttpCheckConfig = {
   timeout?: number;
   notify: boolean;
   notifyThreshold: number;
-  checks: string[];
+  checks: CheckDisjunction;
 };

+ 4 - 1
server/src/ctrl/database.class.ts

@@ -630,7 +630,10 @@ export class Database extends SQLiteController {
       timeout: (hcConf.params?.find(p => p.key === 'timeout')?.value as number) ?? defaults.serviceChecks.httpTimeout,
       notify: (hcConf.params?.find(p => p.key === 'notify')?.value as boolean) ?? defaults.serviceChecks.notify,
       notifyThreshold: (hcConf.params?.find(p => p.key === 'notifyThreshold')?.value as number) ?? defaults.serviceChecks.notifyThreshold,
-      checks: hcConf.params?.reduce((res, p) => (p.key === 'check' && Array.isArray(p.value) ? [...res, ...p.value] : res), [] as string[])
+      checks: hcConf.params?.reduce(
+        (res, p) => (p.key === 'check' && Array.isArray(p.value) ? [...res, ...p.value.map(c => JSON.parse(c))] : res),
+        [] as string[]
+      )
     };
     return {
       id: hcConf.id,

+ 41 - 16
server/src/ctrl/db-migration.class.ts

@@ -5,6 +5,7 @@ import { Database } from 'sqlite3';
 import { Logger } from '../../../common/util/logger.class';
 
 import { MigrationException } from '../lib/migration-exception.class';
+import { MigrationRunner } from '../lib/migration-runner.interface';
 import { SQLiteController } from './sqlite-controller.base';
 
 export class DBMigration extends SQLiteController {
@@ -26,7 +27,7 @@ export class DBMigration extends SQLiteController {
       Logger.debug('[DEBUG] lastid', lastID);
 
       for (const file of files) {
-        const m = /^(\d{12})_(.*)\.sql$/.exec(file);
+        const m = /^(\d{12})_(.*)\.(sql|js)$/.exec(file);
 
         if (!m) {
           throw new MigrationException(`File ${file} does not match migration file pattern. Aborted processing migrations.`);
@@ -35,21 +36,45 @@ export class DBMigration extends SQLiteController {
         const id = Number(m[1]);
         if (id <= lastID) continue;
 
-        const migFilepath = path.join(migrationsDir, file);
-        const migration = await fsp.readFile(migFilepath, { encoding: 'utf-8' });
-
-        Logger.info('[INFO] Applying DB migration', file);
-        await this.beginTransaction();
-        try {
-          await this.exec(migration);
-          await this.run('INSERT INTO db_migrations(id, title, migrated) VALUES(?, ?, ?);', [id, m[2], new Date().getTime()]);
-          await this.commit();
-          Logger.info('[INFO] DB migration', file, 'succeeded.');
-        } catch (error) {
-          Logger.error('[ERROR] DB migration failed at', file, '- Rolling back...');
-          await this.rollback();
-
-          throw error;
+        if (m[2] === 'sql') {
+          const migFilepath = path.join(migrationsDir, file);
+          const migration = await fsp.readFile(migFilepath, { encoding: 'utf-8' });
+
+          Logger.info('[INFO] Applying SQL DB migration', file);
+          await this.beginTransaction();
+          try {
+            await this.exec(migration);
+            await this.run('INSERT INTO db_migrations(id, title, migrated) VALUES(?, ?, ?);', [id, m[2], new Date().getTime()]);
+            await this.commit();
+            Logger.info('[INFO] DB migration', file, 'succeeded.');
+          } catch (error) {
+            Logger.error('[ERROR] DB migration failed at', file, '- Rolling back...');
+            await this.rollback();
+
+            throw error;
+          }
+        } else {
+          const migFilepath = path.join(migrationsDir, file.substring(0, file.length - 3));
+          const imp = require(migFilepath).default;
+          if (typeof imp !== 'function') {
+            console.log('TYPE', typeof imp, 'KEYS', Object.keys(imp));
+            throw new MigrationException(`File ${file} is not a valid <MigrationRunner> implementation!`);
+          }
+          const mig = imp as MigrationRunner;
+
+          Logger.info('[INFO] Applying TypeScript DB migration', file);
+          await this.beginTransaction();
+          try {
+            await mig(this);
+            await this.run('INSERT INTO db_migrations(id, title, migrated) VALUES(?, ?, ?);', [id, m[2], new Date().getTime()]);
+            await this.commit();
+            Logger.info('[INFO] DB migration', file, 'succeeded.');
+          } catch (error) {
+            Logger.error('[ERROR] DB migration failed at', file, '- Rolling back...');
+            await this.rollback();
+
+            throw error;
+          }
         }
       }
     }

+ 7 - 6
server/src/ctrl/http-check-controller.class.ts

@@ -106,12 +106,13 @@ export class HttpCheckController {
       const responseText = new String(response.data).toString();
 
       for (const check of conf.checks) {
-        const reg = new RegExp(check, 'i');
-        if (!reg.test(responseText)) {
-          Logger.debug(`[DEBUG] Regular expression /${check}/i not found in response`);
-          await this.db.insertHealthCheckData(conf.id, now, HttpCheckStatus.CheckFailed, `Regular expression /${check}/i not found in response`);
-          success = false;
-        }
+        // TODO: new CheckDisjunction app logic
+        // const reg = new RegExp(check, 'i');
+        // if (!reg.test(responseText)) {
+        //   Logger.debug(`[DEBUG] Regular expression /${check}/i not found in response`);
+        //   await this.db.insertHealthCheckData(conf.id, now, HttpCheckStatus.CheckFailed, `Regular expression /${check}/i not found in response`);
+        //   success = false;
+        // }
       }
 
       if (success) {

+ 3 - 0
server/src/lib/migration-runner.interface.ts

@@ -0,0 +1,3 @@
+import { SQLiteController } from '../ctrl/sqlite-controller.base';
+
+export type MigrationRunner = (db: SQLiteController) => Promise<void>;

+ 30 - 0
server/src/migrations/202301302212_website_healtcheck_disjunctive_checks.ts

@@ -0,0 +1,30 @@
+import { SQLiteController } from '../ctrl/sqlite-controller.base';
+
+export default async function (db: SQLiteController) {
+  const result = await db.stmt("SELECT * FROM `HealthCheckParams` WHERE `Type` = 'regexp' AND `Key` = 'check'", []);
+  const idsToDelete: number[] = [];
+  const checksByConfId = result.rows.reduce((res, row) => {
+    idsToDelete.push(row['ID']);
+    const confId = row['ConfigID'];
+    if (!res[confId]) {
+      res[confId] = [];
+    }
+    res[confId].push(row['Value']);
+    return res;
+  }, {}) as { [confID: number]: string[] };
+
+  for (const entry of Object.entries(checksByConfId)) {
+    const confId = entry[0] as unknown as number;
+    const checks = entry[1];
+
+    const conjunction = { and: checks };
+
+    await db.stmt('INSERT INTO `HealthCheckParams`(`ConfigID`, `Type`, `Key`, `Value`) VALUES(?, ?, ?, ?)', [
+      confId,
+      'regexp',
+      'check',
+      JSON.stringify(conjunction)
+    ]);
+  }
+  await db.run(`DELETE FROM HealthCheckParams WHERE ID IN (${idsToDelete.map(() => '?').join(',')});`, idsToDelete);
+}

+ 2 - 1
server/tsconfig.json

@@ -12,5 +12,6 @@
       "*": ["node_modules/*", "src/*", "../common/*"]
     }
   },
-  "include": ["./src/*", "./src/**/*", "../common/*", "../common/**/*"]
+  "include": ["./src/*", "./src/**/*", "../common/*", "../common/**/*"],
+  "exclude": ["./src/migrations/*"]
 }

+ 16 - 0
server/tsconfig.mig.json

@@ -0,0 +1,16 @@
+{
+  "compilerOptions": {
+    "strict": true,
+    "outDir": "./dist/server/src",
+    "esModuleInterop": true,
+    "moduleResolution": "node",
+    "module": "commonjs",
+    "target": "ES2016",
+    "sourceMap": false,
+    "baseUrl": "./src/migrations",
+    "paths": {
+      "*": ["node_modules/*", "src/*", "../common/*"]
+    }
+  },
+  "include": ["./src/migrations/*"]
+}