diff --git a/hc/api/migrations/0059_auto_20190314_1744.py b/hc/api/migrations/0059_auto_20190314_1744.py new file mode 100644 index 00000000..1b88d40c --- /dev/null +++ b/hc/api/migrations/0059_auto_20190314_1744.py @@ -0,0 +1,18 @@ +# Generated by Django 2.1.7 on 2019-03-14 17:44 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0058_auto_20190312_1716'), + ] + + operations = [ + migrations.AlterField( + model_name='channel', + name='kind', + field=models.CharField(choices=[('email', 'Email'), ('webhook', 'Webhook'), ('hipchat', 'HipChat'), ('slack', 'Slack'), ('pd', 'PagerDuty'), ('pagertree', 'PagerTree'), ('pagerteam', 'Pager Team'), ('po', 'Pushover'), ('pushbullet', 'Pushbullet'), ('opsgenie', 'OpsGenie'), ('victorops', 'VictorOps'), ('discord', 'Discord'), ('telegram', 'Telegram'), ('sms', 'SMS'), ('zendesk', 'Zendesk'), ('trello', 'Trello'), ('matrix', 'Matrix')], max_length=20), + ), + ] diff --git a/hc/api/models.py b/hc/api/models.py index 562a9138..002bbc71 100644 --- a/hc/api/models.py +++ b/hc/api/models.py @@ -33,6 +33,7 @@ CHANNEL_KINDS = (("email", "Email"), ("slack", "Slack"), ("pd", "PagerDuty"), ("pagertree", "PagerTree"), + ("pagerteam", "Pager Team"), ("po", "Pushover"), ("pushbullet", "Pushbullet"), ("opsgenie", "OpsGenie"), @@ -322,6 +323,8 @@ class Channel(models.Model): return transports.PagerDuty(self) elif self.kind == "pagertree": return transports.PagerTree(self) + elif self.kind == "pagerteam": + return transports.PagerTeam(self) elif self.kind == "victorops": return transports.VictorOps(self) elif self.kind == "pushbullet": diff --git a/hc/api/tests/test_notify.py b/hc/api/tests/test_notify.py index 641df621..ca5ffb19 100644 --- a/hc/api/tests/test_notify.py +++ b/hc/api/tests/test_notify.py @@ -294,6 +294,18 @@ class NotifyTestCase(BaseTestCase): payload = kwargs["json"] self.assertEqual(payload["event_type"], "trigger") + @patch("hc.api.transports.requests.request") + def test_pagerteam(self, mock_post): + self._setup_data("pagerteam", "123") + mock_post.return_value.status_code = 200 + + self.channel.notify(self.check) + assert Notification.objects.count() == 1 + + args, kwargs = mock_post.call_args + payload = kwargs["json"] + self.assertEqual(payload["event_type"], "trigger") + @patch("hc.api.transports.requests.request") def test_slack(self, mock_post): self._setup_data("slack", "123") diff --git a/hc/api/transports.py b/hc/api/transports.py index 72e21b05..a9797289 100644 --- a/hc/api/transports.py +++ b/hc/api/transports.py @@ -269,6 +269,24 @@ class PagerTree(HttpTransport): return self.post(url, json=payload, headers=headers) +class PagerTeam(HttpTransport): + def notify(self, check): + url = self.channel.value + headers = { + "Conent-Type": "application/json" + } + payload = { + "incident_key": str(check.code), + "event_type": "trigger" if check.status == "down" else "resolve", + "title": tmpl("pagerteam_title.html", check=check), + "description": tmpl("pagerteam_description.html", check=check), + "client": settings.SITE_NAME, + "client_url": settings.SITE_ROOT, + "tags": ",".join(check.tags_list()) + } + + return self.post(url, json=payload, headers=headers) + class Pushbullet(HttpTransport): def notify(self, check): diff --git a/hc/front/tests/test_add_pagerteam.py b/hc/front/tests/test_add_pagerteam.py new file mode 100644 index 00000000..530562c2 --- /dev/null +++ b/hc/front/tests/test_add_pagerteam.py @@ -0,0 +1,30 @@ +from hc.api.models import Channel +from hc.test import BaseTestCase + + +class AddPagerTeamTestCase(BaseTestCase): + url = "/integrations/add_pagerteam/" + + def test_instructions_work(self): + self.client.login(username="alice@example.org", password="password") + r = self.client.get(self.url) + self.assertContains(r, "Pager Team") + + def test_it_works(self): + form = {"value": "http://example.org"} + + self.client.login(username="alice@example.org", password="password") + r = self.client.post(self.url, form) + self.assertRedirects(r, "/integrations/") + + c = Channel.objects.get() + self.assertEqual(c.kind, "pagerteam") + self.assertEqual(c.value, "http://example.org") + self.assertEqual(c.project, self.project) + + def test_it_rejects_bad_url(self): + form = {"value": "not an URL"} + + self.client.login(username="alice@example.org", password="password") + r = self.client.post(self.url, form) + self.assertContains(r, "Enter a valid URL") diff --git a/hc/front/urls.py b/hc/front/urls.py index ef3fad79..4b9df356 100644 --- a/hc/front/urls.py +++ b/hc/front/urls.py @@ -24,6 +24,7 @@ channel_urls = [ path('add_pd/', views.add_pd, name="hc-add-pd"), path('add_pd//', views.add_pd, name="hc-add-pd-state"), path('add_pagertree/', views.add_pagertree, name="hc-add-pagertree"), + path('add_pagerteam/', views.add_pagerteam, name="hc-add-pagerteam"), path('add_slack/', views.add_slack, name="hc-add-slack"), path('add_slack_btn/', views.add_slack_btn, name="hc-add-slack-btn"), path('add_pushbullet/', views.add_pushbullet, name="hc-add-pushbullet"), diff --git a/hc/front/views.py b/hc/front/views.py index c7fd0737..9542fe7a 100644 --- a/hc/front/views.py +++ b/hc/front/views.py @@ -749,6 +749,23 @@ def add_pagertree(request): ctx = {"page": "channels", "form": form} return render(request, "integrations/add_pagertree.html", ctx) +@login_required +def add_pagerteam(request): + if request.method == "POST": + form = AddUrlForm(request.POST) + if form.is_valid(): + channel = Channel(project=request.project, kind="pagerteam") + channel.value = form.cleaned_data["value"] + channel.save() + + channel.assign_all_checks() + return redirect("hc-channels") + else: + form = AddUrlForm() + + ctx = {"page": "channels", "form": form} + return render(request, "integrations/add_pagerteam.html", ctx) + def add_slack(request): if not settings.SLACK_CLIENT_ID and not request.user.is_authenticated: diff --git a/static/img/integrations/pagerteam.png b/static/img/integrations/pagerteam.png new file mode 100644 index 00000000..fca74f68 Binary files /dev/null and b/static/img/integrations/pagerteam.png differ diff --git a/static/img/integrations/setup_pagerteam_1.png b/static/img/integrations/setup_pagerteam_1.png new file mode 100644 index 00000000..abd541a4 Binary files /dev/null and b/static/img/integrations/setup_pagerteam_1.png differ diff --git a/templates/front/channels.html b/templates/front/channels.html index 0064984e..79d7b44e 100644 --- a/templates/front/channels.html +++ b/templates/front/channels.html @@ -47,6 +47,8 @@ PagerDuty account {{ ch.pd_account }} {% elif ch.kind == "pagertree" %} PagerTree + {% elif ch.kind == "pagerteam" %} + Pager Team {% elif ch.kind == "opsgenie" %} OpsGenie {% elif ch.kind == "victorops" %} @@ -243,6 +245,15 @@ Add Integration +
  • + PagerTeam icon + +

    Pager Team

    +

    On-call scheduling, alerting, and incident tracking.

    + + Add Integration +
  • {% if enable_discord %}
  • This check has not received any pings yet. -{% endif %} \ No newline at end of file +{% endif %} diff --git a/templates/front/log.html b/templates/front/log.html index 45807b7a..00bc0483 100644 --- a/templates/front/log.html +++ b/templates/front/log.html @@ -107,6 +107,8 @@ Sent alert to PagerDuty {% elif event.channel.kind == "pagertree" %} Sent alert to PagerTree + {% elif event.channel.kind == "pagerteam" %} + Sent alert to Pager Team {% elif event.channel.kind == "opsgenie" %} Sent alert to OpsGenie {% elif event.channel.kind == "hipchat" %} diff --git a/templates/front/welcome.html b/templates/front/welcome.html index ea95c22e..5a7d43df 100644 --- a/templates/front/welcome.html +++ b/templates/front/welcome.html @@ -399,6 +399,13 @@ +
    +
    + Pager Team icon +

    Pager Team
    On-call rotations without limits

    +
    +
    + {% if enable_trello %}
    diff --git a/templates/integrations/add_pagerteam.html b/templates/integrations/add_pagerteam.html new file mode 100644 index 00000000..32b62780 --- /dev/null +++ b/templates/integrations/add_pagerteam.html @@ -0,0 +1,71 @@ +{% extends "base.html" %} +{% load humanize static hc_extras %} + +{% block title %}Add Pager Team - {% site_name %}{% endblock %} + + +{% block content %} +
    +
    +

    Pager Team

    + +

    You can integrate Pager Team + with {% site_name %} to alert your on-call rotation whenever a check goes down.

    + +

    Setup Guide

    + +
    +
    + 1 +

    + Log into your Pager Team account, select the rotation you wish to add this integration to. Click the Copy Webhook button. +

    +
    +
    + Click create integration button +
    +
    + +
    +
    + 2 +

    + Paste the Webhook URL below. Save the integration, and you are done! +

    +
    +
    + +

    Integration Settings

    + +
    + {% csrf_token %} +
    + +
    + + + {% if form.value.errors %} +
    + {{ form.value.errors|join:"" }} +
    + {% endif %} +
    +
    +
    +
    + +
    +
    +
    +
    +
    +{% endblock %} diff --git a/templates/integrations/pagerteam_description.html b/templates/integrations/pagerteam_description.html new file mode 100644 index 00000000..22c9e800 --- /dev/null +++ b/templates/integrations/pagerteam_description.html @@ -0,0 +1,5 @@ +{% load humanize %} +{{ check.name_then_code }} is {{ check.status|upper }}. +{% if check.status == "down" %} +Last ping was {{ check.last_ping|naturaltime }}. +{% endif %} diff --git a/templates/integrations/pagerteam_title.html b/templates/integrations/pagerteam_title.html new file mode 100644 index 00000000..29274284 --- /dev/null +++ b/templates/integrations/pagerteam_title.html @@ -0,0 +1 @@ +{{ check.name_then_code }} is {{ check.status|upper }}