Browse Source

Use PagerDuty Connect.

pull/133/head
Pēteris Caune 7 years ago
parent
commit
96e00df0ab
15 changed files with 182 additions and 90 deletions
  1. +16
    -0
      hc/api/models.py
  2. +14
    -0
      hc/api/tests/test_notify.py
  3. +3
    -2
      hc/api/transports.py
  4. +0
    -5
      hc/front/forms.py
  5. +22
    -12
      hc/front/tests/test_add_pd.py
  6. +1
    -0
      hc/front/urls.py
  7. +44
    -21
      hc/front/views.py
  8. +3
    -0
      hc/settings.py
  9. BIN
      static/img/integrations/pd_connect_button.png
  10. BIN
      static/img/integrations/setup_pd_1.png
  11. BIN
      static/img/integrations/setup_pd_2.png
  12. BIN
      static/img/integrations/setup_pd_3.png
  13. +8
    -2
      templates/front/channels.html
  14. +2
    -0
      templates/front/welcome.html
  15. +69
    -48
      templates/integrations/add_pd.html

+ 16
- 0
hc/api/models.py View File

@ -410,6 +410,22 @@ class Channel(models.Model):
tmpl = "https://api.hipchat.com/v2/room/%s/notification?auth_token=%s" tmpl = "https://api.hipchat.com/v2/room/%s/notification?auth_token=%s"
return tmpl % (doc["roomId"], doc.get("access_token")) return tmpl % (doc["roomId"], doc.get("access_token"))
@property
def pd_service_key(self):
assert self.kind == "pd"
if not self.value.startswith("{"):
return self.value
doc = json.loads(self.value)
return doc["service_key"]
@property
def pd_account(self):
assert self.kind == "pd"
if self.value.startswith("{"):
doc = json.loads(self.value)
return doc["account"]
def latest_notification(self): def latest_notification(self):
return Notification.objects.filter(channel=self).latest() return Notification.objects.filter(channel=self).latest()


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

@ -178,6 +178,20 @@ class NotifyTestCase(BaseTestCase):
args, kwargs = mock_post.call_args args, kwargs = mock_post.call_args
payload = kwargs["json"] payload = kwargs["json"]
self.assertEqual(payload["event_type"], "trigger") self.assertEqual(payload["event_type"], "trigger")
self.assertEqual(payload["service_key"], "123")
@patch("hc.api.transports.requests.request")
def test_pd_complex(self, mock_post):
self._setup_data("pd", json.dumps({"service_key": "456"}))
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")
self.assertEqual(payload["service_key"], "456")
@patch("hc.api.transports.requests.request") @patch("hc.api.transports.requests.request")
def test_slack(self, mock_post): def test_slack(self, mock_post):


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

@ -203,11 +203,12 @@ class PagerDuty(HttpTransport):
def notify(self, check): def notify(self, check):
description = tmpl("pd_description.html", check=check) description = tmpl("pd_description.html", check=check)
payload = { payload = {
"service_key": self.channel.value,
"vendor": settings.PD_VENDOR_KEY,
"service_key": self.channel.pd_service_key,
"incident_key": str(check.code), "incident_key": str(check.code),
"event_type": "trigger" if check.status == "down" else "resolve", "event_type": "trigger" if check.status == "down" else "resolve",
"description": description, "description": description,
"client": "healthchecks.io",
"client": settings.SITE_NAME,
"client_url": settings.SITE_ROOT "client_url": settings.SITE_ROOT
} }


+ 0
- 5
hc/front/forms.py View File

@ -31,11 +31,6 @@ class CronForm(forms.Form):
grace = forms.IntegerField(min_value=1, max_value=43200) grace = forms.IntegerField(min_value=1, max_value=43200)
class AddPdForm(forms.Form):
error_css_class = "has-error"
value = forms.CharField(max_length=32)
class AddOpsGenieForm(forms.Form): class AddOpsGenieForm(forms.Form):
error_css_class = "has-error" error_css_class = "has-error"
value = forms.CharField(max_length=40) value = forms.CharField(max_length=40)


