Browse Source

Implement Pushover emergency alert cancellation when check goes up

master
Pēteris Caune 3 years ago
parent
commit
7fb64c8249
No known key found for this signature in database GPG Key ID: E28D7679E9A9EDE2
3 changed files with 48 additions and 8 deletions
  1. +5
    -0
      CHANGELOG.md
  2. +28
    -5
      hc/api/tests/test_notify_pushover.py
  3. +15
    -3
      hc/api/transports.py

+ 5
- 0
CHANGELOG.md View File

@ -1,6 +1,11 @@
# Changelog # Changelog
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
## v1.25.0 - Unreleased
### Improvements
- Implement Pushover emergency alert cancellation when check goes up
## v1.24.1 - 2021-11-10 ## v1.24.1 - 2021-11-10
### Bug Fixes ### Bug Fixes


+ 28
- 5
hc/api/tests/test_notify_pushover.py View File

@ -8,8 +8,10 @@ from django.utils.timezone import now
from hc.api.models import Channel, Check, Notification, TokenBucket from hc.api.models import Channel, Check, Notification, TokenBucket
from hc.test import BaseTestCase from hc.test import BaseTestCase
API = "https://api.pushover.net/1"
class NotifyTestCase(BaseTestCase):
class NotifyPushoverTestCase(BaseTestCase):
def _setup_data(self, value, status="down", email_verified=True): def _setup_data(self, value, status="down", email_verified=True):
self.check = Check(project=self.project) self.check = Check(project=self.project)
self.check.status = status self.check.status = status
@ -24,24 +26,27 @@ class NotifyTestCase(BaseTestCase):
self.channel.checks.add(self.check) self.channel.checks.add(self.check)
@patch("hc.api.transports.requests.request") @patch("hc.api.transports.requests.request")
def test_pushover(self, mock_post):
def test_it_works(self, mock_post):
self._setup_data("123|0") self._setup_data("123|0")
mock_post.return_value.status_code = 200 mock_post.return_value.status_code = 200
self.channel.notify(self.check) self.channel.notify(self.check)
assert Notification.objects.count() == 1
self.assertEqual(Notification.objects.count(), 1)
args, kwargs = mock_post.call_args args, kwargs = mock_post.call_args
self.assertEqual(args[1], API + "/messages.json")
payload = kwargs["data"] payload = kwargs["data"]
self.assertIn("DOWN", payload["title"]) self.assertIn("DOWN", payload["title"])
self.assertEqual(payload["tags"], self.check.unique_key)
@patch("hc.api.transports.requests.request") @patch("hc.api.transports.requests.request")
def test_pushover_up_priority(self, mock_post):
def test_it_supports_up_priority(self, mock_post):
self._setup_data("123|0|2", status="up") self._setup_data("123|0|2", status="up")
mock_post.return_value.status_code = 200 mock_post.return_value.status_code = 200
self.channel.notify(self.check) self.channel.notify(self.check)
assert Notification.objects.count() == 1
self.assertEqual(Notification.objects.count(), 1)
args, kwargs = mock_post.call_args args, kwargs = mock_post.call_args
payload = kwargs["data"] payload = kwargs["data"]
@ -63,3 +68,21 @@ class NotifyTestCase(BaseTestCase):
self.channel.notify(self.check) self.channel.notify(self.check)
n = Notification.objects.first() n = Notification.objects.first()
self.assertEqual(n.error, "Rate limit exceeded") self.assertEqual(n.error, "Rate limit exceeded")
@patch("hc.api.transports.requests.request")
def test_it_cancels_emergency_notification(self, mock_post):
self._setup_data("123|2|0", status="up")
mock_post.return_value.status_code = 200
self.channel.notify(self.check)
self.assertEqual(Notification.objects.count(), 1)
self.assertEqual(mock_post.call_count, 2)
cancel_args, cancel_kwargs = mock_post.call_args_list[0]
expected = "/receipts/cancel_by_tag/%s.json" % self.check.unique_key
self.assertEqual(cancel_args[1], API + expected)
up_args, up_kwargs = mock_post.call_args_list[1]
payload = up_kwargs["data"]
self.assertIn("UP", payload["title"])

+ 15
- 3
hc/api/transports.py View File

@ -380,25 +380,36 @@ class Pushbullet(HttpTransport):
class Pushover(HttpTransport): class Pushover(HttpTransport):
URL = "https://api.pushover.net/1/messages.json" URL = "https://api.pushover.net/1/messages.json"
CANCEL_TMPL = "https://api.pushover.net/1/receipts/cancel_by_tag/%s.json"
def notify(self, check): def notify(self, check):
pieces = self.channel.value.split("|") pieces = self.channel.value.split("|")
user_key, prio = pieces[0], pieces[1]
user_key, down_prio = pieces[0], pieces[1]
# The third element, if present, is the priority for "up" events # The third element, if present, is the priority for "up" events
if len(pieces) == 3 and check.status == "up":
prio = pieces[2]
up_prio = down_prio
if len(pieces) == 3:
up_prio = pieces[2]
from hc.api.models import TokenBucket from hc.api.models import TokenBucket
if not TokenBucket.authorize_pushover(user_key): if not TokenBucket.authorize_pushover(user_key):
return "Rate limit exceeded" return "Rate limit exceeded"
# If down events have the emergency priority,
# send a cancel call first
if check.status == "up" and down_prio == "2":
url = self.CANCEL_TMPL % check.unique_key
payload = {"token": settings.PUSHOVER_API_TOKEN}
self.post(url, data=payload)
others = self.checks().filter(status="down").exclude(code=check.code) others = self.checks().filter(status="down").exclude(code=check.code)
# list() executes the query, to avoid DB access while # list() executes the query, to avoid DB access while
# rendering a template # rendering a template
ctx = {"check": check, "down_checks": list(others)} ctx = {"check": check, "down_checks": list(others)}
text = tmpl("pushover_message.html", **ctx) text = tmpl("pushover_message.html", **ctx)
title = tmpl("pushover_title.html", **ctx) title = tmpl("pushover_title.html", **ctx)
prio = up_prio if check.status == "up" else down_prio
payload = { payload = {
"token": settings.PUSHOVER_API_TOKEN, "token": settings.PUSHOVER_API_TOKEN,
@ -407,6 +418,7 @@ class Pushover(HttpTransport):
"title": title, "title": title,
"html": 1, "html": 1,
"priority": int(prio), "priority": int(prio),
"tags": check.unique_key,
} }
# Emergency notification # Emergency notification


Loading…
Cancel
Save