From 8d5890d8837fb96424ce2b5bcce15e3327e5e9fb Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?P=C4=93teris=20Caune?=
Date: Fri, 29 Jan 2021 13:47:13 +0200
Subject: [PATCH] Add the OPSGENIE_ENABLED setting, rename OpsGenie -> Opsgenie
---
CHANGELOG.md | 1 +
docker/.env | 1 +
hc/api/models.py | 4 +-
hc/api/tests/test_notify.py | 52 ------------
hc/api/tests/test_notify_opsgenie.py | 85 +++++++++++++++++++
hc/api/transports.py | 5 +-
hc/front/forms.py | 2 +-
hc/front/tests/test_add_opsgenie.py | 9 +-
hc/front/views.py | 7 +-
hc/settings.py | 3 +
templates/docs/configuring_notifications.html | 2 +-
templates/docs/configuring_notifications.md | 2 +-
templates/docs/self_hosted_configuration.html | 3 +
templates/docs/self_hosted_configuration.md | 6 ++
templates/front/channels.html | 6 +-
templates/front/welcome.html | 4 +-
templates/integrations/add_opsgenie.html | 8 +-
17 files changed, 132 insertions(+), 68 deletions(-)
create mode 100644 hc/api/tests/test_notify_opsgenie.py
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7e82fbf9..03a6ed7f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -17,6 +17,7 @@ All notable changes to this project will be documented in this file.
- Add the SLACK_ENABLED setting (#471)
- Add the MATTERMOST_ENABLED setting (#471)
- Add the MSTEAMS_ENABLED setting (#471)
+- Add the OPSGENIE_ENABLED setting (#471)
## Bug Fixes
- Fix unwanted HTML escaping in SMS and WhatsApp notifications
diff --git a/docker/.env b/docker/.env
index 1aa7514f..997e94b1 100644
--- a/docker/.env
+++ b/docker/.env
@@ -27,6 +27,7 @@ MATRIX_HOMESERVER=
MATRIX_USER_ID=
MATTERMOST_ENABLED=True
MSTEAMS_ENABLED=True
+OPSGENIE_ENABLED=True
PD_VENDOR_KEY=
PING_BODY_LIMIT=10000
PING_EMAIL_DOMAIN=localhost
diff --git a/hc/api/models.py b/hc/api/models.py
index 76d66e29..46d3af5d 100644
--- a/hc/api/models.py
+++ b/hc/api/models.py
@@ -36,7 +36,7 @@ CHANNEL_KINDS = (
("pagerteam", "Pager Team"),
("po", "Pushover"),
("pushbullet", "Pushbullet"),
- ("opsgenie", "OpsGenie"),
+ ("opsgenie", "Opsgenie"),
("victorops", "VictorOps"),
("discord", "Discord"),
("telegram", "Telegram"),
@@ -446,7 +446,7 @@ class Channel(models.Model):
elif self.kind == "po":
return transports.Pushover(self)
elif self.kind == "opsgenie":
- return transports.OpsGenie(self)
+ return transports.Opsgenie(self)
elif self.kind == "discord":
return transports.Discord(self)
elif self.kind == "telegram":
diff --git a/hc/api/tests/test_notify.py b/hc/api/tests/test_notify.py
index 54d7be2d..e0ac7eea 100644
--- a/hc/api/tests/test_notify.py
+++ b/hc/api/tests/test_notify.py
@@ -79,58 +79,6 @@ class NotifyTestCase(BaseTestCase):
self.assertFalse(mock_post.called)
self.assertEqual(Notification.objects.count(), 0)
- @patch("hc.api.transports.requests.request")
- def test_opsgenie_with_legacy_value(self, mock_post):
- self._setup_data("opsgenie", "123")
- mock_post.return_value.status_code = 202
-
- self.channel.notify(self.check)
- n = Notification.objects.first()
- self.assertEqual(n.error, "")
-
- self.assertEqual(mock_post.call_count, 1)
- args, kwargs = mock_post.call_args
- self.assertIn("api.opsgenie.com", args[1])
- payload = kwargs["json"]
- self.assertIn("DOWN", payload["message"])
-
- @patch("hc.api.transports.requests.request")
- def test_opsgenie_up(self, mock_post):
- self._setup_data("opsgenie", "123", status="up")
- mock_post.return_value.status_code = 202
-
- self.channel.notify(self.check)
- n = Notification.objects.first()
- self.assertEqual(n.error, "")
-
- self.assertEqual(mock_post.call_count, 1)
- args, kwargs = mock_post.call_args
- method, url = args
- self.assertTrue(str(self.check.code) in url)
-
- @patch("hc.api.transports.requests.request")
- def test_opsgenie_with_json_value(self, mock_post):
- self._setup_data("opsgenie", json.dumps({"key": "456", "region": "eu"}))
- mock_post.return_value.status_code = 202
-
- self.channel.notify(self.check)
- n = Notification.objects.first()
- self.assertEqual(n.error, "")
-
- self.assertEqual(mock_post.call_count, 1)
- args, kwargs = mock_post.call_args
- self.assertIn("api.eu.opsgenie.com", args[1])
-
- @patch("hc.api.transports.requests.request")
- def test_opsgenie_returns_error(self, mock_post):
- self._setup_data("opsgenie", "123")
- mock_post.return_value.status_code = 403
- mock_post.return_value.json.return_value = {"message": "Nice try"}
-
- self.channel.notify(self.check)
- n = Notification.objects.first()
- self.assertEqual(n.error, 'Received status code 403 with a message: "Nice try"')
-
@patch("hc.api.transports.requests.request")
def test_victorops(self, mock_post):
self._setup_data("victorops", "123")
diff --git a/hc/api/tests/test_notify_opsgenie.py b/hc/api/tests/test_notify_opsgenie.py
new file mode 100644
index 00000000..c1b89a6f
--- /dev/null
+++ b/hc/api/tests/test_notify_opsgenie.py
@@ -0,0 +1,85 @@
+# coding: utf-8
+
+from datetime import timedelta as td
+import json
+from unittest.mock import patch
+
+from django.test.utils import override_settings
+from django.utils.timezone import now
+from hc.api.models import Channel, Check, Notification
+from hc.test import BaseTestCase
+
+
+class NotifyTestCase(BaseTestCase):
+ def _setup_data(self, value, status="down", email_verified=True):
+ self.check = Check(project=self.project)
+ self.check.status = status
+ self.check.last_ping = now() - td(minutes=61)
+ self.check.save()
+
+ self.channel = Channel(project=self.project)
+ self.channel.kind = "opsgenie"
+ self.channel.value = value
+ self.channel.email_verified = email_verified
+ self.channel.save()
+ self.channel.checks.add(self.check)
+
+ @patch("hc.api.transports.requests.request")
+ def test_opsgenie_with_legacy_value(self, mock_post):
+ self._setup_data("123")
+ mock_post.return_value.status_code = 202
+
+ self.channel.notify(self.check)
+ n = Notification.objects.first()
+ self.assertEqual(n.error, "")
+
+ self.assertEqual(mock_post.call_count, 1)
+ args, kwargs = mock_post.call_args
+ self.assertIn("api.opsgenie.com", args[1])
+ payload = kwargs["json"]
+ self.assertIn("DOWN", payload["message"])
+
+ @patch("hc.api.transports.requests.request")
+ def test_opsgenie_up(self, mock_post):
+ self._setup_data("123", status="up")
+ mock_post.return_value.status_code = 202
+
+ self.channel.notify(self.check)
+ n = Notification.objects.first()
+ self.assertEqual(n.error, "")
+
+ self.assertEqual(mock_post.call_count, 1)
+ args, kwargs = mock_post.call_args
+ method, url = args
+ self.assertTrue(str(self.check.code) in url)
+
+ @patch("hc.api.transports.requests.request")
+ def test_opsgenie_with_json_value(self, mock_post):
+ self._setup_data(json.dumps({"key": "456", "region": "eu"}))
+ mock_post.return_value.status_code = 202
+
+ self.channel.notify(self.check)
+ n = Notification.objects.first()
+ self.assertEqual(n.error, "")
+
+ self.assertEqual(mock_post.call_count, 1)
+ args, kwargs = mock_post.call_args
+ self.assertIn("api.eu.opsgenie.com", args[1])
+
+ @patch("hc.api.transports.requests.request")
+ def test_opsgenie_returns_error(self, mock_post):
+ self._setup_data("123")
+ mock_post.return_value.status_code = 403
+ mock_post.return_value.json.return_value = {"message": "Nice try"}
+
+ self.channel.notify(self.check)
+ n = Notification.objects.first()
+ self.assertEqual(n.error, 'Received status code 403 with a message: "Nice try"')
+
+ @override_settings(OPSGENIE_ENABLED=False)
+ def test_it_requires_opsgenie_enabled(self):
+ self._setup_data("123")
+ self.channel.notify(self.check)
+
+ n = Notification.objects.get()
+ self.assertEqual(n.error, "Opsgenie notifications are not enabled.")
diff --git a/hc/api/transports.py b/hc/api/transports.py
index d7d642b3..fa8b35e8 100644
--- a/hc/api/transports.py
+++ b/hc/api/transports.py
@@ -279,7 +279,7 @@ class HipChat(HttpTransport):
return True
-class OpsGenie(HttpTransport):
+class Opsgenie(HttpTransport):
@classmethod
def get_error(cls, response):
try:
@@ -288,6 +288,9 @@ class OpsGenie(HttpTransport):
pass
def notify(self, check):
+ if not settings.OPSGENIE_ENABLED:
+ return "Opsgenie notifications are not enabled."
+
headers = {
"Conent-Type": "application/json",
"Authorization": "GenieKey %s" % self.channel.opsgenie_key,
diff --git a/hc/front/forms.py b/hc/front/forms.py
index 818a90d8..3b7a93e0 100644
--- a/hc/front/forms.py
+++ b/hc/front/forms.py
@@ -99,7 +99,7 @@ class CronForm(forms.Form):
grace = forms.IntegerField(min_value=1, max_value=43200)
-class AddOpsGenieForm(forms.Form):
+class AddOpsgenieForm(forms.Form):
error_css_class = "has-error"
region = forms.ChoiceField(initial="us", choices=(("us", "US"), ("eu", "EU")))
key = forms.CharField(max_length=40)
diff --git a/hc/front/tests/test_add_opsgenie.py b/hc/front/tests/test_add_opsgenie.py
index 4c0773e8..aeba5f2d 100644
--- a/hc/front/tests/test_add_opsgenie.py
+++ b/hc/front/tests/test_add_opsgenie.py
@@ -1,10 +1,11 @@
import json
+from django.test.utils import override_settings
from hc.api.models import Channel
from hc.test import BaseTestCase
-class AddOpsGenieTestCase(BaseTestCase):
+class AddOpsgenieTestCase(BaseTestCase):
def setUp(self):
super().setUp()
self.url = "/projects/%s/add_opsgenie/" % self.project.code
@@ -56,3 +57,9 @@ class AddOpsGenieTestCase(BaseTestCase):
self.client.login(username="bob@example.org", password="password")
r = self.client.get(self.url)
self.assertEqual(r.status_code, 403)
+
+ @override_settings(OPSGENIE_ENABLED=False)
+ def test_it_handles_disabled_integration(self):
+ self.client.login(username="alice@example.org", password="password")
+ r = self.client.get(self.url)
+ self.assertEqual(r.status_code, 404)
diff --git a/hc/front/views.py b/hc/front/views.py
index f60baaaa..e6d948dd 100644
--- a/hc/front/views.py
+++ b/hc/front/views.py
@@ -297,6 +297,7 @@ def index(request):
"enable_matrix": settings.MATRIX_ACCESS_TOKEN is not None,
"enable_mattermost": settings.MATTERMOST_ENABLED is True,
"enable_msteams": settings.MSTEAMS_ENABLED is True,
+ "enable_opsgenie": settings.OPSGENIE_ENABLED is True,
"enable_pdc": settings.PD_VENDOR_KEY is not None,
"enable_pushbullet": settings.PUSHBULLET_CLIENT_ID is not None,
"enable_pushover": settings.PUSHOVER_API_TOKEN is not None,
@@ -767,6 +768,7 @@ def channels(request, code):
"enable_matrix": settings.MATRIX_ACCESS_TOKEN is not None,
"enable_mattermost": settings.MATTERMOST_ENABLED is True,
"enable_msteams": settings.MSTEAMS_ENABLED is True,
+ "enable_opsgenie": settings.OPSGENIE_ENABLED is True,
"enable_pdc": settings.PD_VENDOR_KEY is not None,
"enable_pushbullet": settings.PUSHBULLET_CLIENT_ID is not None,
"enable_pushover": settings.PUSHOVER_API_TOKEN is not None,
@@ -1432,12 +1434,13 @@ def add_pushover(request, code):
return render(request, "integrations/add_pushover.html", ctx)
+@require_setting("OPSGENIE_ENABLED")
@login_required
def add_opsgenie(request, code):
project = _get_rw_project_for_user(request, code)
if request.method == "POST":
- form = forms.AddOpsGenieForm(request.POST)
+ form = forms.AddOpsgenieForm(request.POST)
if form.is_valid():
channel = Channel(project=project, kind="opsgenie")
v = {"region": form.cleaned_data["region"], "key": form.cleaned_data["key"]}
@@ -1447,7 +1450,7 @@ def add_opsgenie(request, code):
channel.assign_all_checks()
return redirect("hc-channels", project.code)
else:
- form = forms.AddOpsGenieForm()
+ form = forms.AddOpsgenieForm()
ctx = {"page": "channels", "project": project, "form": form}
return render(request, "integrations/add_opsgenie.html", ctx)
diff --git a/hc/settings.py b/hc/settings.py
index fd656ce8..20876cdc 100644
--- a/hc/settings.py
+++ b/hc/settings.py
@@ -205,6 +205,9 @@ MATTERMOST_ENABLED = envbool("MATTERMOST_ENABLED", "True")
# MS Teams
MSTEAMS_ENABLED = envbool("MSTEAMS_ENABLED", "True")
+# Opsgenie
+OPSGENIE_ENABLED = envbool("OPSGENIE_ENABLED", "True")
+
# PagerDuty
PD_VENDOR_KEY = os.getenv("PD_VENDOR_KEY")
diff --git a/templates/docs/configuring_notifications.html b/templates/docs/configuring_notifications.html
index fcedaeb4..32281c32 100644
--- a/templates/docs/configuring_notifications.html
+++ b/templates/docs/configuring_notifications.html
@@ -34,7 +34,7 @@ The "unused" sends from one month do not carry over to the next month.
If you want to receive repeated notifications for as long as a particular check is
down, you have a few different options:
-- If you use an incident management system (PagerDuty, VictorOps, OpsGenie,
+
- If you use an incident management system (PagerDuty, VictorOps, Opsgenie,
PagerTree), you can set up escalation rules there.
- Use the Pushover integration with the "Emergency" priority. Pushover will
play a loud notification sound on your phone every 5 minutes until the notification
diff --git a/templates/docs/configuring_notifications.md b/templates/docs/configuring_notifications.md
index d72ba607..51e4edf3 100644
--- a/templates/docs/configuring_notifications.md
+++ b/templates/docs/configuring_notifications.md
@@ -44,7 +44,7 @@ When an account exceeds its monthly limit, SITE_NAME will:
If you want to receive repeated notifications for as long as a particular check is
down, you have a few different options:
-* If you use an **incident management system** (PagerDuty, VictorOps, OpsGenie,
+* If you use an **incident management system** (PagerDuty, VictorOps, Opsgenie,
PagerTree), you can set up escalation rules there.
* Use the **Pushover** integration with the "Emergency" priority. Pushover will
play a loud notification sound on your phone every 5 minutes until the notification
diff --git a/templates/docs/self_hosted_configuration.html b/templates/docs/self_hosted_configuration.html
index d8e68a06..cbed0a4b 100644
--- a/templates/docs/self_hosted_configuration.html
+++ b/templates/docs/self_hosted_configuration.html
@@ -146,6 +146,9 @@ integration.
MSTEAMS_ENABLED
Default: True
A boolean that turns on/off the MS Teams integration. Enabled by default.
+OPSGENIE_ENABLED
+Default: True
+A boolean that turns on/off the Opsgenie integration. Enabled by default.
PD_VENDOR_KEY
Default: None
PagerDuty vendor key,
diff --git a/templates/docs/self_hosted_configuration.md b/templates/docs/self_hosted_configuration.md
index 57460fb5..455cd4fe 100644
--- a/templates/docs/self_hosted_configuration.md
+++ b/templates/docs/self_hosted_configuration.md
@@ -242,6 +242,12 @@ Default: `True`
A boolean that turns on/off the MS Teams integration. Enabled by default.
+## `OPSGENIE_ENABLED` {: #OPSGENIE_ENABLED }
+
+Default: `True`
+
+A boolean that turns on/off the Opsgenie integration. Enabled by default.
+
## `PD_VENDOR_KEY` {: #PD_VENDOR_KEY }
Default: `None`
diff --git a/templates/front/channels.html b/templates/front/channels.html
index 19ed3429..47a25e50 100644
--- a/templates/front/channels.html
+++ b/templates/front/channels.html
@@ -287,14 +287,16 @@
{% endif %}
+ {% if enable_opsgenie %}
-
+ class="icon" alt="Opsgenie icon" />
-
OpsGenie
+ Opsgenie
Alerting & Incident Management Solution for Dev & Ops.
Add Integration
+ {% endif %}
-
{% endif %}
+ {% if enable_opsgenie %}
- OpsGenie
+ Opsgenie
{% trans "Incident Management" %}
+ {% endif %}
{% if enable_pdc %}
diff --git a/templates/integrations/add_opsgenie.html b/templates/integrations/add_opsgenie.html
index 79940f04..b3a5286a 100644
--- a/templates/integrations/add_opsgenie.html
+++ b/templates/integrations/add_opsgenie.html
@@ -1,15 +1,15 @@
{% extends "base.html" %}
{% load humanize static hc_extras %}
-{% block title %}OpsGenie Integration for {{ site_name }}{% endblock %}
+{% block title %}Opsgenie Integration for {{ site_name }}{% endblock %}
{% block content %}
-
OpsGenie
+
Opsgenie
-
OpsGenie provides
+
Opsgenie provides
alerting, on-call scheduling, escalation policies, and incident tracking.
You can integrate it with your {{ site_name }} account in a few
simple steps.
@@ -19,7 +19,7 @@
- Log into your OpsGenie account,
+ Log into your Opsgenie account,
select a team, and go to the team's
Integrations › Add integration page.