+ 22
- 12
hc/front/tests/test_add_pd.py View File

@ -1,32 +1,42 @@
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
@override_settings(PD_VENDOR_KEY="foo")
class AddPdTestCase(BaseTestCase): class AddPdTestCase(BaseTestCase):
url = "/integrations/add_pd/" url = "/integrations/add_pd/"
def test_instructions_work(self): def test_instructions_work(self):
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.assertContains(r, "incident management system")
self.assertContains(r, "If your team uses")
def test_it_works(self): def test_it_works(self):
# Integration key is 32 characters long
form = {"value": "12345678901234567890123456789012"}
session = self.client.session
session["pd"] = "1234567890AB" # 12 characters
session.save()
self.client.login(username="[email protected]", password="password") self.client.login(username="[email protected]", password="password")
r = self.client.post(self.url, form)
self.assertRedirects(r, "/integrations/")
url = "/integrations/add_pd/1234567890AB/?service_key=123"
r = self.client.get(url)
self.assertEqual(r.status_code, 302)
c = Channel.objects.get() c = Channel.objects.get()
self.assertEqual(c.kind, "pd") self.assertEqual(c.kind, "pd")
self.assertEqual(c.value, "12345678901234567890123456789012")
self.assertEqual(c.pd_service_key, "123")
def test_it_trims_whitespace(self):
form = {"value": " 123456 "}
def test_it_validates_code(self):
session = self.client.session
session["pd"] = "1234567890AB" # 12 characters
session.save()
self.client.login(username="[email protected]", password="password") self.client.login(username="[email protected]", password="password")
self.client.post(self.url, form)
c = Channel.objects.get()
self.assertEqual(c.value, "123456")
url = "/integrations/add_pd/XXXXXXXXXXXX/?service_key=123"
r = self.client.get(url)
self.assertEqual(r.status_code, 400)
@override_settings(PD_VENDOR_KEY=None)
def test_it_requires_vendor_key(self):
r = self.client.get("/integrations/add_pd/")
self.assertEqual(r.status_code, 404)

+ 1
- 0
hc/front/urls.py View File

