forked from GithubBackups/healthchecks
Foundation for "fail" pings (cc: #151)
This commit is contained in:
parent
7f4a568bea
commit
3fc84ca0ff
23
hc/api/migrations/0040_auto_20180517_1336.py
Normal file
23
hc/api/migrations/0040_auto_20180517_1336.py
Normal file
@ -0,0 +1,23 @@
|
||||
# Generated by Django 2.0.4 on 2018-05-17 13:36
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('api', '0039_remove_check_last_ping_body'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='check',
|
||||
name='last_ping_was_fail',
|
||||
field=models.NullBooleanField(default=False),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='ping',
|
||||
name='fail',
|
||||
field=models.NullBooleanField(default=False),
|
||||
),
|
||||
]
|
@ -27,6 +27,8 @@ DEFAULT_GRACE = td(hours=1)
|
||||
CHECK_KINDS = (("simple", "Simple"),
|
||||
("cron", "Cron"))
|
||||
|
||||
PING_KINDS = (("", "OK"), ("fail", "Fail"))
|
||||
|
||||
CHANNEL_KINDS = (("email", "Email"),
|
||||
("webhook", "Webhook"),
|
||||
("hipchat", "HipChat"),
|
||||
@ -70,6 +72,7 @@ class Check(models.Model):
|
||||
tz = models.CharField(max_length=36, default="UTC")
|
||||
n_pings = models.IntegerField(default=0)
|
||||
last_ping = models.DateTimeField(null=True, blank=True)
|
||||
last_ping_was_fail = models.NullBooleanField(default=False)
|
||||
has_confirmation_link = models.BooleanField(default=False)
|
||||
alert_after = models.DateTimeField(null=True, blank=True, editable=False)
|
||||
status = models.CharField(max_length=6, choices=STATUSES, default="new")
|
||||
@ -121,6 +124,9 @@ class Check(models.Model):
|
||||
if self.status in ("new", "paused"):
|
||||
return self.status
|
||||
|
||||
if self.last_ping_was_fail:
|
||||
return "down"
|
||||
|
||||
if now is None:
|
||||
now = timezone.now()
|
||||
|
||||
@ -129,6 +135,11 @@ class Check(models.Model):
|
||||
def get_alert_after(self):
|
||||
""" Return the datetime when check potentially goes down. """
|
||||
|
||||
# For "fail" pings, sendalerts should the check right
|
||||
# after receiving the ping, without waiting for the grace time:
|
||||
if self.last_ping_was_fail:
|
||||
return self.last_ping
|
||||
|
||||
return self.get_grace_start() + self.grace
|
||||
|
||||
def in_grace_period(self):
|
||||
@ -182,9 +193,10 @@ class Check(models.Model):
|
||||
|
||||
return result
|
||||
|
||||
def ping(self, remote_addr, scheme, method, ua, body):
|
||||
def ping(self, remote_addr, scheme, method, ua, body, is_fail=False):
|
||||
self.n_pings = models.F("n_pings") + 1
|
||||
self.last_ping = timezone.now()
|
||||
self.last_ping_was_fail = is_fail
|
||||
self.has_confirmation_link = "confirm" in str(body).lower()
|
||||
self.alert_after = self.get_alert_after()
|
||||
if self.status in ("new", "paused"):
|
||||
@ -195,6 +207,7 @@ class Check(models.Model):
|
||||
|
||||
ping = Ping(owner=self)
|
||||
ping.n = self.n_pings
|
||||
ping.fail = is_fail
|
||||
ping.remote_addr = remote_addr
|
||||
ping.scheme = scheme
|
||||
ping.method = method
|
||||
@ -209,6 +222,7 @@ class Ping(models.Model):
|
||||
n = models.IntegerField(null=True)
|
||||
owner = models.ForeignKey(Check, models.CASCADE)
|
||||
created = models.DateTimeField(auto_now_add=True)
|
||||
fail = models.NullBooleanField(default=False)
|
||||
scheme = models.CharField(max_length=10, default="http")
|
||||
remote_addr = models.GenericIPAddressField(blank=True, null=True)
|
||||
method = models.CharField(max_length=10, blank=True)
|
||||
|
@ -87,3 +87,13 @@ class CheckModelTestCase(TestCase):
|
||||
|
||||
d = check.to_dict()
|
||||
self.assertEqual(d["next_ping"], "2000-01-01T01:00:00+00:00")
|
||||
|
||||
def test_status_checks_the_fail_flag(self):
|
||||
check = Check()
|
||||
check.status = "up"
|
||||
check.last_ping = timezone.now() - timedelta(minutes=5)
|
||||
check.last_ping_was_fail = True
|
||||
|
||||
# The computed status should be "down" because last_ping_was_fail
|
||||
# is set.
|
||||
self.assertEqual(check.get_status(), "down")
|
||||
|
@ -1,4 +1,5 @@
|
||||
from django.test import Client, TestCase
|
||||
from django.utils.timezone import now
|
||||
|
||||
from hc.api.models import Check, Ping
|
||||
|
||||
@ -114,3 +115,24 @@ class PingTestCase(TestCase):
|
||||
|
||||
self.check.refresh_from_db()
|
||||
self.assertTrue(self.check.has_confirmation_link)
|
||||
|
||||
def test_ping_resets_fail_flag(self):
|
||||
self.check.last_ping_was_fail = True
|
||||
self.check.save()
|
||||
|
||||
r = self.client.get("/ping/%s/" % self.check.code)
|
||||
self.assertEqual(r.status_code, 200)
|
||||
|
||||
self.check.refresh_from_db()
|
||||
self.assertFalse(self.check.last_ping_was_fail)
|
||||
|
||||
def test_fail_endpoint_works(self):
|
||||
r = self.client.get("/ping/%s/fail" % self.check.code)
|
||||
self.assertEqual(r.status_code, 200)
|
||||
|
||||
self.check.refresh_from_db()
|
||||
self.assertTrue(self.check.last_ping_was_fail)
|
||||
self.assertTrue(self.check.alert_after <= now())
|
||||
|
||||
ping = Ping.objects.latest("id")
|
||||
self.assertTrue(ping.fail)
|
||||
|
@ -5,6 +5,9 @@ from hc.api import views
|
||||
urlpatterns = [
|
||||
path('ping/<uuid:code>/', views.ping, name="hc-ping-slash"),
|
||||
path('ping/<uuid:code>', views.ping, name="hc-ping"),
|
||||
path('ping/<uuid:code>/fail', views.ping, {"is_fail": True},
|
||||
name="hc-fail"),
|
||||
|
||||
path('api/v1/checks/', views.checks),
|
||||
path('api/v1/checks/<uuid:code>', views.update, name="hc-api-update"),
|
||||
path('api/v1/checks/<uuid:code>/pause', views.pause, name="hc-api-pause"),
|
||||
|
@ -18,7 +18,7 @@ from hc.lib.badges import check_signature, get_badge_svg
|
||||
|
||||
@csrf_exempt
|
||||
@never_cache
|
||||
def ping(request, code):
|
||||
def ping(request, code, is_fail=False):
|
||||
check = get_object_or_404(Check, code=code)
|
||||
|
||||
headers = request.META
|
||||
@ -29,7 +29,7 @@ def ping(request, code):
|
||||
ua = headers.get("HTTP_USER_AGENT", "")
|
||||
body = request.body.decode()
|
||||
|
||||
check.ping(remote_addr, scheme, method, ua, body)
|
||||
check.ping(remote_addr, scheme, method, ua, body, is_fail)
|
||||
|
||||
response = HttpResponse("OK")
|
||||
response["Access-Control-Allow-Origin"] = "*"
|
||||
|
@ -41,7 +41,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
#log .details span {
|
||||
#log .details span.ua-body {
|
||||
font-family: "Lucida Console", Monaco, monospace;
|
||||
font-size: 11.7px;
|
||||
color: #888;
|
||||
|
@ -39,11 +39,18 @@
|
||||
</td>
|
||||
<td class="date"></td>
|
||||
<td class="time"></td>
|
||||
<td class="text-right">
|
||||
{% if event.fail %}
|
||||
<span class="label label-danger">Failure</span>
|
||||
{% else %}
|
||||
<span class="label label-success">OK</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="details">
|
||||
<div>
|
||||
{% if event.scheme == "email" %}
|
||||
{{ event.ua }}
|
||||
<span>
|
||||
<span class="ua-body">
|
||||
{% if event.body %}
|
||||
- {{ event.body|trunc }}
|
||||
{% endif %}
|
||||
@ -54,7 +61,7 @@
|
||||
{% if event.remote_addr %}
|
||||
from {{ event.remote_addr }}
|
||||
{% endif %}
|
||||
<span>
|
||||
<span class="ua-body">
|
||||
{% if event.ua %}
|
||||
- {{ event.ua }}
|
||||
{% endif %}
|
||||
@ -74,7 +81,7 @@
|
||||
</td>
|
||||
<td class="date"></td>
|
||||
<td class="time"></td>
|
||||
<td class="alert-info">
|
||||
<td class="alert-info" colspan="2">
|
||||
{% if event.channel.kind == "email" %}
|
||||
Sent email alert to {{ event.channel.value }}
|
||||
{% elif event.channel.kind == "slack" %}
|
||||
|
@ -1,5 +1,9 @@
|
||||
<div class="modal-body">
|
||||
<h3>Ping #{{ ping.n }}</h3>
|
||||
<h3>Ping #{{ ping.n }}
|
||||
{% if ping.fail %}
|
||||
<span class="text-danger">(received via the <code>/fail</code> endpoint)</span>
|
||||
{% endif %}
|
||||
</h3>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm-6">
|
||||
@ -49,9 +53,9 @@
|
||||
<span class="ua">{{ ping.ua }}</span>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
{% if ping.body %}
|
||||
<h4>Request Body</h4>
|
||||
<pre>{{ ping.body }}</pre>
|
||||
|
Loading…
x
Reference in New Issue
Block a user