core/source/logic/order.ts
Christian Fraß 4dbb7474e7 [int]
2023-06-18 23:29:57 +02:00

547 lines
12 KiB
TypeScript

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<string, _heimdall.channels.type_channel>
)
{
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<string, _heimdall.checks.type_check>,
channel_implementations : Record<string, _heimdall.channels.type_channel>,
)
{
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<string, _heimdall.channels.type_channel>,
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<string, _heimdall.channels.type_channel>,
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<string, _heimdall.checks.type_check>,
channel_implementations : Record<string, _heimdall.channels.type_channel>,
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<string, _heimdall.checks.type_check>,
channel_implementations : Record<string, _heimdall.channels.type_channel>,
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<string, _heimdall.checks.type_check>,
channel_implementations : Record<string, _heimdall.channels.type_channel>,
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<string> = (
("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"],
}
);
}
}
}