namespace _heimdall.order { function schema_active( ) : _heimdall.helpers.json_schema.type_schema { return { "description": "whether the check shall be executed", "type": "boolean", "default": true, }; } function schema_threshold( ) : _heimdall.helpers.json_schema.type_schema { return { "description": "how often a condition has to occur in order to be reported", "type": "integer", "minimum": 1, "default": 3, }; } function schema_annoy( ) : _heimdall.helpers.json_schema.type_schema { return { "description": "whether notifications about non-ok states shall be kept sending after the threshold has been surpassed", "type": "boolean", "default": false, }; } function schema_interval( allow_null, default_ ) : _heimdall.helpers.json_schema.type_schema { return { "anyOf": [ { "description": "in seconds", "type": (allow_null ? "integer" : ["null", "integer"]), "exclusiveMinimum": 0, }, { "description": "as text", "type": "string", "enum": [ "minute", "hour", "day", "week", ] }, ], "default": default_, }; } function schema_schedule( ) { return { "type": "object", "additionalProperties": false, "properties": { "regular_interval": schema_interval(false, (60 * 60)), "attentive_interval": schema_interval(false, (60 * 2)), "reminding_interval": schema_interval(true, (60 * 60 * 24)), }, "required": [ ], }; } function schema_notifications( channel_implementations : Record ) { return { "type": "array", "items": { "anyOf": ( Object.entries(channel_implementations) .map( ([key, value]) => ({ "title": ("notification channel '" + key + "'"), "type": "object", "unevaluatedProperties": false, "properties": { "kind": { "type": "string", "enum": [key] }, "parameters": value.parameters_schema(), }, "required": [ "kind", "parameters" ], }) ) ) }, "default": [ { "kind": "console", "parameters": { } }, ], }; } export function schema_root( check_kind_implementations : Record, channel_implementations : Record, ) { return { "type": "object", "additionalProperties": false, "properties": { "defaults": { "description": "default values for checks", "type": "object", "additionalProperties": false, "properties": { "active": schema_active(), "threshold": schema_threshold(), "annoy": schema_annoy(), "schedule": schema_schedule(), "notifications": schema_notifications(channel_implementations), }, "required": [ ], }, "includes": { "type": "array", "items": { "type": "string" }, "default": [], "description": "list of relative or absolute paths to other hmdl files on the local machine, which shall be subsumed in the overall monitoring task" }, "checks": { "type": "array", "items": { "allOf": [ { "description": "should represent a specific check", "type": "object", "unevaluatedProperties": false, "properties": { "name": { "type": "string" }, "title": { "type": "string" }, "active": schema_active(), "threshold": schema_threshold(), "annoy": schema_annoy(), "schedule": schema_schedule(), "notifications": schema_notifications(channel_implementations), }, "required": [ "name", ], }, { "anyOf": ( Object.entries(check_kind_implementations) .map( ([key, value]) => ({ "title": ("check '" + key + "'"), "type": "object", "unevaluatedProperties": false, "properties": { "kind": { "type": "string", "enum": [key] }, "parameters": value.parameters_schema(), "custom": { "description": "custom data, which shall be attached to notifications", "default": null, }, }, "required": [ "kind", "parameters", ] }) ) ), }, ] } } }, "required": [ ] }; } function normalize_interval( interval_raw ) { if (interval_raw === null) { return null; } else { if (typeof(interval_raw) === "number") { return interval_raw; } else if (typeof(interval_raw) === "string") { const map_ = { "minute": (60), "hour": (60 * 60), "day": (60 * 60 * 24), "week": (60 * 60 * 24 * 7), }; if (! (interval_raw in map_)) { throw new Error("invalid string interval value: " + interval_raw); } else { return map_[interval_raw]; } } else { throw new Error("invalid type for interval value"); } } } function normalize_schedule( node ) { const node_ = Object.assign( { "regular_interval": (60 * 60), "attentive_interval": (60 * 2), "reminding_interval": (60 * 60 * 24), }, node ); return { "regular_interval": normalize_interval(node_["regular_interval"]), "attentive_interval": normalize_interval(node_["attentive_interval"]), "reminding_interval": normalize_interval(node_["reminding_interval"]), }; } function normalize_notification( channel_implementations : Record, node ) { if (! (node["kind"] in channel_implementations)) { throw new Error("invalid notification kind: " + node["kind"]); } else { return { "kind": node["kind"], "parameters": channel_implementations[node["kind"]].normalize_order_node(node["parameters"]), }; } } function normalize_defaults( channel_implementations : Record, node ) { const node_ = Object.assign( { "active": true, "threshold": 3, "annoy": false, "schedule": { "regular_interval": (60 * 60), "attentive_interval": (60 * 2), "reminding_interval": (60 * 60 * 24), }, "notifications": [ ], }, node ) return { "active": node_["active"], "threshold": node_["threshold"], "annoy": node_["annoy"], "schedule": node_["schedule"], "notifications": ( node_["notifications"] .map(x => normalize_notification(channel_implementations, x)) ), }; } function normalize_check( check_kind_implementations : Record, channel_implementations : Record, defaults, node ) { if (! ("name" in node)) { throw new Error("missing mandatory field in 'check' node: 'name'"); } else { if (! ("kind" in node)) { throw new Error("missing mandatory field in 'check' node: 'kind'"); } else { if (! (node["kind"] in check_kind_implementations)) { throw new Error("invalid check kind: " + node["kind"]); } else { const node_ = Object.assign( Object.assign( defaults, { "title": node["name"], "parameters": {}, "custom": null, }, ), node ); let node__ = {}; if (true) { node__["name"] = node_["name"]; } if (true) { node__["title"] = node_["title"]; } if ("active" in node_) { node__["active"] = node_["active"]; } if ("threshold" in node_) { node__["threshold"] = node_["threshold"]; } if ("annoy" in node_) { node__["annoy"] = node_["annoy"]; } if ("schedule" in node_) { node__["schedule"] = normalize_schedule(node_["schedule"]); } if ("notifications" in node_) { node__["notifications"] = ( node_["notifications"] .map( x => normalize_notification(channel_implementations, x), ) ); } if ("kind" in node_) { node__["kind"] = node_["kind"]; } if (true) { node__["parameters"] = check_kind_implementations[node_["kind"]].normalize_order_node(node_["parameters"]); } if ("custom" in node_) { node__["custom"] = node_["custom"]; } return node__; } } } } function normalize_root( check_kind_implementations : Record, channel_implementations : Record, node, options = {} ) { options = Object.assign( { "use_implicit_default_values": true, }, options ) let counts = {}; let checks_raw = ( ("checks" in node) ? node["checks"] : [] ); checks_raw.forEach( node_ => { if (! (node_["name"] in counts)) { counts[node_["name"]] = 0; } counts[node_["name"]] += 1; } ); let fails : Array<[string, int]> = ( Object.entries(counts) .filter( ([key, value]) => (value > 1) ) ); if (fails.length > 0) { throw new Error( lib_plankton.string.coin( "ambiguous check names: {{names}}", { "names": fails.map(([key, value]) => key).join(","), } ) ); } else { let defaults_raw = ( ("defaults" in node) ? node["defaults"] : {} ) let defaults = ( options["use_implicit_default_values"] ? normalize_defaults( channel_implementations, defaults_raw ) : defaults_raw ) let includes = ( ("includes" in node) ? node["includes"] : [] ) return { "defaults": defaults, "includes": includes, "checks": ( checks_raw .map( node_ => normalize_check( check_kind_implementations, channel_implementations, defaults, node_ ) ) ) }; } } export async function load( check_kind_implementations : Record, channel_implementations : Record, path : string, options = {} ) { options = Object.assign( { "root": true, "already_included": new Set([]), }, options ) if (path in options["already_included"]) { throw new Error("circular dependency detected") } else { const order_raw = lib_plankton.json.decode(await lib_plankton.file.read(path)); let includes : Array = ( ("includes" in order_raw) ? order_raw["includes"] : [] ); for (let index = 0; index < includes.length; index += 1) { let path_ = includes[index]; let sub_order = load( check_kind_implementations, channel_implementations, ( _os.path.isabs(path_) ? path_ : _os.path.join(_os.path.dirname(path), path_) ), { "root": false, // TODO set union "already_included": (options["already_included"] | {path}) } ) if (! ("checks" in order_raw)) { order_raw["checks"] = []; } order_raw["checks"].extend( sub_order["checks"] .map( check => Object.assign( check, { "name": lib_plankton.string.coin( "{{prefix}}.{{original_name}}", { "prefix": _os.path.basename(path_).split(".")[0], "original_name": check["name"], } ), } ), sub_order["checks"] ) ); order_raw["includes"] = []; } return normalize_root( check_kind_implementations, channel_implementations, order_raw, { "use_implicit_default_values": options["root"], } ); } } }