@ -16,6 +16,7 @@ channel_urls = [
url(r'^add_email/$', views.add_email, name="hc-add-email"), url(r'^add_email/$', views.add_email, name="hc-add-email"),
url(r'^add_webhook/$', views.add_webhook, name="hc-add-webhook"), url(r'^add_webhook/$', views.add_webhook, name="hc-add-webhook"),
url(r'^add_pd/$', views.add_pd, name="hc-add-pd"), url(r'^add_pd/$', views.add_pd, name="hc-add-pd"),
url(r'^add_pd/([\w]{12})/$', views.add_pd, name="hc-add-pd-state"),
url(r'^add_slack/$', views.add_slack, name="hc-add-slack"), url(r'^add_slack/$', views.add_slack, name="hc-add-slack"),
url(r'^add_slack_btn/$', views.add_slack_btn, name="hc-add-slack-btn"), url(r'^add_slack_btn/$', views.add_slack_btn, name="hc-add-slack-btn"),
url(r'^add_hipchat/$', views.add_hipchat, name="hc-add-hipchat"), url(r'^add_hipchat/$', views.add_hipchat, name="hc-add-hipchat"),


+ 44
- 21
hc/front/views.py View File

@ -24,7 +24,7 @@ from hc.api.models import (DEFAULT_GRACE, DEFAULT_TIMEOUT, Channel, Check,
Ping, Notification) Ping, Notification)
from hc.api.transports import Telegram from hc.api.transports import Telegram
from hc.front.forms import (AddWebhookForm, NameTagsForm, from hc.front.forms import (AddWebhookForm, NameTagsForm,
TimeoutForm, AddUrlForm, AddPdForm, AddEmailForm,
TimeoutForm, AddUrlForm, AddEmailForm,
AddOpsGenieForm, CronForm, AddSmsForm) AddOpsGenieForm, CronForm, AddSmsForm)
from hc.front.schemas import telegram_callback from hc.front.schemas import telegram_callback
from hc.lib import jsonschema from hc.lib import jsonschema
@ -99,6 +99,7 @@ def index(request):
"enable_discord": settings.DISCORD_CLIENT_ID is not None, "enable_discord": settings.DISCORD_CLIENT_ID is not None,
"enable_telegram": settings.TELEGRAM_TOKEN is not None, "enable_telegram": settings.TELEGRAM_TOKEN is not None,
"enable_sms": settings.TWILIO_AUTH is not None, "enable_sms": settings.TWILIO_AUTH is not None,
"enable_pd": settings.PD_VENDOR_KEY is not None,
"registration_open": settings.REGISTRATION_OPEN "registration_open": settings.REGISTRATION_OPEN
} }
@ -351,6 +352,7 @@ def channels(request):
"enable_discord": settings.DISCORD_CLIENT_ID is not None, "enable_discord": settings.DISCORD_CLIENT_ID is not None,
"enable_telegram": settings.TELEGRAM_TOKEN is not None, "enable_telegram": settings.TELEGRAM_TOKEN is not None,
"enable_sms": settings.TWILIO_AUTH is not None, "enable_sms": settings.TWILIO_AUTH is not None,
"enable_pd": settings.PD_VENDOR_KEY is not None,
"added": request.GET.get("added") "added": request.GET.get("added")
} }
@ -455,31 +457,13 @@ def add_webhook(request):
return render(request, "integrations/add_webhook.html", ctx) return render(request, "integrations/add_webhook.html", ctx)
@login_required
def add_pd(request):
if request.method == "POST":
form = AddPdForm(request.POST)
if form.is_valid():
channel = Channel(user=request.team.user, kind="pd")
channel.value = form.cleaned_data["value"]
channel.save()
channel.assign_all_checks()
return redirect("hc-channels")
else:
form = AddPdForm()
ctx = {"page": "channels", "form": form}
return render(request, "integrations/add_pd.html", ctx)
def _prepare_state(request, session_key): def _prepare_state(request, session_key):
state = get_random_string() state = get_random_string()
request.session[session_key] = state request.session[session_key] = state
return state return state
def _get_validated_code(request, session_key):
def _get_validated_code(request, session_key, key="code"):
if session_key not in request.session: if session_key not in request.session:
return None return None
@ -488,7 +472,46 @@ def _get_validated_code(request, session_key):
if session_state is None or session_state != request_state: if session_state is None or session_state != request_state:
return None return None
return request.GET.get("code")
return request.GET.get(key)
def add_pd(request, state=None):
if settings.PD_VENDOR_KEY is None:
raise Http404("pagerduty integration is not available")
if state and request.user.is_authenticated():
if "pd" not in request.session:
return HttpResponseBadRequest()
session_state = request.session.pop("pd")
if session_state != state:
return HttpResponseBadRequest()
if request.GET.get("error") == "cancelled":
messages.warning(request, "PagerDuty setup was cancelled")
return redirect("hc-channels")
channel = Channel()
channel.user = request.team.user
channel.kind = "pd"
channel.value = json.dumps({
"service_key": request.GET.get("service_key"),
"account": request.GET.get("account")
})
channel.save()
channel.assign_all_checks()
messages.success(request, "The PagerDuty integration has been added!")
return redirect("hc-channels")
state = _prepare_state(request, "pd")
callback = settings.SITE_ROOT + reverse("hc-add-pd-state", args=[state])
connect_url = "https://connect.pagerduty.com/connect?" + urlencode({
"vendor": settings.PD_VENDOR_KEY,
"callback": callback
})
ctx = {"page": "channels", "connect_url": connect_url}
return render(request, "integrations/add_pd.html", ctx)
def add_slack(request): def add_slack(request):


