forked from GithubBackups/healthchecks
Add Ping.exitstatus field, store received exit status values in db
Fixes #455
This commit is contained in:
parent
524d1a7375
commit
617bd92434
@ -16,6 +16,7 @@ All notable changes to this project will be documented in this file.
|
||||
- Require confirmation codes (sent to email) before sensitive actions
|
||||
- Implement WebAuthn two-factor authentication
|
||||
- Implement badge mode (up/down vs up/late/down) selector (#282)
|
||||
- Add Ping.exitstatus field, store client's reported exit status values (#455)
|
||||
|
||||
## v1.17.0 - 2020-10-14
|
||||
|
||||
|
23
hc/api/migrations/0076_auto_20201128_0951.py
Normal file
23
hc/api/migrations/0076_auto_20201128_0951.py
Normal file
@ -0,0 +1,23 @@
|
||||
# Generated by Django 3.1.2 on 2020-11-28 09:51
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('api', '0075_auto_20200805_1004'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='ping',
|
||||
name='exitstatus',
|
||||
field=models.SmallIntegerField(null=True),
|
||||
),
|
||||
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'), ('whatsapp', 'WhatsApp'), ('apprise', 'Apprise'), ('mattermost', 'Mattermost'), ('msteams', 'Microsoft Teams'), ('shell', 'Shell Command'), ('zulip', 'Zulip'), ('spike', 'Spike'), ('call', 'Phone Call'), ('linenotify', 'LINE Notify')], max_length=20),
|
||||
),
|
||||
]
|
@ -252,7 +252,7 @@ class Check(models.Model):
|
||||
|
||||
return result
|
||||
|
||||
def ping(self, remote_addr, scheme, method, ua, body, action):
|
||||
def ping(self, remote_addr, scheme, method, ua, body, action, exitstatus=None):
|
||||
now = timezone.now()
|
||||
|
||||
if self.status == "paused" and self.manual_resume:
|
||||
@ -299,6 +299,7 @@ class Check(models.Model):
|
||||
# If User-Agent is longer than 200 characters, truncate it:
|
||||
ping.ua = ua[:200]
|
||||
ping.body = body[: settings.PING_BODY_LIMIT]
|
||||
ping.exitstatus = exitstatus
|
||||
ping.save()
|
||||
|
||||
def downtimes(self, months=3):
|
||||
@ -351,6 +352,7 @@ class Ping(models.Model):
|
||||
method = models.CharField(max_length=10, blank=True)
|
||||
ua = models.CharField(max_length=200, blank=True)
|
||||
body = models.TextField(blank=True, null=True)
|
||||
exitstatus = models.SmallIntegerField(null=True)
|
||||
|
||||
def to_dict(self):
|
||||
return {
|
||||
|
@ -11,7 +11,7 @@ class PingTestCase(BaseTestCase):
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
self.check = Check.objects.create(project=self.project)
|
||||
self.url = "/ping/%s/" % self.check.code
|
||||
self.url = "/ping/%s" % self.check.code
|
||||
|
||||
def test_it_works(self):
|
||||
r = self.client.get(self.url)
|
||||
@ -26,6 +26,7 @@ class PingTestCase(BaseTestCase):
|
||||
self.assertEqual(ping.scheme, "http")
|
||||
self.assertEqual(ping.kind, None)
|
||||
self.assertEqual(ping.created, self.check.last_ping)
|
||||
self.assertIsNone(ping.exitstatus)
|
||||
|
||||
def test_it_changes_status_of_paused_check(self):
|
||||
self.check.status = "paused"
|
||||
@ -234,6 +235,7 @@ class PingTestCase(BaseTestCase):
|
||||
|
||||
ping = Ping.objects.latest("id")
|
||||
self.assertEqual(ping.kind, None)
|
||||
self.assertEqual(ping.exitstatus, 0)
|
||||
|
||||
def test_nonzero_exit_status_works(self):
|
||||
r = self.client.get("/ping/%s/123" % self.check.code)
|
||||
@ -244,3 +246,4 @@ class PingTestCase(BaseTestCase):
|
||||
|
||||
ping = Ping.objects.latest("id")
|
||||
self.assertEqual(ping.kind, "fail")
|
||||
self.assertEqual(ping.exitstatus, 123)
|
||||
|
@ -30,7 +30,7 @@ class BadChannelException(Exception):
|
||||
|
||||
@csrf_exempt
|
||||
@never_cache
|
||||
def ping(request, code, action="success", exitstatus=0):
|
||||
def ping(request, code, action="success", exitstatus=None):
|
||||
check = get_object_or_404(Check, code=code)
|
||||
|
||||
headers = request.META
|
||||
@ -41,13 +41,13 @@ def ping(request, code, action="success", exitstatus=0):
|
||||
ua = headers.get("HTTP_USER_AGENT", "")
|
||||
body = request.body.decode()
|
||||
|
||||
if exitstatus > 0:
|
||||
if exitstatus is not None and exitstatus > 0:
|
||||
action = "fail"
|
||||
|
||||
if check.methods == "POST" and method != "POST":
|
||||
action = "ign"
|
||||
|
||||
check.ping(remote_addr, scheme, method, ua, body, action)
|
||||
check.ping(remote_addr, scheme, method, ua, body, action, exitstatus)
|
||||
|
||||
response = HttpResponse("OK")
|
||||
response["Access-Control-Allow-Origin"] = "*"
|
||||
|
@ -59,3 +59,17 @@ class PingDetailsTestCase(BaseTestCase):
|
||||
self.client.login(username="alice@example.org", password="password")
|
||||
r = self.client.get("/checks/%s/pings/123/" % self.check.code)
|
||||
self.assertContains(r, "No additional information is", status_code=200)
|
||||
|
||||
def test_it_shows_nonzero_exitstatus(self):
|
||||
Ping.objects.create(owner=self.check, kind="fail", exitstatus=42)
|
||||
|
||||
self.client.login(username="alice@example.org", password="password")
|
||||
r = self.client.get(self.url)
|
||||
self.assertContains(r, "(failure, exit status 42)", status_code=200)
|
||||
|
||||
def test_it_shows_zero_exitstatus(self):
|
||||
Ping.objects.create(owner=self.check, exitstatus=0)
|
||||
|
||||
self.client.login(username="alice@example.org", password="password")
|
||||
r = self.client.get(self.url)
|
||||
self.assertContains(r, "(exit status 0)", status_code=200)
|
||||
|
@ -10,7 +10,9 @@
|
||||
<td class="date"></td>
|
||||
<td class="time"></td>
|
||||
<td class="event">
|
||||
{% if event.kind == "fail" %}
|
||||
{% if event.exitstatus > 0 %}
|
||||
<span class="label label-danger">Status {{ event.exitstatus }}</span>
|
||||
{% elif event.kind == "fail" %}
|
||||
<span class="label label-danger">Failure</span>
|
||||
{% elif event.kind == "start" %}
|
||||
<span class="label label-start">Started</span>
|
||||
|
@ -51,7 +51,9 @@
|
||||
<td class="date"></td>
|
||||
<td class="time"></td>
|
||||
<td class="event">
|
||||
{% if event.kind == "fail" %}
|
||||
{% if event.exitstatus %}
|
||||
<span class="label label-danger">Status {{ event.exitstatus }}</span>
|
||||
{% elif event.kind == "fail" %}
|
||||
<span class="label label-danger">Failure</span>
|
||||
{% elif event.kind == "start" %}
|
||||
<span class="label label-start">Started</span>
|
||||
|
@ -1,6 +1,10 @@
|
||||
<div class="modal-body">
|
||||
<h3>Ping #{{ ping.n }}
|
||||
{% if ping.kind == "fail" %}
|
||||
{% if ping.exitstatus > 0 %}
|
||||
<span class="text-danger">(failure, exit status {{ ping.exitstatus }})</span>
|
||||
{% elif ping.exitstatus == 0 %}
|
||||
<span class="text-success">(exit status 0)</span>
|
||||
{% elif ping.kind == "fail" %}
|
||||
<span class="text-danger">(received via the <code>/fail</code> endpoint)</span>
|
||||
{% elif ping.kind == "start" %}
|
||||
<span class="text-success">(received via the <code>/start</code> endpoint)</span>
|
||||
|
Loading…
x
Reference in New Issue
Block a user