async function main( ) : Promise { // consts const version : string = "0.8"; // init translations // TODO: use env language await lib_plankton.translate.initialize_promise( { "verbosity": 1, "order": ["de", "en"], "packages": await Promise.all( [ {"identifier": "de", "path": "localization/de.json"}, {"identifier": "en", "path": "localization/en.json"}, ] .map( (entry) => ( lib_plankton.file.read(entry.path) .then(content => Promise.resolve(JSON.parse(content))) .then(tree => Promise.resolve({"meta": {"identifier": entry.identifier}, "tree": tree})) ) ) ), } ); // args const arg_handler : lib_plankton.args.class_handler = new lib_plankton.args.class_handler( { "order_path": new lib_plankton.args.class_argument( { "name": "order_path", "type": lib_plankton.args.enum_type.string, "kind": lib_plankton.args.enum_kind.positional, "mode": lib_plankton.args.enum_mode.replace, "default": "monitoring.hmdl.json", "parameters": { "index": 0, }, "info": lib_plankton.translate.get("help.args.order_path"), } ), "show_help": new lib_plankton.args.class_argument( { "name": "help", "type": lib_plankton.args.enum_type.boolean, "kind": lib_plankton.args.enum_kind.volatile, "mode": lib_plankton.args.enum_mode.replace, "default": false, "parameters": { "indicators_long": ["help"], "indicators_short": ["h"], }, "info": lib_plankton.translate.get("help.args.show_help"), } ), "show_version": new lib_plankton.args.class_argument( { "name": "version", "type": lib_plankton.args.enum_type.boolean, "kind": lib_plankton.args.enum_kind.volatile, "mode": lib_plankton.args.enum_mode.replace, "default": false, "parameters": { "indicators_long": ["version"], "indicators_short": ["v"], }, "info": lib_plankton.translate.get("help.args.show_version"), } ), "show_schema": new lib_plankton.args.class_argument( { "name": "schema", "type": lib_plankton.args.enum_type.boolean, "kind": lib_plankton.args.enum_kind.volatile, "mode": lib_plankton.args.enum_mode.replace, "default": false, "parameters": { "indicators_long": ["schema"], "indicators_short": ["s"], }, "info": lib_plankton.translate.get("help.args.show_schema"), } ), "expose_full_order": new lib_plankton.args.class_argument( { "name": "expose_full_order", "type": lib_plankton.args.enum_type.boolean, "kind": lib_plankton.args.enum_kind.volatile, "mode": lib_plankton.args.enum_mode.replace, "default": false, "parameters": { "indicators_long": ["expose-full-order"], "indicators_short": ["e"], }, "info": lib_plankton.translate.get("help.args.expose_full_order"), } ), "erase_state": new lib_plankton.args.class_argument( { "name": "erase_state", "type": lib_plankton.args.enum_type.boolean, "kind": lib_plankton.args.enum_kind.volatile, "mode": lib_plankton.args.enum_mode.replace, "default": false, "parameters": { "indicators_long": ["erase-state"], "indicators_short": ["x"], }, "info": lib_plankton.translate.get("help.args.erase_state"), } ), "database_path": new lib_plankton.args.class_argument( { "name": "database_path", "type": lib_plankton.args.enum_type.string, "kind": lib_plankton.args.enum_kind.volatile, "mode": lib_plankton.args.enum_mode.replace, "default": null, "parameters": { "indicators_long": ["database-path"], "indicators_short": ["d"], }, "info": lib_plankton.translate.get("help.args.database_path"), } ), "mutex_path": new lib_plankton.args.class_argument( { "name": "mutex_path", "type": lib_plankton.args.enum_type.string, "kind": lib_plankton.args.enum_kind.volatile, "mode": lib_plankton.args.enum_mode.replace, "default": "/tmp/heimdall.lock", "parameters": { "indicators_long": ["mutex-path"], "indicators_short": ["m"], }, "info": lib_plankton.translate.get("help.args.mutex_path"), } ), "send_ok_notifications": new lib_plankton.args.class_argument( { "name": "send_ok_notifications", "type": lib_plankton.args.enum_type.boolean, "kind": lib_plankton.args.enum_kind.volatile, "mode": lib_plankton.args.enum_mode.replace, "default": false, "parameters": { "indicators_long": ["send-ok-notifications"], "indicators_short": ["y"], }, "info": lib_plankton.translate.get( "help.args.send_ok_notifications", { "condition_name": lib_plankton.translate.get("conditions.ok"), } ), } ), "language": new lib_plankton.args.class_argument( { "name": "language", "type": lib_plankton.args.enum_type.string, "kind": lib_plankton.args.enum_kind.volatile, "mode": lib_plankton.args.enum_mode.replace, "default": null, "parameters": { "indicators_long": ["language"], "indicators_short": ["l"], }, "info": lib_plankton.translate.get("help.args.language"), } ), "time_to_live": new lib_plankton.args.class_argument( { "name": "time_to_live", "type": lib_plankton.args.enum_type.float, "kind": lib_plankton.args.enum_kind.volatile, "mode": lib_plankton.args.enum_mode.replace, "default": (60 * 60 * 24 * 7), "parameters": { "indicators_long": ["time-to-live"], "indicators_short": ["t"], }, "info": lib_plankton.translate.get("help.args.time_to_live"), } ), "verbosity": new lib_plankton.args.class_argument( { "name": "verbosity", "type": lib_plankton.args.enum_type.integer, "kind": lib_plankton.args.enum_kind.volatile, "mode": lib_plankton.args.enum_mode.replace, "default": 1, "parameters": { "indicators_long": ["verbosity"], "indicators_short": ["v"], }, "info": lib_plankton.translate.get("help.args.verbosity"), } ), } ); const args : Record = arg_handler.read( lib_plankton.args.enum_environment.cli, process.argv.slice(2).join(" ") ); // setup logging lib_plankton.log.conf_push( [ new lib_plankton.log.class_channel_minlevel( new lib_plankton.log.class_channel_stdout(), [ lib_plankton.log.enum_level.debug, lib_plankton.log.enum_level.info, lib_plankton.log.enum_level.notice, lib_plankton.log.enum_level.warning, lib_plankton.log.enum_level.error, ][Math.min(4, args["verbosity"])] ), ] ); // exec if (args.show_help) { process.stdout.write( arg_handler.generate_help( { "programname": "heimdall", "description": lib_plankton.translate.get("help.title"), "executable": "heimdall", } ) ); } else { if (args["show_version"]) { process.stdout.write(version + "\n"); } else { const notification_kind_implementations : Record = { "console": _heimdall.notification_kinds.console.notification_kind_implementation(), }; const check_kind_implementations : Record = { "script": _heimdall.check_kinds.script.check_kind_implementation(), "http_request": _heimdall.check_kinds.http_request.check_kind_implementation(), "file_state": _heimdall.check_kinds.file_state.check_kind_implementation(), "tls_certificate": _heimdall.check_kinds.tls_certificate.check_kind_implementation(), "generic_remote": _heimdall.check_kinds.generic_remote.check_kind_implementation(), }; if (args["show_schema"]) { process.stdout.write( lib_plankton.json.encode( _heimdall.order.schema_root( check_kind_implementations, notification_kind_implementations ), true ) + "\n" ) } else { const nm_path = require("path"); const nm_fs = require("fs"); if (! nm_fs.existsSync(args["order_path"])) { process.stderr.write( lib_plankton.translate.get( "misc.order_file_not_found", { "path": args["order_path"], } ) + "\n" ); } else { // get order data const order = await _heimdall.order.load( check_kind_implementations, notification_kind_implementations, nm_path.normalize(args["order_path"]) ); if (args["expose_full_order"]) { process.stdout.write(lib_plankton.json.encode(order, true) + "\n"); } else { const database_path : string = ( (args["database_path"] !== null) ? args["database_path"] : nm_path.join( // TODO get path from fs or path module? "/tmp", lib_plankton.string.coin( "monitoring-state-{{id}}.sqlite", { "id": lib_plankton.call.convey( args["order_path"], [ nm_path.normalize, // encode(ascii), x => lib_plankton.sha256.get(x), x => x.slice(0, 8), ] ), } ) ) ); lib_plankton.log.info( lib_plankton.translate.get("misc.state_file_path"), { "database_path": database_path, } ); // mutex check if (nm_fs.existsSync(args["mutex_path"])) { lib_plankton.log.error( lib_plankton.translate.get("misc.still_running"), { "mutex_path": args["mutex_path"], } ); process.exit(2); } else { await _heimdall.state_repository.setup(database_path); const clean_count : int = await _heimdall.state_repository.clean( database_path, args["time_to_live"], args["erase_state"] ); // create mutex file await lib_plankton.file.write(args["mutex_path"], ""); order.checks.forEach( async (check) => { if (! check.active) { // do nothing } else { let old_item_state : (null | _heimdall.type_item_state); const last_notification_timestamp : (null | int) = await _heimdall.state_repository.get_last_notification_timestamp( database_path, check.name ); const rows : Array< { timestamp : int; condition : _heimdall.enum_condition; notification_sent : boolean; } > = await _heimdall.state_repository.probe( database_path, check.name, (check.threshold + 1), ); if (rows.length <= 0) { old_item_state = null; } else { let count : int = 1; rows.slice(1).some( (row) => { if (row.condition === rows[0].condition) { count += 1; return true; } else { return false; } } ); if (count > check.threshold) { count = null; } else { // do nothing } old_item_state = { "timestamp": rows[0].timestamp, "condition": rows[0].condition, "count": count, "last_notification_timestamp": last_notification_timestamp, } const timestamp : int = _heimdall.get_current_timestamp(); const due : boolean = ( (old_item_state === null) || (old_item_state.condition !== _heimdall.enum_condition.ok) || ((timestamp - old_item_state.timestamp) >= check.schedule.regular_interval) || ( (! (old_item_state.count === null)) && ((timestamp - old_item_state.timestamp) >= check.schedule.attentive_interval) ) ); if (! due) { // do nothing } else { process.stderr.write( lib_plankton.string.coin( "-- {{check_name}}\n", { "check_name": check.name, } ) ); // execute check and set new state let result; try { result = await check_kind_implementations[check.kind].run(check.parameters); } catch (error) { result = { "condition": _heimdall.enum_condition.unknown, "info": { "cause": lib_plankton.translate.get("misc.check_procedure_failed"), "error": error.toString(), }, } } const count : (null | int) = ( ( (old_item_state === null) || (old_item_state.condition !== result.condition) ) ? 1 : ( ( (! (old_item_state.count === null)) && ((old_item_state.count + 1) <= check.threshold) ) ? (old_item_state.count + 1) : null ) ) const shall_send_notification : boolean = ( ( ( (! (count === null)) && (count == check.threshold) ) || ( (count === null) && check.annoy ) || ( (count === null) && ( (! (old_item_state === null)) && (! (old_item_state.last_notification_timestamp === null)) && (! (check.schedule.reminding_interval === null)) && ( (timestamp - old_item_state.last_notification_timestamp) >= check.schedule.reminding_interval ) ) ) ) && ( (result.condition !== _heimdall.enum_condition.ok) || args["send_ok_notifications"] ) ) const new_item_state : _heimdall.type_item_state = { "timestamp": timestamp, "condition": result.condition, "count": count, "last_notification_timestamp": ( shall_send_notification ? timestamp : ( (old_item_state === null) ? null : old_item_state.last_notification_timestamp ) ), } await _heimdall.state_repository.feed( database_path, check.name, timestamp, result.condition, shall_send_notification, result.info ); // send notifications if (! shall_send_notification) { // do nothing } else { check.notifications.forEach( notification => { notification_kind_implementations[notification.kind].notify( notification.parameters, check.name, check, new_item_state, Object.assign( ( (check.custom === null) ? {} : {"custom": check.custom} ), result.info ) ) } ); } } } } } ); // drop mutex file await ( new Promise( (resolve, reject) => { nm_fs.unlink( args["mutex_path"], () => { resolve(undefined); } ); } ) ); } } } } } } } main();