+ 3
- 0
hc/settings.py View File

@ -162,6 +162,9 @@ TWILIO_ACCOUNT = None
TWILIO_AUTH = None TWILIO_AUTH = None
TWILIO_FROM = None TWILIO_FROM = None
# PagerDuty
PD_VENDOR_KEY = None
if os.path.exists(os.path.join(BASE_DIR, "hc/local_settings.py")): if os.path.exists(os.path.join(BASE_DIR, "hc/local_settings.py")):
from .local_settings import * from .local_settings import *
else: else:


BIN
static/img/integrations/pd_connect_button.png View File

Before After
Width: 299  |  Height: 55  |  Size: 6.5 KiB

BIN
static/img/integrations/setup_pd_1.png View File

Before After
Width: 635  |  Height: 659  |  Size: 67 KiB Width: 750  |  Height: 581  |  Size: 93 KiB

BIN
static/img/integrations/setup_pd_2.png View File

Before After
Width: 679  |  Height: 261  |  Size: 25 KiB Width: 821  |  Height: 461  |  Size: 90 KiB

BIN
static/img/integrations/setup_pd_3.png View File

Before After
Width: 976  |  Height: 394  |  Size: 36 KiB

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

@ -40,8 +40,12 @@
<span class="channel-unconfirmed">(unconfirmed)</span> <span class="channel-unconfirmed">(unconfirmed)</span>
{% endif %} {% endif %}
{% elif ch.kind == "pd" %} {% elif ch.kind == "pd" %}
<span class="preposition">API key</span>
{{ ch.value }}
{% if ch.pd_account %}
<span class="preposition">account</span>
{{ ch.pd_account}},
{% endif %}
<span class="preposition">service key</span>
{{ ch.pd_service_key }}
{% elif ch.kind == "opsgenie" %} {% elif ch.kind == "opsgenie" %}
<span class="preposition">API key</span> <span class="preposition">API key</span>
{{ ch.value }} {{ ch.value }}
@ -215,6 +219,7 @@
<a href="{% url 'hc-add-telegram' %}" class="btn btn-primary">Add Integration</a> <a href="{% url 'hc-add-telegram' %}" class="btn btn-primary">Add Integration</a>
</li> </li>
{% endif %} {% endif %}
{% if enable_pd %}
<li> <li>
<img src="{% static 'img/integrations/pd.png' %}" <img src="{% static 'img/integrations/pd.png' %}"
class="icon" alt="PagerDuty icon" /> class="icon" alt="PagerDuty icon" />
@ -224,6 +229,7 @@
<a href="{% url 'hc-add-pd' %}" class="btn btn-primary">Add Integration</a> <a href="{% url 'hc-add-pd' %}" class="btn btn-primary">Add Integration</a>
</li> </li>
{% endif %}
<li> <li>
<img src="{% static 'img/integrations/hipchat.png' %}" <img src="{% static 'img/integrations/hipchat.png' %}"
class="icon" alt="HipChat icon" /> class="icon" alt="HipChat icon" />


+ 2
- 0
templates/front/welcome.html View File

