mirror of
https://github.com/healthchecks/healthchecks.git
synced 2025-03-15 20:54:53 +00:00
Added "When paused, ignore pings" option in the Filtering Rules dialog (#369)
This commit is contained in:
parent
5c8b5b7b63
commit
3eebd8968d
15 changed files with 172 additions and 11 deletions
|
@ -10,6 +10,7 @@ All notable changes to this project will be documented in this file.
|
|||
- Add a "Transfer Ownership" feature in Project Settings
|
||||
- In checks list, the pause button asks for confirmation (#356)
|
||||
- Added /api/v1/metrics/ endpoint, useful for monitoring the service itself
|
||||
- Added "When paused, ignore pings" option in the Filtering Rules dialog (#369)
|
||||
|
||||
### Bug Fixes
|
||||
- "Get a single check" API call now supports read-only API keys (#346)
|
||||
|
|
18
hc/api/migrations/0071_check_manual_resume.py
Normal file
18
hc/api/migrations/0071_check_manual_resume.py
Normal file
|
@ -0,0 +1,18 @@
|
|||
# Generated by Django 3.0.4 on 2020-06-02 07:35
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('api', '0070_auto_20200411_1310'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='check',
|
||||
name='manual_resume',
|
||||
field=models.NullBooleanField(default=False),
|
||||
),
|
||||
]
|
|
@ -75,6 +75,7 @@ class Check(models.Model):
|
|||
tz = models.CharField(max_length=36, default="UTC")
|
||||
subject = models.CharField(max_length=100, blank=True)
|
||||
methods = models.CharField(max_length=30, blank=True)
|
||||
manual_resume = models.NullBooleanField(default=False)
|
||||
|
||||
n_pings = models.IntegerField(default=0)
|
||||
last_ping = models.DateTimeField(null=True, blank=True)
|
||||
|
@ -243,6 +244,9 @@ class Check(models.Model):
|
|||
def ping(self, remote_addr, scheme, method, ua, body, action):
|
||||
now = timezone.now()
|
||||
|
||||
if self.status == "paused" and self.manual_resume:
|
||||
action = "ign"
|
||||
|
||||
if action == "start":
|
||||
self.last_start = now
|
||||
# Don't update "last_ping" field.
|
||||
|
|
|
@ -209,3 +209,18 @@ class PingTestCase(BaseTestCase):
|
|||
|
||||
ping = Ping.objects.latest("id")
|
||||
self.assertEqual(len(ping.body), 20000)
|
||||
|
||||
def test_it_handles_manual_resume_flag(self):
|
||||
self.check.status = "paused"
|
||||
self.check.manual_resume = True
|
||||
self.check.save()
|
||||
|
||||
r = self.client.get(self.url)
|
||||
self.assertEqual(r.status_code, 200)
|
||||
|
||||
self.check.refresh_from_db()
|
||||
self.assertEqual(self.check.status, "paused")
|
||||
|
||||
ping = Ping.objects.latest("id")
|
||||
self.assertEqual(ping.scheme, "http")
|
||||
self.assertEqual(ping.kind, "ign")
|
||||
|
|
|
@ -63,8 +63,9 @@ class NameTagsForm(forms.Form):
|
|||
|
||||
|
||||
class FilteringRulesForm(forms.Form):
|
||||
subject = forms.CharField(max_length=100)
|
||||
subject = forms.CharField(required=False, max_length=100)
|
||||
methods = forms.ChoiceField(required=False, choices=(("", "Any"), ("POST", "POST")))
|
||||
manual_resume = forms.BooleanField(required=False)
|
||||
|
||||
|
||||
class TimeoutForm(forms.Form):
|
||||
|
|
|
@ -12,12 +12,16 @@ class FilteringRulesTestCase(BaseTestCase):
|
|||
|
||||
def test_it_works(self):
|
||||
self.client.login(username="alice@example.org", password="password")
|
||||
r = self.client.post(self.url, data={"subject": "SUCCESS", "methods": "POST"})
|
||||
r = self.client.post(
|
||||
self.url,
|
||||
data={"subject": "SUCCESS", "methods": "POST", "manual_resume": "1"},
|
||||
)
|
||||
self.assertRedirects(r, self.redirect_url)
|
||||
|
||||
self.check.refresh_from_db()
|
||||
self.assertEqual(self.check.subject, "SUCCESS")
|
||||
self.assertEqual(self.check.methods, "POST")
|
||||
self.assertTrue(self.check.manual_resume)
|
||||
|
||||
def test_it_clears_method(self):
|
||||
self.check.method = "POST"
|
||||
|
@ -29,3 +33,25 @@ class FilteringRulesTestCase(BaseTestCase):
|
|||
|
||||
self.check.refresh_from_db()
|
||||
self.assertEqual(self.check.methods, "")
|
||||
|
||||
def test_it_clears_subject(self):
|
||||
self.check.subject = "SUCCESS"
|
||||
self.check.save()
|
||||
|
||||
self.client.login(username="alice@example.org", password="password")
|
||||
r = self.client.post(self.url, data={"methods": ""})
|
||||
self.assertRedirects(r, self.redirect_url)
|
||||
|
||||
self.check.refresh_from_db()
|
||||
self.assertEqual(self.check.subject, "")
|
||||
|
||||
def test_it_clears_manual_resume_flag(self):
|
||||
self.check.manual_resume = True
|
||||
self.check.save()
|
||||
|
||||
self.client.login(username="alice@example.org", password="password")
|
||||
r = self.client.post(self.url, data={})
|
||||
self.assertRedirects(r, self.redirect_url)
|
||||
|
||||
self.check.refresh_from_db()
|
||||
self.assertFalse(self.check.manual_resume)
|
||||
|
|
|
@ -10,7 +10,7 @@ class PauseTestCase(BaseTestCase):
|
|||
super(PauseTestCase, self).setUp()
|
||||
self.check = Check.objects.create(project=self.project, status="up")
|
||||
self.url = "/checks/%s/pause/" % self.check.code
|
||||
self.redirect_url = "/projects/%s/checks/" % self.project.code
|
||||
self.redirect_url = "/checks/%s/details/" % self.check.code
|
||||
|
||||
def test_it_pauses(self):
|
||||
self.client.login(username="alice@example.org", password="password")
|
||||
|
|
28
hc/front/tests/test_resume.py
Normal file
28
hc/front/tests/test_resume.py
Normal file
|
@ -0,0 +1,28 @@
|
|||
from hc.api.models import Check
|
||||
from hc.test import BaseTestCase
|
||||
|
||||
|
||||
class ResumeTestCase(BaseTestCase):
|
||||
def setUp(self):
|
||||
super(ResumeTestCase, self).setUp()
|
||||
self.check = Check.objects.create(project=self.project, status="paused")
|
||||
self.url = "/checks/%s/resume/" % self.check.code
|
||||
self.redirect_url = "/checks/%s/details/" % self.check.code
|
||||
|
||||
def test_it_resumes(self):
|
||||
self.client.login(username="alice@example.org", password="password")
|
||||
r = self.client.post(self.url)
|
||||
self.assertRedirects(r, self.redirect_url)
|
||||
|
||||
self.check.refresh_from_db()
|
||||
self.assertEqual(self.check.status, "new")
|
||||
|
||||
def test_it_rejects_get(self):
|
||||
self.client.login(username="alice@example.org", password="password")
|
||||
r = self.client.get(self.url)
|
||||
self.assertEqual(r.status_code, 405)
|
||||
|
||||
def test_it_allows_cross_team_access(self):
|
||||
self.client.login(username="bob@example.org", password="password")
|
||||
r = self.client.post(self.url)
|
||||
self.assertRedirects(r, self.redirect_url)
|
|
@ -50,3 +50,15 @@ class StatusSingleTestCase(BaseTestCase):
|
|||
self.client.login(username="bob@example.org", password="password")
|
||||
r = self.client.get("/checks/%s/status/" % self.check.code)
|
||||
self.assertEqual(r.status_code, 200)
|
||||
|
||||
def test_it_handles_manual_resume(self):
|
||||
self.check.status = "paused"
|
||||
self.check.manual_resume = True
|
||||
self.check.save()
|
||||
|
||||
self.client.login(username="alice@example.org", password="password")
|
||||
r = self.client.get("/checks/%s/status/" % self.check.code)
|
||||
doc = r.json()
|
||||
|
||||
self.assertEqual(doc["status"], "paused")
|
||||
self.assertTrue("will ignore pings until resumed" in doc["status_text"])
|
||||
|
|
|
@ -8,6 +8,7 @@ check_urls = [
|
|||
path("filtering_rules/", views.filtering_rules, name="hc-filtering-rules"),
|
||||
path("timeout/", views.update_timeout, name="hc-update-timeout"),
|
||||
path("pause/", views.pause, name="hc-pause"),
|
||||
path("resume/", views.resume, name="hc-resume"),
|
||||
path("remove/", views.remove_check, name="hc-remove-check"),
|
||||
path("log/", views.log, name="hc-log"),
|
||||
path("status/", views.status_single, name="hc-status-single"),
|
||||
|
|
|
@ -344,6 +344,7 @@ def filtering_rules(request, code):
|
|||
if form.is_valid():
|
||||
check.subject = form.cleaned_data["subject"]
|
||||
check.methods = form.cleaned_data["methods"]
|
||||
check.manual_resume = form.cleaned_data["manual_resume"]
|
||||
check.save()
|
||||
|
||||
return redirect("hc-details", code)
|
||||
|
@ -442,14 +443,25 @@ def pause(request, code):
|
|||
check.alert_after = None
|
||||
check.save()
|
||||
|
||||
if "/details/" in request.META.get("HTTP_REFERER", ""):
|
||||
return redirect("hc-details", code)
|
||||
|
||||
# Don't redirect after an AJAX request:
|
||||
if request.META.get("HTTP_X_REQUESTED_WITH") == "XMLHttpRequest":
|
||||
return HttpResponse()
|
||||
|
||||
return redirect("hc-checks", check.project.code)
|
||||
return redirect("hc-details", code)
|
||||
|
||||
|
||||
@require_POST
|
||||
@login_required
|
||||
def resume(request, code):
|
||||
check = _get_check_for_user(request, code)
|
||||
|
||||
check.status = "new"
|
||||
check.last_start = None
|
||||
check.last_ping = None
|
||||
check.alert_after = None
|
||||
check.save()
|
||||
|
||||
return redirect("hc-details", code)
|
||||
|
||||
|
||||
@require_POST
|
||||
|
|
|
@ -36,6 +36,11 @@ $(function () {
|
|||
return false;
|
||||
});
|
||||
|
||||
$("#log-status-text").on("click", "#resume-btn", function() {
|
||||
$("#resume-form").submit();
|
||||
return false;
|
||||
});
|
||||
|
||||
$("#pause").click(function(e) {
|
||||
$("#pause-form").submit();
|
||||
return false;
|
||||
|
@ -79,7 +84,9 @@ $(function () {
|
|||
if (data.status_text != lastStatusText) {
|
||||
lastStatusText = data.status_text;
|
||||
$("#log-status-icon").attr("class", "status icon-" + data.status);
|
||||
$("#log-status-text").text(data.status_text);
|
||||
$("#log-status-text").html(data.status_text);
|
||||
|
||||
$('#pause-btn').prop('disabled', data.status == "paused");
|
||||
}
|
||||
|
||||
if (data.events) {
|
||||
|
|
|
@ -127,7 +127,11 @@
|
|||
<div class="text-right">
|
||||
<form action="{% url 'hc-pause' check.code %}" method="post">
|
||||
{% csrf_token %}
|
||||
<input type="submit" class="btn btn-sm btn-default" value="Pause" />
|
||||
<input
|
||||
id="pause-btn"
|
||||
type="submit"
|
||||
{% if check.status == "paused" %}disabled{% endif %}
|
||||
class="btn btn-sm btn-default" value="Pause" />
|
||||
</form>
|
||||
|
||||
<button
|
||||
|
@ -289,8 +293,12 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div id="transfer-modal" class="modal">
|
||||
</div>
|
||||
<div id="transfer-modal" class="modal"></div>
|
||||
|
||||
<form id="resume-form" action="{% url 'hc-resume' check.code %}" method="post">
|
||||
{% csrf_token %}
|
||||
</form>
|
||||
|
||||
|
||||
{% include "front/update_name_modal.html" %}
|
||||
{% include "front/update_timeout_modal.html" %}
|
||||
|
|
|
@ -58,6 +58,29 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal-body">
|
||||
<p>Handling of pings while paused:</p>
|
||||
<label class="radio-container">
|
||||
<input
|
||||
type="radio"
|
||||
name="manual_resume"
|
||||
value=""
|
||||
{% if not check.manual_resume %}checked{% endif %}>
|
||||
<span class="radiomark"></span>
|
||||
Leave the paused state (default)
|
||||
</label>
|
||||
<label class="radio-container">
|
||||
<input
|
||||
type="radio"
|
||||
name="manual_resume"
|
||||
value="1"
|
||||
{% if check.manual_resume %}checked{% endif %}>
|
||||
<span class="radiomark"></span>
|
||||
Ignore pings, stay in the paused state
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
|
||||
<button type="submit" class="btn btn-primary">Save</button>
|
||||
|
|
|
@ -6,8 +6,13 @@
|
|||
This check is up. Last ping was {{ check.last_ping|naturaltime }}.
|
||||
{% elif status == "grace" %}
|
||||
This check is late. Last ping was {{ check.last_ping|naturaltime }}.
|
||||
{% elif status == "paused" and check.manual_resume %}
|
||||
This check is paused, and will ignore pings until resumed.
|
||||
<a id="resume-btn" href="#">(Resume Now)</a>
|
||||
{% elif status == "paused" %}
|
||||
This check is paused.
|
||||
{% elif status == "new" and check.n_pings %}
|
||||
This check is ready for pings.
|
||||
{% elif status == "new" %}
|
||||
This check has never received a ping.
|
||||
{% elif status == "started" %}
|
||||
|
|
Loading…
Reference in a new issue