forked from GithubBackups/healthchecks
Use PagerDuty Connect.
This commit is contained in:
parent
b3395f1314
commit
96e00df0ab
@ -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()
|
||||||
|
|
||||||
|
@ -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):
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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)
|
||||||
|
@ -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="alice@example.org", password="password")
|
self.client.login(username="alice@example.org", 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
|
session = self.client.session
|
||||||
form = {"value": "12345678901234567890123456789012"}
|
session["pd"] = "1234567890AB" # 12 characters
|
||||||
|
session.save()
|
||||||
|
|
||||||
self.client.login(username="alice@example.org", password="password")
|
self.client.login(username="alice@example.org", password="password")
|
||||||
r = self.client.post(self.url, form)
|
url = "/integrations/add_pd/1234567890AB/?service_key=123"
|
||||||
self.assertRedirects(r, "/integrations/")
|
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):
|
def test_it_validates_code(self):
|
||||||
form = {"value": " 123456 "}
|
session = self.client.session
|
||||||
|
session["pd"] = "1234567890AB" # 12 characters
|
||||||
|
session.save()
|
||||||
|
|
||||||
self.client.login(username="alice@example.org", password="password")
|
self.client.login(username="alice@example.org", password="password")
|
||||||
self.client.post(self.url, form)
|
url = "/integrations/add_pd/XXXXXXXXXXXX/?service_key=123"
|
||||||
|
r = self.client.get(url)
|
||||||
|
self.assertEqual(r.status_code, 400)
|
||||||
|
|
||||||
c = Channel.objects.get()
|
@override_settings(PD_VENDOR_KEY=None)
|
||||||
self.assertEqual(c.value, "123456")
|
def test_it_requires_vendor_key(self):
|
||||||
|
r = self.client.get("/integrations/add_pd/")
|
||||||
|
self.assertEqual(r.status_code, 404)
|
||||||
|
@ -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"),
|
||||||
|
@ -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):
|
||||||
|
@ -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
Normal file
BIN
static/img/integrations/pd_connect_button.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 6.5 KiB |
Binary file not shown.
Before Width: | Height: | Size: 67 KiB After Width: | Height: | Size: 93 KiB |
Binary file not shown.
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 90 KiB |
BIN
static/img/integrations/setup_pd_3.png
Normal file
BIN
static/img/integrations/setup_pd_3.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 36 KiB |
@ -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>
|
{% if ch.pd_account %}
|
||||||
{{ ch.value }}
|
<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" />
|
||||||
|
@ -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>
|
||||||
|
@ -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
|
<div class="jumbotron">
|
||||||
a well-known incident management system. It provides
|
{% if request.user.is_authenticated %}
|
||||||
alerting, on-call scheduling, escalation policies and incident tracking.
|
<p>If your team uses <a href="https://www.pagerduty.com">PagerDuty</a>,
|
||||||
If you use or plan on using PagerDuty, you can can integrate it
|
you can set up {% site_name %} to create a PagerDuty incident when
|
||||||
with your {% site_name %} account in few simple steps.</p>
|
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,
|
After {% if request.user.is_authenticated %}{% else %}logging in and{% endif %}
|
||||||
go to <strong>Configuration > Services</strong>,
|
clicking on "Alert with PagerDuty", you will be
|
||||||
and click on <strong>Add New Service</strong>.
|
asked to log into your PagerDuty account.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
|
||||||
Give it a descriptive name, and
|
|
||||||
for Integration Type select
|
|
||||||
<strong>Use our API directly</strong>.
|
|
||||||
</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
|
<p>
|
||||||
<strong>Integration Key</strong>, a long string
|
Next, PagerDuty will let set the name and escalation policy
|
||||||
of letters and digits.
|
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>
|
<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 class="col-sm-6">
|
||||||
|
<img
|
||||||
|
class="ai-guide-screenshot"
|
||||||
|
alt="Screenshot"
|
||||||
|
src="{% static 'img/integrations/setup_pd_3.png' %}">
|
||||||
</div>
|
</div>
|
||||||
</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>
|
|
||||||
</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>
|
|
||||||
</form>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user