@ -310,12 +310,14 @@
</tr> </tr>
{% endif %} {% endif %}
{% if enable_pd %}
<tr> <tr>
<td> <td>
<img width="22" height="22" alt="PagerDuty icon" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACwAAAAsCAIAAACR5s1WAAAEcElEQVRYw+1Ya0wcVRQ+9zEDKwtlWRp5tA1gISywtNH+tFqN0gZTU1MVTRtjfSRtolFTIw2VIi19aSPRvyZqfZDUxmj6UtKUaKPGGKOmlAUtYGpg2WV3h2IR2tmZe/wBuzu7MAPZwq4/PJlsZu+9c+53v3Pudx8EEcFg3Zc9Xd986/H0KWNjCVW3aISQPIejuqpyw73ra901cVXRnhRl7MCho/0Df8qyBEtpYU0rKylpeX2Pw5EbB2JgYHB3417GGKTKNE1rP3akrLRkBkRIGXvm+V2pRBDFceLTD202GwWA/QePpB4BAHDOd734CgDw7ss9/f2DS50HZjY+/rfP52flla6h4WFIn13p72fFq0onJ6fSCCIUUriiKAAIaTUuRJoRAABfXFlMEsRiOTIbDCHklphAJI6KgGS/mViu08CvxYQJY+E96++22WwJHWq69tXX5yWJz8uEKYjcshCTtZJNfTwzHD84zHd7PcfXUT6Do6K8XFFCrfv2yrKc4MRdXXWs/R1rMaSIYPYInbq2//zHiTU3lNtuGp6poN3346qaZ38SGpluCQCH21obm/aN+PxDw97oc/XqX53nL+x5bbeuC4uOuOX8RKFT1/ZfLr5azzI1Y5g2tJ8WGlu+1hvsLgAABASAQwdaNm1+JMNABufSuVOfA8BHn3T4fH6LnLDItZlfwgXhhgwQBDUGCCjIzOc4vSDpsiRLkhFEJBuQWHRkxQRGqhBxzmYIkXIEk5Zm7wtmgmDsa2Oz2e8xuPFTFWMeMFkmYj5mMUEwwjDGOiMz/+YYPSbLBBAgBMYHHVmF15msx1JCp4FLty+v9cdwEgQAj6eXMWp0qOt6j6evuqoSLJkgGx/aYlaXVfBPxrKp1Q//bnMmLrPqhNzb4SYMx67kAcCK4mJVVX2jo3SWPgohVq5cEVbV0UAwmXAQohOKE95syR4mhmaIZMKbfX0oR1dphGiRk5Pt8/uA0FlOIHdZTiAQSjIcVBbuHb+pE3LXy3XGcAABJmtgGHNWlv3dt98MBIIN2542ijSl9MyXn3HOdzy3M8nEBEBEQrmgsmYUq7mnM0BmZoZAHYAZQJCIVGCyiYlRnYB5FvxoS2EyRS09WIsVMQx04duORRUrALFAJjDG2aKLlQBTsbJaa4zzSEQEQ1gzgZZihYEeJ5U1613gdGVLa9vFrs64Y2c4vPOFl558/NGRkRGLLRYrvaPCrM7muEFlcemDGsLmoSHf6fzu+x/eOnow0TtjRQWFTc2tZJZ+xA32/rp6U53gQlcZoQsJBD7R8FhYVROFVVVPnTlHKZ3n1uC+B+v/C7vt/88d03FfyLlgyZlw5uUFgsE0M7F2TW16EbgqK+nGugdUVcX0WXNTI113153l5avTRYPdbi8qKiSIGAwpm7dslaRU3xgRQi50nuWcUwDId+Ydf/+9cDicyigAwMmOj6e3PLHL1MnJyYZtT127Np6aKJz+4mT0fEYSxGp42Lu/7XBPb++S8A/gcrneaG4qKio0lv8LIahMKT/4QwUAAAAASUVORK5CYII=" /> <img width="22" height="22" alt="PagerDuty icon" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACwAAAAsCAIAAACR5s1WAAAEcElEQVRYw+1Ya0wcVRQ+9zEDKwtlWRp5tA1gISywtNH+tFqN0gZTU1MVTRtjfSRtolFTIw2VIi19aSPRvyZqfZDUxmj6UtKUaKPGGKOmlAUtYGpg2WV3h2IR2tmZe/wBuzu7MAPZwq4/PJlsZu+9c+53v3Pudx8EEcFg3Zc9Xd986/H0KWNjCVW3aISQPIejuqpyw73ra901cVXRnhRl7MCho/0Df8qyBEtpYU0rKylpeX2Pw5EbB2JgYHB3417GGKTKNE1rP3akrLRkBkRIGXvm+V2pRBDFceLTD202GwWA/QePpB4BAHDOd734CgDw7ss9/f2DS50HZjY+/rfP52flla6h4WFIn13p72fFq0onJ6fSCCIUUriiKAAIaTUuRJoRAABfXFlMEsRiOTIbDCHklphAJI6KgGS/mViu08CvxYQJY+E96++22WwJHWq69tXX5yWJz8uEKYjcshCTtZJNfTwzHD84zHd7PcfXUT6Do6K8XFFCrfv2yrKc4MRdXXWs/R1rMaSIYPYInbq2//zHiTU3lNtuGp6poN3346qaZ38SGpluCQCH21obm/aN+PxDw97oc/XqX53nL+x5bbeuC4uOuOX8RKFT1/ZfLr5azzI1Y5g2tJ8WGlu+1hvsLgAABASAQwdaNm1+JMNABufSuVOfA8BHn3T4fH6LnLDItZlfwgXhhgwQBDUGCCjIzOc4vSDpsiRLkhFEJBuQWHRkxQRGqhBxzmYIkXIEk5Zm7wtmgmDsa2Oz2e8xuPFTFWMeMFkmYj5mMUEwwjDGOiMz/+YYPSbLBBAgBMYHHVmF15msx1JCp4FLty+v9cdwEgQAj6eXMWp0qOt6j6evuqoSLJkgGx/aYlaXVfBPxrKp1Q//bnMmLrPqhNzb4SYMx67kAcCK4mJVVX2jo3SWPgohVq5cEVbV0UAwmXAQohOKE95syR4mhmaIZMKbfX0oR1dphGiRk5Pt8/uA0FlOIHdZTiAQSjIcVBbuHb+pE3LXy3XGcAABJmtgGHNWlv3dt98MBIIN2542ijSl9MyXn3HOdzy3M8nEBEBEQrmgsmYUq7mnM0BmZoZAHYAZQJCIVGCyiYlRnYB5FvxoS2EyRS09WIsVMQx04duORRUrALFAJjDG2aKLlQBTsbJaa4zzSEQEQ1gzgZZihYEeJ5U1613gdGVLa9vFrs64Y2c4vPOFl558/NGRkRGLLRYrvaPCrM7muEFlcemDGsLmoSHf6fzu+x/eOnow0TtjRQWFTc2tZJZ+xA32/rp6U53gQlcZoQsJBD7R8FhYVROFVVVPnTlHKZ3n1uC+B+v/C7vt/88d03FfyLlgyZlw5uUFgsE0M7F2TW16EbgqK+nGugdUVcX0WXNTI113153l5avTRYPdbi8qKiSIGAwpm7dslaRU3xgRQi50nuWcUwDId+Ydf/+9cDicyigAwMmOj6e3PLHL1MnJyYZtT127Np6aKJz+4mT0fEYSxGp42Lu/7XBPb++S8A/gcrneaG4qKio0lv8LIahMKT/4QwUAAAAASUVORK5CYII=" />
</td> </td>
<td>Open and resolve incidents in <a href="https://www.pagerduty.com/">PagerDuty</a>.</td> <td>Open and resolve incidents in <a href="https://www.pagerduty.com/">PagerDuty</a>.</td>
</tr> </tr>
{% endif %}
<tr> <tr>
<td> <td>


