547 lines
12 KiB
TypeScript
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"],
|
|
}
|
|
);
|
|
}
|
|
}
|
|
|
|
}
|