slack notifier

This commit is contained in:
beckerinj
2022-05-10 13:24:27 -04:00
parent 1acf4d989e
commit 7fe05d7c8a
8 changed files with 257 additions and 2 deletions

View File

@@ -23,6 +23,7 @@
"dependencies": {
"@monitor/util": "1.0.0",
"@monitor/util-node": "1.0.0",
"@slack/web-api": "^6.7.1",
"axios": "^0.26.1",
"bcrypt": "^5.0.1",
"dockerode": "^3.3.1",

View File

@@ -45,3 +45,10 @@ export const SERVER_CHECK_TIMEOUT = getNumberFromEnv(
"SERVER_CHECK_TIMEOUT",
1000
);
export const SERVER_STATS_INTERVAL = getNumberFromEnv("SERVER_STATS_INTERVAL", 5 * 60 * 1000); // 5 minute check default
export const SLACK_CHANNEL = getStringFromEnv("SLACK_CHANNEL", "");
export const SLACK_TOKEN = getStringFromEnv("SLACK_TOKEN", "");
export const CPU_USAGE_NOTIFY_LIMIT = getNumberFromEnv("CPU_USAGE_NOTIFY_LIMIT", 50);
export const MEM_USAGE_NOTIFY_LIMIT = getNumberFromEnv("MEM_USAGE_NOTIFY_LIMIT", 75);
export const DISK_USAGE_NOTIFY_LIMIT = getNumberFromEnv("DISK_USAGE_NOTIFY_LIMIT", 75);

View File

@@ -9,6 +9,7 @@ import docker from "./plugins/docker";
import frontend from "./plugins/frontend";
import actionStates from "./plugins/actionStates";
import routes from "./routes";
import slackNotifier from "./plugins/slackNotifier";
async function main() {
const app = fastify({ logger: LOGGER })
@@ -31,6 +32,7 @@ async function main() {
.register(ws)
.register(frontend)
.register(actionStates)
.register(slackNotifier)
.register(routes);
app.listen(PORT, "0.0.0.0", async (err, address) => {

View File

@@ -0,0 +1,78 @@
import { getSystemStats } from "@monitor/util-node";
import { FastifyInstance } from "fastify";
import fp from "fastify-plugin";
import {
CPU_USAGE_NOTIFY_LIMIT,
DISK_USAGE_NOTIFY_LIMIT,
MEM_USAGE_NOTIFY_LIMIT,
SERVER_STATS_INTERVAL,
SLACK_TOKEN,
} from "../config";
import { getPeripherySystemStats } from "../util/periphery/server";
import { serverStatusPeriphery } from "../util/periphery/status";
import { notifySlack } from "../util/slack";
const slackNotifier = fp((app: FastifyInstance, _: {}, done: () => void) => {
const getAllServerStats = async () => {
const servers = await app.servers.find({});
const serversWithStatus = (
await Promise.all(
servers
.filter((server) => server.enabled)
.map(async (server) => {
const status = await serverStatusPeriphery(server);
return {
...server,
stats: server.isCore
? await getSystemStats()
: status
? await getPeripherySystemStats(server)
: undefined,
};
})
)
).filter((server) => server.stats);
return serversWithStatus;
};
const interval = async () => {
const servers = await getAllServerStats();
servers.forEach((server) => {
// check for out of bounds stats
const stats = server.stats!;
if (stats.cpu > CPU_USAGE_NOTIFY_LIMIT) {
// high cpu usage
notifySlack(`
WARNING | server ${server.name} has high CPU usage.
usage: ${stats.cpu}%
`);
}
if (stats.mem.usedMemPercentage > MEM_USAGE_NOTIFY_LIMIT) {
// high memory usage
notifySlack(`
WARNING | server ${server.name} has high memory usage.
using ${stats.mem.usedMemMb} MB of ${stats.mem.totalMemMb} MB (${stats.mem.usedMemPercentage}%)
`);
}
if (stats.disk.usedPercentage > DISK_USAGE_NOTIFY_LIMIT) {
// high disk usage
notifySlack(`
WARNING | server ${server.name} has high disk usage.
using ${stats.disk.usedGb} GB of ${stats.disk.totalGb} (${stats.disk.usedPercentage}% full)
`);
}
});
};
if (SLACK_TOKEN) {
// only do this if slack token is provided
setInterval(interval, SERVER_STATS_INTERVAL);
}
done();
});
export default slackNotifier;

View File

@@ -1,4 +1,4 @@
import { CommandLogError, Server } from "@monitor/types";
import { CommandLogError, Server, SystemStats } from "@monitor/types";
import axios from "axios";
import { SECRETS } from "../../config";
@@ -24,7 +24,7 @@ export async function getPeripheryDockerStats({ address }: Server) {
export async function getPeripherySystemStats({ address }: Server) {
return await axios
.get<CommandLogError>(`${address}/sys-stats`, {
.get<SystemStats>(`${address}/sys-stats`, {
headers: {
Authorization: SECRETS.PASSKEY,
},

18
core/src/util/slack.ts Normal file
View File

@@ -0,0 +1,18 @@
import { readableTimestamp, timestamp } from "@monitor/util";
import { WebClient, LogLevel } from "@slack/web-api";
import { SLACK_CHANNEL, SLACK_TOKEN } from "../config";
const slack = new WebClient(SLACK_TOKEN, { logLevel: LogLevel.INFO });
export async function notifySlack(text: string) {
try {
await slack.chat.postMessage({
token: SLACK_TOKEN,
channel: SLACK_CHANNEL,
text,
});
} catch (error) {
console.log("POST TO SLACK FAILED @", readableTimestamp(timestamp()));
console.log(error);
}
}

View File

@@ -120,4 +120,16 @@ export function objMap<T>(obj: { [key: string]: T }, map: (entry: T) => any) {
return Object.fromEntries(
Object.entries(obj).map((entry) => [entry[0], map(entry[1])])
);
}
export function readableTimestamp(unixTimeInSecs: number) {
const date = new Date(unixTimeInSecs * 1000);
const hours24 = date.getHours();
let hours = hours24 % 12;
if (hours === 0) hours = 12;
const pm = hours24 > 11;
const minutes = date.getMinutes();
return `${date.getMonth() + 1}/${date.getDate()} ${hours}:${
minutes > 9 ? minutes : "0" + minutes
} ${pm ? "PM" : "AM"}`;
}

137
yarn.lock
View File

@@ -385,6 +385,35 @@
semver "^7.3.5"
tar "^6.1.11"
"@slack/logger@^3.0.0":
version "3.0.0"
resolved "https://registry.yarnpkg.com/@slack/logger/-/logger-3.0.0.tgz#b736d4e1c112c22a10ffab0c2d364620aedcb714"
integrity sha512-DTuBFbqu4gGfajREEMrkq5jBhcnskinhr4+AnfJEk48zhVeEv3XnUKGIX98B74kxhYsIMfApGGySTn7V3b5yBA==
dependencies:
"@types/node" ">=12.0.0"
"@slack/types@^2.0.0":
version "2.4.0"
resolved "https://registry.yarnpkg.com/@slack/types/-/types-2.4.0.tgz#b6f6d50e9181f723080b841302e089739cef512d"
integrity sha512-0k8UlVEH9gUVwTbwcanS1JT2vCROkr1WESgdXW7d2maWYTuwbVEx87YvXPjsemAJfdu+RYqxGhO2oGTigprepA==
"@slack/web-api@^6.7.1":
version "6.7.1"
resolved "https://registry.yarnpkg.com/@slack/web-api/-/web-api-6.7.1.tgz#a0e983ec7925dccaf8fe15047f1a53eb82c24888"
integrity sha512-Aa2E/7NtGagd7mVsFCrc69iZMoviR2032SBOic06sYVvptdzJlvNsSQVqLCb1Aqz7r/jodb2fnXO1gl016OcWQ==
dependencies:
"@slack/logger" "^3.0.0"
"@slack/types" "^2.0.0"
"@types/is-stream" "^1.1.0"
"@types/node" ">=12.0.0"
axios "^0.26.1"
eventemitter3 "^3.1.0"
form-data "^2.5.0"
is-electron "2.2.0"
is-stream "^1.1.0"
p-queue "^6.6.1"
p-retry "^4.0.0"
"@swc/core-android-arm-eabi@1.2.156":
version "1.2.156"
resolved "https://registry.yarnpkg.com/@swc/core-android-arm-eabi/-/core-android-arm-eabi-1.2.156.tgz#4d4cc38d5071b74c7f55ddc5e0931a2cd3bcc530"
@@ -499,6 +528,13 @@
dependencies:
"@types/node" "*"
"@types/is-stream@^1.1.0":
version "1.1.0"
resolved "https://registry.yarnpkg.com/@types/is-stream/-/is-stream-1.1.0.tgz#b84d7bb207a210f2af9bed431dc0fbe9c4143be1"
integrity sha512-jkZatu4QVbR60mpIzjINmtS1ZF4a/FqdTUTBeQDVOQ2PYyidtwFKr0B5G6ERukKwliq+7mIXvxyppwzG5EgRYg==
dependencies:
"@types/node" "*"
"@types/minimist@^1.2.2":
version "1.2.2"
resolved "https://registry.yarnpkg.com/@types/minimist/-/minimist-1.2.2.tgz#ee771e2ba4b3dc5b372935d549fd9617bf345b8c"
@@ -514,6 +550,11 @@
resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.21.tgz#864b987c0c68d07b4345845c3e63b75edd143644"
integrity sha512-DBZCJbhII3r90XbQxI8Y9IjjiiOGlZ0Hr32omXIZvwwZ7p4DMMXGrKXVyPfuoBOri9XNtL0UK69jYIBIsRX3QQ==
"@types/node@>=12.0.0":
version "17.0.31"
resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.31.tgz#a5bb84ecfa27eec5e1c802c6bbf8139bdb163a5d"
integrity sha512-AR0x5HbXGqkEx9CadRH3EBYx/VkiUgZIhP4wvPn/+5KIsgpNoyFaRlVe0Zlx9gRtg8fA06a9tskE2MSN7TcG4Q==
"@types/normalize-package-data@^2.4.0":
version "2.4.1"
resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz#d3357479a0fdfdd5907fe67e17e0a85c906e1301"
@@ -533,6 +574,11 @@
"@types/scheduler" "*"
csstype "^3.0.2"
"@types/retry@0.12.0":
version "0.12.0"
resolved "https://registry.yarnpkg.com/@types/retry/-/retry-0.12.0.tgz#2b35eccfcee7d38cd72ad99232fbd58bffb3c84d"
integrity sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==
"@types/scheduler@*":
version "0.16.2"
resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.2.tgz#1a62f89525723dde24ba1b01b092bf5df8ad4d39"
@@ -694,6 +740,11 @@ astral-regex@^2.0.0:
resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31"
integrity sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==
asynckit@^0.4.0:
version "0.4.0"
resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
integrity sha1-x57Zf380y48robyXkLzDZkdLS3k=
atomic-sleep@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/atomic-sleep/-/atomic-sleep-1.0.0.tgz#eb85b77a601fc932cfe432c5acd364a9e2c9075b"
@@ -951,6 +1002,13 @@ color-support@^1.1.2:
resolved "https://registry.yarnpkg.com/color-support/-/color-support-1.1.3.tgz#93834379a1cc9a0c61f82f52f0d04322251bd5a2"
integrity sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==
combined-stream@^1.0.6:
version "1.0.8"
resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f"
integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==
dependencies:
delayed-stream "~1.0.0"
concat-map@0.0.1:
version "0.0.1"
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
@@ -1039,6 +1097,11 @@ deepmerge@^4.2.2:
resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.2.2.tgz#44d2ea3679b8f4d4ffba33f03d865fc1e7bf4955"
integrity sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==
delayed-stream@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk=
delegates@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a"
@@ -1284,6 +1347,16 @@ etag@~1.8.1:
resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887"
integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=
eventemitter3@^3.1.0:
version "3.1.2"
resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-3.1.2.tgz#2d3d48f9c346698fce83a85d7d664e98535df6e7"
integrity sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q==
eventemitter3@^4.0.4:
version "4.0.7"
resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f"
integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==
fast-decode-uri-component@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/fast-decode-uri-component/-/fast-decode-uri-component-1.0.1.tgz#46f8b6c22b30ff7a81357d4f59abfae938202543"
@@ -1480,6 +1553,15 @@ follow-redirects@^1.14.8:
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.9.tgz#dd4ea157de7bfaf9ea9b3fbd85aa16951f78d8d7"
integrity sha512-MQDfihBQYMcyy5dhRDJUHcw7lb2Pv/TuE6xP1vyraLukNDHKbDxDNaOE3NbCAdKQApno+GPRyo1YAp89yCjK4w==
form-data@^2.5.0:
version "2.5.1"
resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.5.1.tgz#f2cbec57b5e59e23716e128fe44d4e5dd23895f4"
integrity sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==
dependencies:
asynckit "^0.4.0"
combined-stream "^1.0.6"
mime-types "^2.1.12"
forwarded@0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811"
@@ -1770,6 +1852,11 @@ is-core-module@^2.5.0, is-core-module@^2.8.1:
dependencies:
has "^1.0.3"
is-electron@2.2.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/is-electron/-/is-electron-2.2.0.tgz#8943084f09e8b731b3a7a0298a7b5d56f6b7eef0"
integrity sha512-SpMppC2XR3YdxSzczXReBjqs2zGscWQpBIKqwXYBFic0ERaxNVgwLCHwOLZeESfdJQjX0RDvrJ1lBXX2ij+G1Q==
is-extglob@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2"
@@ -1797,6 +1884,11 @@ is-plain-obj@^1.1.0:
resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e"
integrity sha1-caUMhCnfync8kqOQpKA7OfzVHT4=
is-stream@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44"
integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ=
is-what@^4.1.6:
version "4.1.7"
resolved "https://registry.yarnpkg.com/is-what/-/is-what-4.1.7.tgz#c41dc1d2d2d6a9285c624c2505f61849c8b1f9cc"
@@ -1952,6 +2044,18 @@ merge-anything@^5.0.2:
is-what "^4.1.6"
ts-toolbelt "^9.6.0"
mime-db@1.52.0:
version "1.52.0"
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70"
integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==
mime-types@^2.1.12:
version "2.1.35"
resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a"
integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==
dependencies:
mime-db "1.52.0"
mime@1.6.0:
version "1.6.0"
resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1"
@@ -2193,6 +2297,11 @@ onetime@^5.1.0:
dependencies:
mimic-fn "^2.1.0"
p-finally@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae"
integrity sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=
p-limit@^3.0.2, p-limit@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b"
@@ -2207,6 +2316,29 @@ p-locate@^5.0.0:
dependencies:
p-limit "^3.0.2"
p-queue@^6.6.1:
version "6.6.2"
resolved "https://registry.yarnpkg.com/p-queue/-/p-queue-6.6.2.tgz#2068a9dcf8e67dd0ec3e7a2bcb76810faa85e426"
integrity sha512-RwFpb72c/BhQLEXIZ5K2e+AhgNVmIejGlTgiB9MzZ0e93GRvqZ7uSi0dvRF7/XIXDeNkra2fNHBxTyPDGySpjQ==
dependencies:
eventemitter3 "^4.0.4"
p-timeout "^3.2.0"
p-retry@^4.0.0:
version "4.6.2"
resolved "https://registry.yarnpkg.com/p-retry/-/p-retry-4.6.2.tgz#9baae7184057edd4e17231cee04264106e092a16"
integrity sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==
dependencies:
"@types/retry" "0.12.0"
retry "^0.13.1"
p-timeout@^3.2.0:
version "3.2.0"
resolved "https://registry.yarnpkg.com/p-timeout/-/p-timeout-3.2.0.tgz#c7e17abc971d2a7962ef83626b35d635acf23dfe"
integrity sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==
dependencies:
p-finally "^1.0.0"
parse-json@^5.2.0:
version "5.2.0"
resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd"
@@ -2434,6 +2566,11 @@ ret@~0.2.0:
resolved "https://registry.yarnpkg.com/ret/-/ret-0.2.2.tgz#b6861782a1f4762dce43402a71eb7a283f44573c"
integrity sha512-M0b3YWQs7R3Z917WRQy1HHA7Ba7D8hvZg6UE5mLykJxQVE2ju0IXbGlaHPPlkY+WN7wFP+wUMXmBFA0aV6vYGQ==
retry@^0.13.1:
version "0.13.1"
resolved "https://registry.yarnpkg.com/retry/-/retry-0.13.1.tgz#185b1587acf67919d63b357349e03537b2484658"
integrity sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==
reusify@^1.0.0, reusify@^1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76"