]> git.immae.eu Git - perso/Immae/Config/Nix.git/commitdiff
Move notification systems to apprise
authorIsmaël Bouya <ismael.bouya@normalesup.org>
Tue, 26 Jul 2022 00:23:57 +0000 (02:23 +0200)
committerIsmaël Bouya <ismael.bouya@normalesup.org>
Sun, 9 Apr 2023 15:16:25 +0000 (17:16 +0200)
14 files changed:
flakes/private/buildbot/flake.nix
modules/private/buildbot/common/build_helpers.py
modules/private/buildbot/projects/caldance/__init__.py
modules/private/buildbot/projects/cryptoportfolio/__init__.py
modules/private/buildbot/projects/immaeEu/__init__.py
modules/private/buildbot/projects/test/__init__.py
modules/private/environment.nix
modules/private/gitolite/default.nix
modules/private/monitoring/myplugins.nix
modules/private/monitoring/objects_master.nix
modules/private/monitoring/objects_monitoring-1.nix
modules/private/monitoring/plugins/notify_by_apprise [new file with mode: 0755]
modules/private/websites/tools/tools/default.nix
modules/private/websites/tools/tools/webhooks.nix

index eb699a06339d75e7c5b1209f5de7628658ed0995..1cbd0ea23f98b3f0ea644bc6e8b16a558fafaebb 100644 (file)
@@ -45,8 +45,9 @@
             '';
         });
         common_packages = pkgs: [
-          pkgs.libvirt pkgs.treq pkgs.ldap3 buildbot-full
-          pkgs.buildbot-worker pkgs.pip buildbot_common wokkel
+          (pkgs.apprise.overridePythonAttrs(old: { propagatedBuildInputs = old.propagatedBuildInputs ++ [ pkgs.sleekxmpp ]; })) pkgs.libvirt pkgs.treq pkgs.ldap3
+          buildbot-full pkgs.buildbot-worker pkgs.pip buildbot_common
+          wokkel
         ];
       };
       buildslist-plugin = pkgs.callPackage ./buildslist {
index ebd49ae9d4a53310b9aaf7ecb605bb07769a00ac..77e6c074c5c4bc71777a218dab48b0c9e580df04 100644 (file)
@@ -4,10 +4,11 @@ from shutil import which
 
 __all__ = [
         "force_scheduler", "deploy_scheduler", "git_hook_scheduler",
-        "clean_branch", "package_and_upload", "SlackStatusPush",
+        "clean_branch", "package_and_upload", "AppriseStatusPush",
         "XMPPStatusPush", "NixShellCommand",
         "all_builder_names", "compute_build_infos", "deploy_ssh_command",
-        "configure_slack_push", "configure_xmpp_push", "deploy_hook_scheduler",
+        "configure_apprise_push",
+        "configure_xmpp_push", "deploy_hook_scheduler",
         ]
 
 # Small helpers"
@@ -119,45 +120,35 @@ def deploy_hook_scheduler(project, builders, timer=1):
 def all_builder_names(c):
     return [builder.name for builder in c['builders']]
 
-# Slack/XMPP status push
+# Apprise/XMPP status push
 from buildbot.reporters.http import HttpStatusPushBase
 from twisted.internet import defer
 from twisted.python import log
-from buildbot.util import httpclientservice
 from buildbot.reporters import utils
 from buildbot.process import results
 from twisted.words.protocols.jabber.jid import JID
 from wokkel import client, xmppim
 from functools import partial
+import apprise
 
-class SlackStatusPush(HttpStatusPushBase):
-    name = "SlackStatusPush"
+class AppriseStatusPush(HttpStatusPushBase):
+    name = "AppriseStatusPush"
 
     @defer.inlineCallbacks
-    def reconfigService(self, serverUrl, **kwargs):
+    def reconfigService(self, appriseUrls, **kwargs):
+        self.appriseUrls = appriseUrls
         yield HttpStatusPushBase.reconfigService(self, **kwargs)
-        self._http = yield httpclientservice.HTTPClientService.getService(
-            self.master, serverUrl)
 
     @defer.inlineCallbacks
     def send(self, build):
         yield utils.getDetailsForBuild(self.master, build, wantProperties=True)
-        response = yield self._http.post("", json=self.format(build))
-        if response.code != 200:
-            log.msg("%s: unable to upload status: %s" %
-                    (response.code, response.content))
+        appobject = apprise.Apprise()
+        message = self.format(build)
+        for url in self.appriseUrls:
+            appobject.add(url.format(**message))
+        yield appobject.notify(title=message["title"], body=message["text"])
 
     def format(self, build):
-        colors = [
-                "#36A64F", # success
-                "#F1E903", # warnings
-                "#DA0505", # failure
-                "#FFFFFF", # skipped
-                "#000000", # exception
-                "#FFFFFF", # retry
-                "#D02CA9", # cancelled
-                ]
-
         if "environment" in build["properties"]:
             msg = "{} environment".format(build["properties"]["environment"][0])
             if "build" in build["properties"]:
@@ -178,71 +169,30 @@ class SlackStatusPush(HttpStatusPushBase):
             else:
                 duration = "{}s".format(seconds)
 
-            text = "Build <{}|{}> of {}'s {} was {} in {}.".format(
-                    build["url"], build["buildid"],
+            text = "Build {} ({}) of {}'s {} was {} in {}.".format(
+                    build["number"], build["url"],
                     build["builder"]["name"],
                     msg,
                     results.Results[build["results"]],
                     duration,
                     )
-            fields = [
-                    {
-                        "title": "Build",
-                        "value": "<{}|{}>".format(build["url"], build["buildid"]),
-                        "short": True,
-                        },
-                    {
-                        "title": "Project",
-                        "value": build["builder"]["name"],
-                        "short": True,
-                        },
-                    {
-                        "title": "Build status",
-                        "value": results.Results[build["results"]],
-                        "short": True,
-                        },
-                    {
-                        "title": "Build duration",
-                        "value": duration,
-                        "short": True,
-                        },
-                    ]
-            if "environment" in build["properties"]:
-                fields.append({
-                        "title": "Environment",
-                        "value": build["properties"]["environment"][0],
-                        "short": True,
-                    })
-            if "build" in build["properties"]:
-                fields.append({
-                        "title": "Archive",
-                        "value": build["properties"]["build"][0],
-                        "short": True,
-                    })
-            attachments = [{
-                    "fallback": "",
-                    "color": colors[build["results"]],
-                    "fields": fields
-                    }]
         else:
-            text = "Build <{}|{}> of {}'s {} started.".format(
-                    build["url"], build["buildid"],
+            text = "Build {} ({}) of {}'s {} started.".format(
+                    build["number"], build["url"],
                     build["builder"]["name"],
                     msg,
                     )
-            attachments = []
-
         return {
                 "username": "Buildbot",
-                "icon_url": "http://docs.buildbot.net/current/_static/icon.png",
+                "image_url": "http://docs.buildbot.net/current/_static/icon.png",
                 "text": text,
-                "attachments": attachments,
+                "title": "",
                 }
 
-def configure_slack_push(c, secrets_file, builders):
-    c['services'].append(SlackStatusPush(
-        name="slack_status", builders=builders,
-        serverUrl=open(secrets_file + "/slack_webhook", "r").read().rstrip()))
+def configure_apprise_push(c, secrets_file, builders):
+    c['services'].append(AppriseStatusPush(
+        name="apprise_status", builders=builders,
+        appriseUrls=open(secrets_file + "/apprise_webhooks", "r").read().split("\n")))
 
 class XMPPStatusPush(HttpStatusPushBase):
     name = "XMPPStatusPush"
@@ -292,7 +242,7 @@ class XMPPStatusPush(HttpStatusPushBase):
                 duration = "{}s".format(seconds)
 
             text = "Build {} ( {} ) of {}'s {} was {} in {}.".format(
-                    build["buildid"], build["url"],
+                    build["number"], build["url"],
                     build["builder"]["name"],
                     msg,
                     results.Results[build["results"]],
@@ -300,7 +250,7 @@ class XMPPStatusPush(HttpStatusPushBase):
                     )
         else:
             text = "Build {} ( {} ) of {}'s {} started.".format(
-                    build["buildid"], build["url"],
+                    build["number"], build["url"],
                     build["builder"]["name"],
                     msg,
                     )
index 747e86e265f1d1cb29e6e795ae6636601cd50e76..fe3ff640ccc355f88cc5080d024da55e6cd59625 100644 (file)
@@ -70,7 +70,7 @@ def configure(c):
     configure_build(c, db_lock.access('exclusive'))
     configure_deploy(c, db_lock.access('exclusive'))
 
-    configure_slack_push(c, E.SECRETS_FILE, all_builder_names(c))
+    configure_apprise_push(c, E.SECRETS_FILE, all_builder_names(c))
     configure_xmpp_push(c, E.SECRETS_FILE, all_builder_names(c), E.XMPP_RECIPIENTS)
 
 def configure_build(c, lock):
index b99ebb5c020e6f2b3b1305b19f724765762457f2..46c783e48309b9b420532e1c850920e3b617d1cd 100644 (file)
@@ -44,7 +44,7 @@ def configure(c):
 
     configure_build(c)
     configure_deploy(c)
-    configure_slack_push(c, E.SECRETS_FILE, all_builder_names(c))
+    configure_apprise_push(c, E.SECRETS_FILE, all_builder_names(c))
 
 def configure_build(c):
     front_builder_name = "Front_build"
index e817ad0ac0a93a1ddd8abee7d748ec5ffdb50e44..a98648ebb7dd63eb97b5fdff4d6516d74bba8fa5 100644 (file)
@@ -82,7 +82,7 @@ def configure(c):
     c['schedulers'].append(force_scheduler("force_immae_eu", withbranch))
     c['schedulers'].append(force_scheduler("force_no_branch_immae_eu", withoutbranch, nobranch=True))
 
-    configure_slack_push(c, E.SECRETS_FILE, all_builder_names(c))
+    configure_apprise_push(c, E.SECRETS_FILE, all_builder_names(c))
     configure_xmpp_push(c, E.SECRETS_FILE, all_builder_names(c), E.XMPP_RECIPIENTS)
 
 BRANCH_TO_SYMFONY_ENV = {
index 926a6bb4c0e15d803b2c39ca175efe83e9b23f34..d5893fdea3ce175f4f07a0229d27848a35c88d60 100644 (file)
@@ -66,7 +66,7 @@ def configure(c):
     configure_build(c)
     configure_deploy(c)
 
-    configure_slack_push(c, E.SECRETS_FILE, all_builder_names(c))
+    configure_apprise_push(c, E.SECRETS_FILE, all_builder_names(c))
     configure_xmpp_push(c, E.SECRETS_FILE, all_builder_names(c), E.XMPP_RECIPIENTS)
 
 def configure_build(c):
index b321e03f50328fcf8f27c396701c753dfff503bc..ae00298039b2921ac74f97ab369fec84197a0255 100644 (file)
@@ -586,8 +586,7 @@ in
             };
           };
           nrdp_tokens = mkOption { type = listOf str; description = "Tokens allowed to push status update"; };
-          slack_url = mkOption { type = str; description = "Slack webhook url to push status update"; };
-          slack_channel = mkOption { type = str; description = "Slack channel to push status update"; };
+          apprise_urls = mkOption { type = str; description = "Apprise space-separated urls to push status update"; };
           netdata_aggregator = mkOption { type = str; description = "Url where netdata information should be sent"; };
           netdata_keys = mkOption { type = attrsOf str; description = "netdata host keys"; };
           contacts = mkOption { type = attrsOf unspecified; description = "Contact dicts to fill naemon objects"; };
index d02a4eff395c2cad0c7986cbc469dcfcdc1c5267..e34d4fa86d2f1f6a3f7a58511e2726ef1218dc25 100644 (file)
@@ -59,6 +59,7 @@ in {
     users.users.gitolite.packages = let
       python-packages = python-packages: with python-packages; [
         simplejson
+        apprise
         urllib3
         sleekxmpp
         pyyaml
index 76aa5572f96f5a8011fe00da18dc6b894835d687..4997e90deca6eb0633de38e3334e849a298a75fd 100644 (file)
@@ -21,24 +21,24 @@ in
   };
   notify-primary = {
     resources = {
-      USER206 = config.myEnv.monitoring.slack_channel;
-      USER207 = config.myEnv.monitoring.slack_url;
+      USER210 = config.myEnv.monitoring.apprise_urls;
     };
     commands = {
       # $OVE is to force naemon to run via shell instead of execve which fails here
       notify-host-by-email = "ADMINEMAIL=\"$ADMINEMAIL$\" SERVICENOTIFICATIONID=\"$SERVICENOTIFICATIONID$\" HOSTSTATE=\"$HOSTSTATE$\" HOSTOUTPUT=\"$HOSTOUTPUT$\" $USER2$/notify_by_email host \"$NOTIFICATIONTYPE$\" \"$HOSTALIAS$\" \"$LONGDATETIME$\" \"$CONTACTEMAIL$\" $OVE";
       # $OVE is to force naemon to run via shell instead of execve which fails here
       notify-service-by-email = "ADMINEMAIL=\"$ADMINEMAIL$\" SERVICENOTIFICATIONID=\"$SERVICENOTIFICATIONID$\" SERVICEDESC=\"$SERVICEDESC$\" SERVICESTATE=\"$SERVICESTATE$\" SERVICEOUTPUT=\"$SERVICEOUTPUT$\" $USER2$/notify_by_email service \"$NOTIFICATIONTYPE$\" \"$HOSTALIAS$\" \"$LONGDATETIME$\" \"$CONTACTEMAIL$\" $OVE";
-      notify-by-slack = "HOST=\"$HOSTALIAS$\" SERVICESTATE=\"$SERVICESTATE$\" SERVICEDESC=\"$SERVICEDESC$\" SERVICEOUTPUT=\"$SERVICEOUTPUT$\" $USER2$/notify_by_slack \"$ARG1$\" \"$ARG2$\"";
+      notify-host-by-apprise = "HOST=\"$HOSTALIAS$\" NOTIFICATIONTYPE=\"$NOTIFICATIONTYPE$\" HOSTSTATE=\"$HOSTSTATE$\" HOSTOUTPUT=\"$HOSTOUTPUT$\" $USER2$/notify_by_apprise host \"$ARG1$\"";
+      notify-service-by-apprise = "HOST=\"$HOSTALIAS$\" NOTIFICATIONTYPE=\"$NOTIFICATIONTYPE$\" SERVICESTATE=\"$SERVICESTATE$\" SERVICEDESC=\"$SERVICEDESC$\" SERVICEOUTPUT=\"$SERVICEOUTPUT$\" $USER2$/notify_by_apprise service \"$ARG1$\"";
     };
     chunk = ''
-      cp ${./plugins}/{notify_by_email,notify_by_slack} $out
-      patchShebangs $out/{notify_by_email,notify_by_slack}
+      cp ${./plugins}/{notify_by_email,notify_by_apprise} $out
+      patchShebangs $out/{notify_by_email,notify_by_apprise}
       wrapProgram $out/notify_by_email --prefix PATH : ${lib.makeBinPath [
         pkgs.mailutils
       ]}
-      wrapProgram $out/notify_by_slack --prefix PATH : ${lib.makeBinPath [
-        pkgs.curl pkgs.jq
+      wrapProgram $out/notify_by_apprise --prefix PATH : ${lib.makeBinPath [
+        pkgs.apprise
       ]}
     '';
   };
index 30bfe7320a0ddb9831ae9ac1f78248366cc678f7..7b6c84e1e4bdd696bd6e2c73a49b8af50e5d6b37 100644 (file)
@@ -4,8 +4,8 @@
     immae = config.myEnv.monitoring.contacts.immae // {
       use = "generic-contact";
       contactgroups = "admins";
-      host_notification_commands = "notify-host-by-email,notify-by-slack!$USER206$!$USER207$";
-      service_notification_commands = "notify-service-by-email,notify-by-slack!$USER206$!$USER207$";
+      host_notification_commands = "notify-host-by-email,notify-host-by-apprise!$USER210$";
+      service_notification_commands = "notify-service-by-email,notify-service-by-apprise!$USER210$";
     };
   };
   command = {
index 352ed7912536b50bd61e0411c23ffd282b4182c4..f82af2171ef70dab752315e8536cdbf339f95949 100644 (file)
@@ -5,6 +5,10 @@
     # Dummy host for testing
     # "dummy-host" = {
     #   alias = "dummy.host";
+    #   check_interval = "0.3";
+    #   max_check_attempts = "1";
+    #   flap_detection_enabled = "0";
+    #   notification_interval = "0.1";
     #   address = "dummy.host";
     #   use = "linux-server";
     #   check_command = "check_ok";
diff --git a/modules/private/monitoring/plugins/notify_by_apprise b/modules/private/monitoring/plugins/notify_by_apprise
new file mode 100755 (executable)
index 0000000..82bc5a3
--- /dev/null
@@ -0,0 +1,31 @@
+#!/usr/bin/env bash
+
+APPRISE_USERNAME="Naemon"
+APPRISE_USERICON="https://assets.immae.eu/monitoring.png"
+APPRISE_URLS=$(echo "$2" | sed -e "s/{username}/$APPRISE_USERNAME/g" -e "s@{image_url}@$APPRISE_USERICON@g")
+
+if [ "$SERVICESTATE" = "CRITICAL" ]; then
+  ICON="❗"
+elif [ "$SERVICESTATE" = "WARNING" ]; then
+  ICON="⚠️:"
+elif [ "$SERVICESTATE" = "OK" ]; then
+  ICON="✅"
+elif [ "$SERVICESTATE" = "UNKNOWN" ]; then
+  ICON="❓"
+elif [ "$HOSTSTATE" = "UP" ]; then
+  ICON="✅"
+elif [ "$HOSTSTATE" = "DOWN" ]; then
+  ICON="❗"
+elif [ "$HOSTSTATE" = "UNKNOWN" ]; then
+  ICON="❓"
+elif [ "$HOSTSTATE" = "UNREACHABLE" ]; then
+  ICON="❓"
+else
+  ICON="◻"
+fi
+
+if [ "$1" = "host" ]; then
+  apprise --title "${ICON} ${NOTIFICATIONTYPE} ${HOST} is ${HOSTSTATE}" --body "$HOSTOUTPUT" $APPRISE_URLS
+else
+  apprise --title "${ICON} ${NOTIFICATIONTYPE} ${SERVICEDESC} on ${HOST} is ${SERVICESTATE}" --body "$SERVICEOUTPUT" $APPRISE_URLS
+fi
index 6ca4c177a9fd0451fb03522e0c3a210fee0041cf..e640eb690d8ef241def1293b35b94c921567caf6 100644 (file)
@@ -55,8 +55,14 @@ let
       e.empteintesduweb.monitoranswers e.lr94.autosubscribe
       e.phpbbmodders.adduser ]);
   };
+  webhooks-bin-env = pkgs.buildEnv {
+    name = "webhook-env";
+    paths = [ pkgs.apprise ];
+    pathsToLink = [ "/bin" ];
+  };
   webhooks = pkgs.callPackage ./webhooks.nix {
     env = config.myEnv.tools.webhooks;
+    binEnv = webhooks-bin-env;
   };
   dmarc-reports = pkgs.callPackage ./dmarc_reports.nix {
     env = config.myEnv.tools.dmarc_reports;
@@ -305,6 +311,7 @@ in {
           "php_admin_value[open_basedir]" = builtins.concatStringsSep ":" [
             "/run/wrappers/bin/sendmail" landing "/tmp"
             config.secrets.fullPaths."webapps/webhooks"
+            "${webhooks-bin-env}/bin"
           ];
         };
         phpEnv = {
index c24c09c63b0938916efcafaa2ec6ec58a4e4c0e0..d21e73376a08a47faafa3060bdfcf8aec0edb4ad 100644 (file)
@@ -1,17 +1,17 @@
-{ lib, env }:
+{ lib, env, binEnv }:
 {
   keys = lib.attrsets.mapAttrs' (k: v:
     lib.nameValuePair "webapps/webhooks/${k}.php" {
     user = "wwwrun";
     group = "wwwrun";
     permissions = "0400";
-    text = v;
+    text = builtins.replaceStrings ["{{webhooks-bin-env}}"] [ "${binEnv}" ] v;
   }) env // lib.attrsets.mapAttrs' (k: v:
     lib.nameValuePair "webapps/webhooks/${k}/index.php" {
     user = "wwwrun";
     group = "wwwrun";
     permissions = "0400";
-    text = v;
+    text = builtins.replaceStrings ["{{webhooks-bin-env}}"] [ "${binEnv}" ] v;
   }) env // {
     "webapps/webhooks" = {
       isDir = true;