Browse Source

Add the OPSGENIE_ENABLED setting, rename OpsGenie -> Opsgenie

pull/474/head
Pēteris Caune 4 years ago
parent
commit
8d5890d883
No known key found for this signature in database GPG Key ID: E28D7679E9A9EDE2
17 changed files with 132 additions and 68 deletions
  1. +1
    -0
      CHANGELOG.md
  2. +1
    -0
      docker/.env
  3. +2
    -2
      hc/api/models.py
  4. +0
    -52
      hc/api/tests/test_notify.py
  5. +85
    -0
      hc/api/tests/test_notify_opsgenie.py
  6. +4
    -1
      hc/api/transports.py
  7. +1
    -1
      hc/front/forms.py
  8. +8
    -1
      hc/front/tests/test_add_opsgenie.py
  9. +5
    -2
      hc/front/views.py
  10. +3
    -0
      hc/settings.py
  11. +1
    -1
      templates/docs/configuring_notifications.html
  12. +1
    -1
      templates/docs/configuring_notifications.md
  13. +3
    -0
      templates/docs/self_hosted_configuration.html
  14. +6
    -0
      templates/docs/self_hosted_configuration.md
  15. +4
    -2
      templates/front/channels.html
  16. +3
    -1
      templates/front/welcome.html
  17. +4
    -4
      templates/integrations/add_opsgenie.html

+ 1
- 0
CHANGELOG.md View File