+ 69
- 48
templates/integrations/add_pd.html View File

@ -9,27 +9,66 @@
<div class="col-sm-12"> <div class="col-sm-12">
<h1>PagerDuty</h1> <h1>PagerDuty</h1>
<p><a href="https://www.pagerduty.com/">PagerDuty</a> is
a well-known incident management system. It provides
alerting, on-call scheduling, escalation policies and incident tracking.
If you use or plan on using PagerDuty, you can can integrate it
with your {% site_name %} account in few simple steps.</p>
<div class="jumbotron">
{% if request.user.is_authenticated %}
<p>If your team uses <a href="https://www.pagerduty.com">PagerDuty</a>,
you can set up {% site_name %} to create a PagerDuty incident when
a check goes down, and resolve it when a check goes back up.</p>
<div class="text-center">
<div class="text-center">
<a href="{{ connect_url|safe }}">
<img
alt="Alert with PagerDuty"
height="55" width="299"
src="{% static 'img/integrations/pd_connect_button.png' %}" />
</a>
</div>
</div>
{% else %}
<p>
{% site_name %} is a <strong>free</strong> and
<a href="https://github.com/healthchecks/healthchecks">open source</a>
service for monitoring your cron jobs, background processes and
scheduled tasks. Before adding PagerDuty integration, please log into
{% site_name %}:</p>
<div class="text-center">
<form class="form-inline" action="{% url 'hc-login' %}" method="post">
{% csrf_token %}
<div class="form-group">
<div class="input-group input-group-lg">
<div class="input-group-addon">@</div>
<input
type="email"
class="form-control"
name="email"
autocomplete="email"
placeholder="Email">
</div>
</div>
<div class="form-group">
<button type="submit" class="btn btn-lg btn-primary pull-right">
Log In
</button>
</div>
</form>
</div>
{% endif %}
</div>
<h2>Setup Guide</h2> <h2>Setup Guide</h2>
<div class="row ai-step"> <div class="row ai-step">
<div class="col-sm-6"> <div class="col-sm-6">
<span class="step-no">1</span> <span class="step-no">1</span>
<p> <p>
Log into your PagerDuty account,
go to <strong>Configuration &gt; Services</strong>,
and click on <strong>Add New Service</strong>.
</p>
<p>
Give it a descriptive name, and
for Integration Type select
<strong>Use our API directly</strong>.
After {% if request.user.is_authenticated %}{% else %}logging in and{% endif %}
clicking on "Alert with PagerDuty", you will be
asked to log into your PagerDuty account.
</p> </p>
</div> </div>
<div class="col-sm-6"> <div class="col-sm-6">
<img <img
@ -38,13 +77,14 @@
src="{% static 'img/integrations/setup_pd_1.png' %}"> src="{% static 'img/integrations/setup_pd_1.png' %}">
</div> </div>
</div> </div>
<div class="row ai-step"> <div class="row ai-step">
<div class="col-sm-6"> <div class="col-sm-6">
<span class="step-no">2</span> <span class="step-no">2</span>
After adding the new service, take note of its
<strong>Integration Key</strong>, a long string
of letters and digits.
<p>
Next, PagerDuty will let set the name and escalation policy
for this integration.
</p>
</div> </div>
<div class="col-sm-6"> <div class="col-sm-6">
<img <img
@ -57,38 +97,19 @@
<div class="row ai-step"> <div class="row ai-step">
<div class="col-sm-6"> <div class="col-sm-6">
<span class="step-no">3</span> <span class="step-no">3</span>
<p>Paste the Integration Key down below. Save the integration, and it's done!</p>
</div>
</div>
<h2>Integration Settings</h2>
<form method="post" class="form-horizontal" action="{% url 'hc-add-pd' %}">
{% csrf_token %}
<div class="form-group {{ form.value.css_classes }}">
<label for="api-key" class="col-sm-2 control-label">Integration Key</label>
<div class="col-sm-4">
<input
id="api-key"
type="text"
class="form-control"
name="value"
placeholder=""
value="{{ form.value.value|default:"" }}">
{% if form.value.errors %}
<div class="help-block">
{{ form.value.errors|join:"" }}
</div>
{% endif %}
</div>
<p>
And that is all! You will then be redirected back to
"Integrations" page on {% site_name %} and see
the new integration!
</p>
</div> </div>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<button type="submit" class="btn btn-primary">Save Integration</button>
</div>
<div class="col-sm-6">
<img
class="ai-guide-screenshot"
alt="Screenshot"
src="{% static 'img/integrations/setup_pd_3.png' %}">
</div> </div>
</form>
</div>
</div> </div>
</div> </div>
{% endblock %} {% endblock %}

Loading…
Cancel
Save