diff --git a/source/localization/de.json b/source/localization/de.json index ba0fc67..8ae818d 100644 --- a/source/localization/de.json +++ b/source/localization/de.json @@ -16,6 +16,7 @@ "help.args.show_schema": "nur das hmdl-JSON-Schema zur Standard-Ausgabe schreiben und beenden", "help.args.expose_full_order": "nur den Pfad zur Datenbank-Datei zur Standard-Ausgabe schreiben und beenden (nützlich für Fehlersuche)", "help.args.show_version": "nur die Version zur Standard-Ausgabe schreiben und beenden", + "help.args.verbosity": "Schwellwert für Log-Ausgaben", "checks.file_state.exists": "Datei existiert (soll aber nicht)", "checks.file_state.missing": "Datei existiert nicht (soll aber)", "checks.file_state.timestamp_implausible": "Datei ist scheinbar aus der Zukunft", diff --git a/source/localization/en.json b/source/localization/en.json index 73698f4..318dcc5 100644 --- a/source/localization/en.json +++ b/source/localization/en.json @@ -16,6 +16,7 @@ "help.args.show_schema": "print the hmdl JSON schema to stdout and exit", "help.args.expose_full_order": "only print the extended order to stdout and exit (useful for debugging)", "help.args.show_version": "only print the version to stdout and exit", + "help.args.verbosity": "threshold for log outputs", "checks.file_state.exists": "file exists (but shall not)", "checks.file_state.missing": "file does not exist (but shall)", "checks.file_state.timestamp_implausible": "file is apparently from the future", diff --git a/source/logic/base.ts b/source/logic/base.ts index 5978791..25d5c1a 100644 --- a/source/logic/base.ts +++ b/source/logic/base.ts @@ -102,6 +102,15 @@ namespace _heimdall }; + /** + */ + export function get_current_timestamp( + ) : int + { + return Math.floor(Date.now() / 1000); + } + + /** * converts a condition to a human readable string */ diff --git a/source/logic/check_kinds/file_state.ts b/source/logic/check_kinds/file_state.ts index a3c6f2f..86cf1a2 100644 --- a/source/logic/check_kinds/file_state.ts +++ b/source/logic/check_kinds/file_state.ts @@ -90,7 +90,7 @@ namespace _heimdall.check_kinds.file_state ) : any { const version : string = ( - (! ("exist_mode" in node) + (! ("exist_mode" in node)) ? "v1" : "v2" ); @@ -115,6 +115,7 @@ namespace _heimdall.check_kinds.file_state node ); return { + "path": node["path"], "exist_mode": node_["exist"], "exist_critical": node_["strict"], "age_threshold_concerning": ( @@ -171,7 +172,153 @@ namespace _heimdall.check_kinds.file_state parameters ) : Promise<_heimdall.type_result> { - return Promise.reject<_heimdall.type_result>(new Error("not implemented")); + const nm_fs = require("fs"); + + let condition : _heimdall.enum_condition = _heimdall.enum_condition.ok; + let faults : Array = []; + let data : Record = {}; + + let exists : boolean; + let stats : { + ctime : float; + size : int; + }; + await new Promise( + (resolve, reject) => { + nm_fs.stat( + parameters["path"], + { + "bigint": false, + }, + (err, stats_) => { + if (err) { + exists = false; + stats = null; + resolve(undefined); + } + else { + exists = true; + stats = stats_; + resolve(undefined); + } + } + ); + } + ); + if (! parameters["exist_mode"]) { + if (exists) { + condition = ( + parameters["exist_critical"] + ? _heimdall.enum_condition.concerning + : _heimdall.enum_condition.critical + ); + faults.push(lib_plankton.translate.get("checks.file_state.exists")); + } + else { + // do nothing + } + } + else { + if (! exists) { + condition = ( + parameters["exist_critical"] + ? _heimdall.enum_condition.concerning + : _heimdall.enum_condition.critical + ); + faults.push(lib_plankton.translate.get("checks.file_state.missing")); + } + else { + // age + { + const timestamp_this : int = _heimdall.get_current_timestamp(); + const timestamp_that : int = Math.floor(stats.ctime); + const age : int = (timestamp_this - timestamp_that); + if (age < 0) { + condition = _heimdall.enum_condition.critical; + faults.push(lib_plankton.translate.get("checks.file_state.timestamp_implausible")); + } + else { + if ( + (parameters["age_threshold_critical"] !== null) + && + (age > parameters["age_threshold_critical"]) + ) { + condition = _heimdall.enum_condition.critical; + faults.push(lib_plankton.translate.get("checks.file_state.too_old")); + } + else { + if ( + (parameters["age_threshold_concerning"] !== null) + && + (age > parameters["age_threshold_concerning"]) + ) { + condition = _heimdall.enum_condition.critical; + faults.push(lib_plankton.translate.get("checks.file_state.too_old")); + } + else { + // do nothing + } + } + } + data = Object.assign( + data, + { + "timestamp_of_checking_instance": timestamp_this, + "timestamp_of_file": timestamp_that, + "age_value_in_seconds": age, + "age_threshold_in_seconds_concerning": parameters["age_threshold_concerning"], + "age_threshold_in_seconds_critical": parameters["age_threshold_critical"], + } + ); + } + // size + { + const size : int = stats.size; + if (size < 0) { + condition = _heimdall.enum_condition.critical; + faults.push(lib_plankton.translate.get("checks.file_state.size_implausible")); + } + else { + if ( + (parameters["size_threshold_critical"] !== null) + && + (size > parameters["size_threshold_critical"]) + ) { + condition = _heimdall.enum_condition.critical; + faults.push(lib_plankton.translate.get("checks.file_state.too_big")); + } + else { + if ( + (parameters["size_threshold_concerning"] !== null) + && + (size > parameters["size_threshold_concerning"]) + ) { + condition = _heimdall.enum_condition.critical; + faults.push(lib_plankton.translate.get("checks.file_state.too_big")); + } + else { + // do nothing + } + } + } + data = Object.assign( + data, + { + "size_value_in_bytes": size, + "size_threshold_in_bytes": parameters["size_threshold"], + } + ); + } + } + } + return { + "condition": condition, + "info": { + "path": parameters["path"], + "faults": faults, + "data": data, + } + }; } diff --git a/source/logic/main.ts b/source/logic/main.ts index 6987790..1e2c564 100644 --- a/source/logic/main.ts +++ b/source/logic/main.ts @@ -151,7 +151,12 @@ async function main( "indicators_long": ["send-ok-notifications"], "indicators_short": ["y"], }, - "info": lib_plankton.translate.get("help.args.send_ok_notifications"), + "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( @@ -182,6 +187,20 @@ async function main( "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( @@ -189,6 +208,22 @@ async function main( 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( @@ -211,6 +246,7 @@ async function main( }; const check_kind_implementations : Record = { "http_request": _heimdall.check_kinds.http_request.check_kind_implementation(), + "file_state": _heimdall.check_kinds.file_state.check_kind_implementation(), }; if (args["show_schema"]) { process.stdout.write( @@ -308,6 +344,13 @@ async function main( // 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; @@ -319,29 +362,19 @@ async function main( check.name, (check.threshold + 1), ); - - // TODO: type - 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 - ); - if (rows.length <= 0) { old_item_state = null; } else { - last_notification_timestamp = null; let count : int = 1; rows.slice(1).some( (row) => { if (row.condition === rows[0].condition) { count += 1; - return false; + return true; } else { - return true; + return false; } } ); @@ -351,17 +384,6 @@ async function main( else { // do nothing } - rows.some( - (row) => { - if (row.notification_sent) { - last_notification_timestamp = row.timestamp; - return true; - } - else { - return false; - } - } - ); old_item_state = { "timestamp": rows[0].timestamp, "condition": rows[0].condition, @@ -369,7 +391,7 @@ async function main( "last_notification_timestamp": last_notification_timestamp, } - const timestamp : int = Math.floor(Date.now() / 1000); + const timestamp : int = _heimdall.get_current_timestamp(); const due : boolean = ( (old_item_state === null) || diff --git a/source/logic/order.ts b/source/logic/order.ts index 302a8eb..52ffb3d 100644 --- a/source/logic/order.ts +++ b/source/logic/order.ts @@ -519,10 +519,13 @@ namespace _heimdall.order throw new Error("circular dependency detected") } else { - const order_raw = lib_plankton.json.decode(await lib_plankton.file.read(path)); + const order_raw : { + includes ?: Array; + checks ?: Array; + } = lib_plankton.json.decode(await lib_plankton.file.read(path)); let includes : Array = ( ("includes" in order_raw) - ? order_raw["includes"] + ? order_raw.includes : [] ); for (let index = 0; index < includes.length; index += 1) { @@ -541,9 +544,9 @@ namespace _heimdall.order } ) if (! ("checks" in order_raw)) { - order_raw["checks"] = []; + order_raw.checks = []; } - order_raw["checks"].extend( + order_raw.checks = order_raw.checks.concat( sub_order.checks .map( check => Object.assign( @@ -561,7 +564,7 @@ namespace _heimdall.order sub_order.checks ) ); - order_raw["includes"] = []; + order_raw.includes = []; } return normalize_root( check_kind_implementations, diff --git a/tools/heimdall.prj.json b/tools/heimdall.prj.json index 60e4eb3..be39629 100644 --- a/tools/heimdall.prj.json +++ b/tools/heimdall.prj.json @@ -26,6 +26,7 @@ "source/logic/notification_kinds/console.ts", "source/logic/check_kinds/_abstract.ts", "source/logic/check_kinds/http_request.ts", + "source/logic/check_kinds/file_state.ts", "source/logic/state_repository.ts", "source/logic/order.ts", "source/logic/main.ts" @@ -60,6 +61,7 @@ } }, { + "active": false, "name": "node_modules", "type": "copy", "parameters": {