@ -17,6 +17,7 @@ All notable changes to this project will be documented in this file.
- Add the SLACK_ENABLED setting (#471) - Add the SLACK_ENABLED setting (#471)
- Add the MATTERMOST_ENABLED setting (#471) - Add the MATTERMOST_ENABLED setting (#471)
- Add the MSTEAMS_ENABLED setting (#471) - Add the MSTEAMS_ENABLED setting (#471)
- Add the OPSGENIE_ENABLED setting (#471)
## Bug Fixes ## Bug Fixes
- Fix unwanted HTML escaping in SMS and WhatsApp notifications - Fix unwanted HTML escaping in SMS and WhatsApp notifications


+ 1
- 0
docker/.env View File

@ -27,6 +27,7 @@ MATRIX_HOMESERVER=
MATRIX_USER_ID= MATRIX_USER_ID=
MATTERMOST_ENABLED=True MATTERMOST_ENABLED=True
MSTEAMS_ENABLED=True MSTEAMS_ENABLED=True
OPSGENIE_ENABLED=True
PD_VENDOR_KEY= PD_VENDOR_KEY=
PING_BODY_LIMIT=10000 PING_BODY_LIMIT=10000
PING_EMAIL_DOMAIN=localhost PING_EMAIL_DOMAIN=localhost


+ 2
- 2
hc/api/models.py View File

@ -36,7 +36,7 @@ CHANNEL_KINDS = (
("pagerteam", "Pager Team"), ("pagerteam", "Pager Team"),
("po", "Pushover"), ("po", "Pushover"),
("pushbullet", "Pushbullet"), ("pushbullet", "Pushbullet"),
("opsgenie", "OpsGenie"),
("opsgenie", "Opsgenie"),
("victorops", "VictorOps"), ("victorops", "VictorOps"),
("discord", "Discord"), ("discord", "Discord"),
("telegram", "Telegram"), ("telegram", "Telegram"),
@ -446,7 +446,7 @@ class Channel(models.Model):
elif self.kind == "po": elif self.kind == "po":
return transports.Pushover(self) return transports.Pushover(self)
elif self.kind == "opsgenie": elif self.kind == "opsgenie":
return transports.OpsGenie(self)
return transports.Opsgenie(self)
elif self.kind == "discord": elif self.kind == "discord":
return transports.Discord(self) return transports.Discord(self)
elif self.kind == "telegram": elif self.kind == "telegram":


+ 0
- 52
hc/api/tests/test_notify.py View File

@ -79,58 +79,6 @@ class NotifyTestCase(BaseTestCase):
self.assertFalse(mock_post.called) self.assertFalse(mock_post.called)
self.assertEqual(Notification.objects.count(), 0) 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") @patch("hc.api.transports.requests.request")
def test_victorops(self, mock_post): def test_victorops(self, mock_post):
self._setup_data("victorops", "123") self._setup_data("victorops", "123")


+ 85
- 0
hc/api/tests/test_notify_opsgenie.py View File

@ -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.")

+ 4
- 1
hc/api/transports.py View File

@ -279,7 +279,7 @@ class HipChat(HttpTransport):
return True return True
class OpsGenie(HttpTransport):
class Opsgenie(HttpTransport):
@classmethod @classmethod
def get_error(cls, response): def get_error(cls, response):
try: try:
@ -288,6 +288,9 @@ class OpsGenie(HttpTransport):
pass pass
def notify(self, check): def notify(self, check):
if not settings.OPSGENIE_ENABLED:
return "Opsgenie notifications are not enabled."
headers = { headers = {
"Conent-Type": "application/json", "Conent-Type": "application/json",
"Authorization": "GenieKey %s" % self.channel.opsgenie_key, "Authorization": "GenieKey %s" % self.channel.opsgenie_key,


+ 1
- 1
hc/front/forms.py View File

@ -99,7 +99,7 @@ class CronForm(forms.Form):
grace = forms.IntegerField(min_value=1, max_value=43200) grace = forms.IntegerField(min_value=1, max_value=43200)
class AddOpsGenieForm(forms.Form):
class AddOpsgenieForm(forms.Form):
error_css_class = "has-error" error_css_class = "has-error"
region = forms.ChoiceField(initial="us", choices=(("us", "US"), ("eu", "EU"))) region = forms.ChoiceField(initial="us", choices=(("us", "US"), ("eu", "EU")))
key = forms.CharField(max_length=40) key = forms.CharField(max_length=40)


+ 8
- 1
hc/front/tests/test_add_opsgenie.py View File

@ -1,10 +1,11 @@
import json import json
from django.test.utils import override_settings
from hc.api.models import Channel from hc.api.models import Channel
from hc.test import BaseTestCase from hc.test import BaseTestCase
class AddOpsGenieTestCase(BaseTestCase):
class AddOpsgenieTestCase(BaseTestCase):
def setUp(self): def setUp(self):
super().setUp() super().setUp()
self.url = "/projects/%s/add_opsgenie/" % self.project.code self.url = "/projects/%s/add_opsgenie/" % self.project.code
@ -56,3 +57,9 @@ class AddOpsGenieTestCase(BaseTestCase):
self.client.login(username="[email protected]", password="password") self.client.login(username="[email protected]", password="password")
r = self.client.get(self.url) r = self.client.get(self.url)
self.assertEqual(r.status_code, 403) self.assertEqual(r.status_code, 403)
@override_settings(OPSGENIE_ENABLED=False)
def test_it_handles_disabled_integration(self):
self.client.login(username="[email protected]", password="password")
r = self.client.get(self.url)
self.assertEqual(r.status_code, 404)

+ 5
- 2
hc/front/views.py View File

@ -297,6 +297,7 @@ def index(request):
"enable_matrix": settings.MATRIX_ACCESS_TOKEN is not None, "enable_matrix": settings.MATRIX_ACCESS_TOKEN is not None,
"enable_mattermost": settings.MATTERMOST_ENABLED is True, "enable_mattermost": settings.MATTERMOST_ENABLED is True,
"enable_msteams": settings.MSTEAMS_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_pdc": settings.PD_VENDOR_KEY is not None,
"enable_pushbullet": settings.PUSHBULLET_CLIENT_ID is not None, "enable_pushbullet": settings.PUSHBULLET_CLIENT_ID is not None,
"enable_pushover": settings.PUSHOVER_API_TOKEN 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_matrix": settings.MATRIX_ACCESS_TOKEN is not None,
"enable_mattermost": settings.MATTERMOST_ENABLED is True, "enable_mattermost": settings.MATTERMOST_ENABLED is True,
"enable_msteams": settings.MSTEAMS_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_pdc": settings.PD_VENDOR_KEY is not None,
"enable_pushbullet": settings.PUSHBULLET_CLIENT_ID is not None, "enable_pushbullet": settings.PUSHBULLET_CLIENT_ID is not None,
"enable_pushover": settings.PUSHOVER_API_TOKEN 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) return render(request, "integrations/add_pushover.html", ctx)
@require_setting("OPSGENIE_ENABLED")
@login_required @login_required
def add_opsgenie(request, code): def add_opsgenie(request, code):
project = _get_rw_project_for_user(request, code) project = _get_rw_project_for_user(request, code)
if request.method == "POST": if request.method == "POST":
form = forms.AddOpsGenieForm(request.POST)
form = forms.AddOpsgenieForm(request.POST)
if form.is_valid(): if form.is_valid():
channel = Channel(project=project, kind="opsgenie") channel = Channel(project=project, kind="opsgenie")
v = {"region": form.cleaned_data["region"], "key": form.cleaned_data["key"]} v = {"region": form.cleaned_data["region"], "key": form.cleaned_data["key"]}
@ -1447,7 +1450,7 @@ def add_opsgenie(request, code):
channel.assign_all_checks() channel.assign_all_checks()
return redirect("hc-channels", project.code) return redirect("hc-channels", project.code)
else: else:
form = forms.AddOpsGenieForm()
form = forms.AddOpsgenieForm()
ctx = {"page": "channels", "project": project, "form": form} ctx = {"page": "channels", "project": project, "form": form}
return render(request, "integrations/add_opsgenie.html", ctx) return render(request, "integrations/add_opsgenie.html", ctx)


+ 3
- 0
hc/settings.py View File

@ -205,6 +205,9 @@ MATTERMOST_ENABLED = envbool("MATTERMOST_ENABLED", "True")
# MS Teams # MS Teams
MSTEAMS_ENABLED = envbool("MSTEAMS_ENABLED", "True") MSTEAMS_ENABLED = envbool("MSTEAMS_ENABLED", "True")
# Opsgenie
OPSGENIE_ENABLED = envbool("OPSGENIE_ENABLED", "True")
# PagerDuty # PagerDuty
PD_VENDOR_KEY = os.getenv("PD_VENDOR_KEY") PD_VENDOR_KEY = os.getenv("PD_VENDOR_KEY")


+ 1
- 1
templates/docs/configuring_notifications.html View File

@ -34,7 +34,7 @@ The "unused" sends from one month do not carry over to the next month.</p>
<p>If you want to receive repeated notifications for as long as a particular check is <p>If you want to receive repeated notifications for as long as a particular check is
down, you have a few different options:</p> down, you have a few different options:</p>
<ul> <ul>
<li>If you use an <strong>incident management system</strong> (PagerDuty, VictorOps, OpsGenie,
<li>If you use an <strong>incident management system</strong> (PagerDuty, VictorOps, Opsgenie,
PagerTree), you can set up escalation rules there.</li> PagerTree), you can set up escalation rules there.</li>
<li>Use the <strong>Pushover</strong> integration with the "Emergency" priority. Pushover will <li>Use the <strong>Pushover</strong> integration with the "Emergency" priority. Pushover will
play a loud notification sound on your phone every 5 minutes until the notification play a loud notification sound on your phone every 5 minutes until the notification


+ 1
- 1
templates/docs/configuring_notifications.md View File

@ -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 If you want to receive repeated notifications for as long as a particular check is
down, you have a few different options: 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. PagerTree), you can set up escalation rules there.
* Use the **Pushover** integration with the "Emergency" priority. Pushover will * Use the **Pushover** integration with the "Emergency" priority. Pushover will
play a loud notification sound on your phone every 5 minutes until the notification play a loud notification sound on your phone every 5 minutes until the notification


+ 3
- 0
templates/docs/self_hosted_configuration.html View File

@ -146,6 +146,9 @@ integration.</p>
<h2 id="MSTEAMS_ENABLED"><code>MSTEAMS_ENABLED</code></h2> <h2 id="MSTEAMS_ENABLED"><code>MSTEAMS_ENABLED</code></h2>
<p>Default: <code>True</code></p> <p>Default: <code>True</code></p>
<p>A boolean that turns on/off the MS Teams integration. Enabled by default.</p> <p>A boolean that turns on/off the MS Teams integration. Enabled by default.</p>
<h2 id="OPSGENIE_ENABLED"><code>OPSGENIE_ENABLED</code></h2>
<p>Default: <code>True</code></p>
<p>A boolean that turns on/off the Opsgenie integration. Enabled by default.</p>
<h2 id="PD_VENDOR_KEY"><code>PD_VENDOR_KEY</code></h2> <h2 id="PD_VENDOR_KEY"><code>PD_VENDOR_KEY</code></h2>
<p>Default: <code>None</code></p> <p>Default: <code>None</code></p>
<p><a href="https://www.pagerduty.com/">PagerDuty</a> vendor key, <p><a href="https://www.pagerduty.com/">PagerDuty</a> vendor key,


+ 6
- 0
templates/docs/self_hosted_configuration.md View File

@ -242,6 +242,12 @@ Default: `True`
A boolean that turns on/off the MS Teams integration. Enabled by default. 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 } ## `PD_VENDOR_KEY` {: #PD_VENDOR_KEY }
Default: `None` Default: `None`


+ 4
- 2
templates/front/channels.html View File

@ -287,14 +287,16 @@
</li> </li>
{% endif %} {% endif %}
{% if enable_opsgenie %}
<li> <li>
<img src="{% static 'img/integrations/opsgenie.png' %}" <img src="{% static 'img/integrations/opsgenie.png' %}"
class="icon" alt="OpsGenie icon" />
class="icon" alt="Opsgenie icon" />
<h2>OpsGenie</h2>
<h2>Opsgenie</h2>
<p> Alerting &amp; Incident Management Solution for Dev &amp; Ops.</p> <p> Alerting &amp; Incident Management Solution for Dev &amp; Ops.</p>
<a href="{% url 'hc-add-opsgenie' project.code %}" class="btn btn-primary">Add Integration</a> <a href="{% url 'hc-add-opsgenie' project.code %}" class="btn btn-primary">Add Integration</a>
</li> </li>
{% endif %}
<li> <li>
<img src="{% static 'img/integrations/pd.png' %}" <img src="{% static 'img/integrations/pd.png' %}"


+ 3
- 1
templates/front/welcome.html View File

@ -484,15 +484,17 @@
</div> </div>
{% endif %} {% endif %}
{% if enable_opsgenie %}
<div class="col-lg-2 col-md-3 col-sm-4 col-xs-6"> <div class="col-lg-2 col-md-3 col-sm-4 col-xs-6">
<div class="integration"> <div class="integration">
<img src="{% static 'img/integrations/opsgenie.png' %}" class="icon" alt="" /> <img src="{% static 'img/integrations/opsgenie.png' %}" class="icon" alt="" />
<h3> <h3>
OpsGenie<br>
Opsgenie<br>
<small>{% trans "Incident Management" %}</small> <small>{% trans "Incident Management" %}</small>
</h3> </h3>
</div> </div>
</div> </div>
{% endif %}
<div class="col-lg-2 col-md-3 col-sm-4 col-xs-6"> <div class="col-lg-2 col-md-3 col-sm-4 col-xs-6">
{% if enable_pdc %} {% if enable_pdc %}


+ 4
- 4
templates/integrations/add_opsgenie.html View File

@ -1,15 +1,15 @@
{% extends "base.html" %} {% extends "base.html" %}
{% load humanize static hc_extras %} {% load humanize static hc_extras %}
{% block title %}OpsGenie Integration for {{ site_name }}{% endblock %}
{% block title %}Opsgenie Integration for {{ site_name }}{% endblock %}
{% block content %} {% block content %}
<div class="row"> <div class="row">
<div class="col-sm-12"> <div class="col-sm-12">
<h1>OpsGenie</h1>
<h1>Opsgenie</h1>
<p><a href="https://www.opsgenie.com/">OpsGenie</a> provides
<p><a href="https://www.opsgenie.com/">Opsgenie</a> provides
alerting, on-call scheduling, escalation policies, and incident tracking. alerting, on-call scheduling, escalation policies, and incident tracking.
You can integrate it with your {{ site_name }} account in a few You can integrate it with your {{ site_name }} account in a few
simple steps.</p> simple steps.</p>
@ -19,7 +19,7 @@
<div class="col-sm-6"> <div class="col-sm-6">
<span class="step-no"></span> <span class="step-no"></span>
<p> <p>
Log into your OpsGenie account,
Log into your Opsgenie account,
select a team, and go to the team's select a team, and go to the team's
<strong>Integrations › Add integration</strong> page. <strong>Integrations › Add integration</strong> page.
</p> </p>


Loading…
Cancel
Save