forked from GithubBackups/healthchecks
UI for entering cron expression and setting timezone.
This commit is contained in:
parent
8633a5a892
commit
6ada656df4
@ -1,5 +1,5 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
# Generated by Django 1.10.1 on 2016-12-05 08:33
|
# Generated by Django 1.10.1 on 2016-12-13 10:59
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
from django.db import migrations, models
|
from django.db import migrations, models
|
||||||
@ -12,10 +12,15 @@ class Migration(migrations.Migration):
|
|||||||
]
|
]
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='check',
|
||||||
|
name='kind',
|
||||||
|
field=models.CharField(choices=[('simple', 'Simple'), ('cron', 'Cron')], default='simple', max_length=10),
|
||||||
|
),
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='check',
|
model_name='check',
|
||||||
name='schedule',
|
name='schedule',
|
||||||
field=models.CharField(blank=True, max_length=100),
|
field=models.CharField(default='* * * * *', max_length=100),
|
||||||
),
|
),
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='check',
|
model_name='check',
|
@ -22,6 +22,9 @@ STATUSES = (
|
|||||||
)
|
)
|
||||||
DEFAULT_TIMEOUT = td(days=1)
|
DEFAULT_TIMEOUT = td(days=1)
|
||||||
DEFAULT_GRACE = td(hours=1)
|
DEFAULT_GRACE = td(hours=1)
|
||||||
|
CHECK_KINDS = (("simple", "Simple"),
|
||||||
|
("cron", "Cron"))
|
||||||
|
|
||||||
CHANNEL_KINDS = (("email", "Email"),
|
CHANNEL_KINDS = (("email", "Email"),
|
||||||
("webhook", "Webhook"),
|
("webhook", "Webhook"),
|
||||||
("hipchat", "HipChat"),
|
("hipchat", "HipChat"),
|
||||||
@ -52,9 +55,11 @@ class Check(models.Model):
|
|||||||
code = models.UUIDField(default=uuid.uuid4, editable=False, db_index=True)
|
code = models.UUIDField(default=uuid.uuid4, editable=False, db_index=True)
|
||||||
user = models.ForeignKey(User, blank=True, null=True)
|
user = models.ForeignKey(User, blank=True, null=True)
|
||||||
created = models.DateTimeField(auto_now_add=True)
|
created = models.DateTimeField(auto_now_add=True)
|
||||||
|
kind = models.CharField(max_length=10, default="simple",
|
||||||
|
choices=CHECK_KINDS)
|
||||||
timeout = models.DurationField(default=DEFAULT_TIMEOUT)
|
timeout = models.DurationField(default=DEFAULT_TIMEOUT)
|
||||||
grace = models.DurationField(default=DEFAULT_GRACE)
|
grace = models.DurationField(default=DEFAULT_GRACE)
|
||||||
schedule = models.CharField(max_length=100, blank=True)
|
schedule = models.CharField(max_length=100, default="* * * * *")
|
||||||
tz = models.CharField(max_length=36, default="UTC")
|
tz = models.CharField(max_length=36, default="UTC")
|
||||||
n_pings = models.IntegerField(default=0)
|
n_pings = models.IntegerField(default=0)
|
||||||
last_ping = models.DateTimeField(null=True, blank=True)
|
last_ping = models.DateTimeField(null=True, blank=True)
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
from django import forms
|
from django import forms
|
||||||
from hc.front.validators import WebhookValidator
|
from hc.front.validators import WebhookValidator
|
||||||
from hc.api.models import Channel
|
from hc.api.models import CHECK_KINDS, Channel
|
||||||
|
|
||||||
|
|
||||||
class NameTagsForm(forms.Form):
|
class NameTagsForm(forms.Form):
|
||||||
@ -19,7 +19,10 @@ class NameTagsForm(forms.Form):
|
|||||||
|
|
||||||
|
|
||||||
class TimeoutForm(forms.Form):
|
class TimeoutForm(forms.Form):
|
||||||
|
kind = forms.ChoiceField(choices=CHECK_KINDS)
|
||||||
timeout = forms.IntegerField(min_value=60, max_value=2592000)
|
timeout = forms.IntegerField(min_value=60, max_value=2592000)
|
||||||
|
schedule = forms.CharField(required=False, max_length=100)
|
||||||
|
tz = forms.CharField(required=False, max_length=36)
|
||||||
grace = forms.IntegerField(min_value=60, max_value=2592000)
|
grace = forms.IntegerField(min_value=60, max_value=2592000)
|
||||||
|
|
||||||
|
|
||||||
|
@ -13,22 +13,41 @@ class UpdateTimeoutTestCase(BaseTestCase):
|
|||||||
|
|
||||||
def test_it_works(self):
|
def test_it_works(self):
|
||||||
url = "/checks/%s/timeout/" % self.check.code
|
url = "/checks/%s/timeout/" % self.check.code
|
||||||
payload = {"timeout": 3600, "grace": 60}
|
payload = {"kind": "simple", "timeout": 3600, "grace": 60}
|
||||||
|
|
||||||
self.client.login(username="alice@example.org", password="password")
|
self.client.login(username="alice@example.org", password="password")
|
||||||
r = self.client.post(url, data=payload)
|
r = self.client.post(url, data=payload)
|
||||||
self.assertRedirects(r, "/checks/")
|
self.assertRedirects(r, "/checks/")
|
||||||
|
|
||||||
self.check.refresh_from_db()
|
self.check.refresh_from_db()
|
||||||
|
self.assertEqual(self.check.kind, "simple")
|
||||||
self.assertEqual(self.check.timeout.total_seconds(), 3600)
|
self.assertEqual(self.check.timeout.total_seconds(), 3600)
|
||||||
self.assertEqual(self.check.grace.total_seconds(), 60)
|
self.assertEqual(self.check.grace.total_seconds(), 60)
|
||||||
|
|
||||||
# alert_after should be updated too
|
# alert_after should be updated too
|
||||||
self.assertEqual(self.check.alert_after, self.check.get_alert_after())
|
self.assertEqual(self.check.alert_after, self.check.get_alert_after())
|
||||||
|
|
||||||
|
def test_it_saves_cron_expression(self):
|
||||||
|
url = "/checks/%s/timeout/" % self.check.code
|
||||||
|
payload = {
|
||||||
|
"kind": "cron",
|
||||||
|
"schedule": "* * * * *",
|
||||||
|
"tz": "UTC",
|
||||||
|
"timeout": 60,
|
||||||
|
"grace": 60
|
||||||
|
}
|
||||||
|
|
||||||
|
self.client.login(username="alice@example.org", password="password")
|
||||||
|
r = self.client.post(url, data=payload)
|
||||||
|
self.assertRedirects(r, "/checks/")
|
||||||
|
|
||||||
|
self.check.refresh_from_db()
|
||||||
|
self.assertEqual(self.check.kind, "cron")
|
||||||
|
self.assertEqual(self.check.schedule, "* * * * *")
|
||||||
|
|
||||||
def test_team_access_works(self):
|
def test_team_access_works(self):
|
||||||
url = "/checks/%s/timeout/" % self.check.code
|
url = "/checks/%s/timeout/" % self.check.code
|
||||||
payload = {"timeout": 7200, "grace": 60}
|
payload = {"kind": "simple", "timeout": 7200, "grace": 60}
|
||||||
|
|
||||||
# Logging in as bob, not alice. Bob has team access so this
|
# Logging in as bob, not alice. Bob has team access so this
|
||||||
# should work.
|
# should work.
|
||||||
|
@ -163,12 +163,18 @@ def update_timeout(request, code):
|
|||||||
|
|
||||||
form = TimeoutForm(request.POST)
|
form = TimeoutForm(request.POST)
|
||||||
if form.is_valid():
|
if form.is_valid():
|
||||||
|
check.kind = form.cleaned_data["kind"]
|
||||||
check.timeout = td(seconds=form.cleaned_data["timeout"])
|
check.timeout = td(seconds=form.cleaned_data["timeout"])
|
||||||
check.grace = td(seconds=form.cleaned_data["grace"])
|
check.grace = td(seconds=form.cleaned_data["grace"])
|
||||||
|
check.schedule = form.cleaned_data["schedule"]
|
||||||
|
check.tz = form.cleaned_data["tz"]
|
||||||
|
|
||||||
if check.last_ping:
|
if check.last_ping:
|
||||||
check.alert_after = check.get_alert_after()
|
check.alert_after = check.get_alert_after()
|
||||||
|
|
||||||
check.save()
|
check.save()
|
||||||
|
else:
|
||||||
|
assert 0, "form is not valid! %s" % form.errors
|
||||||
|
|
||||||
return redirect("hc-checks")
|
return redirect("hc-checks")
|
||||||
|
|
||||||
|
@ -23,6 +23,14 @@
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#type-simple, #type-cron {
|
||||||
|
width: 70px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#schedule-block {
|
||||||
|
margin: 0 50px;
|
||||||
|
}
|
||||||
|
|
||||||
#period-slider {
|
#period-slider {
|
||||||
margin: 20px 50px 80px 50px;
|
margin: 20px 50px 80px 50px;
|
||||||
}
|
}
|
||||||
|
@ -91,11 +91,9 @@ $(function () {
|
|||||||
$('[data-toggle="tooltip"]').tooltip();
|
$('[data-toggle="tooltip"]').tooltip();
|
||||||
|
|
||||||
$(".my-checks-name").click(function() {
|
$(".my-checks-name").click(function() {
|
||||||
var $this = $(this);
|
$("#update-name-form").attr("action", this.dataset.url);
|
||||||
|
$("#update-name-input").val(this.dataset.name);
|
||||||
$("#update-name-form").attr("action", $this.data("url"));
|
$("#update-tags-input").val(this.dataset.tags);
|
||||||
$("#update-name-input").val($this.data("name"));
|
|
||||||
$("#update-tags-input").val($this.data("tags"));
|
|
||||||
$('#update-name-modal').modal("show");
|
$('#update-name-modal').modal("show");
|
||||||
$("#update-name-input").focus();
|
$("#update-name-input").focus();
|
||||||
|
|
||||||
@ -103,21 +101,45 @@ $(function () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
$(".timeout-grace").click(function() {
|
$(".timeout-grace").click(function() {
|
||||||
var $this = $(this);
|
$("#update-timeout-form").attr("action", this.dataset.url);
|
||||||
|
periodSlider.noUiSlider.set(this.dataset.timeout);
|
||||||
|
graceSlider.noUiSlider.set(this.dataset.grace);
|
||||||
|
$("#schedule").val(this.dataset.schedule);
|
||||||
|
$("#tz").val(this.dataset.tz);
|
||||||
|
|
||||||
|
if (this.dataset.kind == "cron") {
|
||||||
|
$("#type-simple").removeClass("active");
|
||||||
|
$("#type-cron").addClass("active");
|
||||||
|
$("#type-cron input").prop("checked", true);
|
||||||
|
|
||||||
|
$("#period-block").hide();
|
||||||
|
$("#schedule-block").show();
|
||||||
|
} else {
|
||||||
|
$("#type-simple").addClass("active");
|
||||||
|
$("#type-simple input").prop("checked", true);
|
||||||
|
$("#type-cron").removeClass("active");
|
||||||
|
|
||||||
|
$("#period-block").show();
|
||||||
|
$("#schedule-block").hide();
|
||||||
|
}
|
||||||
|
|
||||||
$("#update-timeout-form").attr("action", $this.data("url"));
|
|
||||||
periodSlider.noUiSlider.set($this.data("timeout"))
|
|
||||||
graceSlider.noUiSlider.set($this.data("grace"))
|
|
||||||
$('#update-timeout-modal').modal({"show":true, "backdrop":"static"});
|
$('#update-timeout-modal').modal({"show":true, "backdrop":"static"});
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
|
|
||||||
$(".check-menu-remove").click(function() {
|
$("#type-simple").click(function() {
|
||||||
var $this = $(this);
|
$("#period-block").show();
|
||||||
|
$("#schedule-block").hide();
|
||||||
|
});
|
||||||
|
|
||||||
$("#remove-check-form").attr("action", $this.data("url"));
|
$("#type-cron").click(function() {
|
||||||
$(".remove-check-name").text($this.data("name"));
|
$("#period-block").hide();
|
||||||
|
$("#schedule-block").show();
|
||||||
|
});
|
||||||
|
|
||||||
|
$(".check-menu-remove").click(function() {
|
||||||
|
$("#remove-check-form").attr("action", this.dataset.url);
|
||||||
|
$(".remove-check-name").text(this.dataset.name);
|
||||||
$('#remove-check-modal').modal("show");
|
$('#remove-check-modal').modal("show");
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
@ -118,20 +118,42 @@
|
|||||||
<input type="hidden" name="grace" id="update-timeout-grace" />
|
<input type="hidden" name="grace" id="update-timeout-grace" />
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<div class="update-timeout-info text-center">
|
<div id="period-block">
|
||||||
<span
|
<div class="update-timeout-info text-center">
|
||||||
class="update-timeout-label"
|
<span
|
||||||
data-toggle="tooltip"
|
class="update-timeout-label"
|
||||||
title="Expected time between pings.">
|
data-toggle="tooltip"
|
||||||
Period
|
title="Expected time between pings.">
|
||||||
</span>
|
Period
|
||||||
<span
|
</span>
|
||||||
id="period-slider-value"
|
<span
|
||||||
class="update-timeout-value">
|
id="period-slider-value"
|
||||||
1 day
|
class="update-timeout-value">
|
||||||
</span>
|
1 day
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div id="period-slider"></div>
|
||||||
|
</div>
|
||||||
|
<div id="schedule-block">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="schedule">
|
||||||
|
Cron expression
|
||||||
|
<a href="https://en.wikipedia.org/wiki/Cron#Overview">(reference)</a>
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
class="form-control input-lg"
|
||||||
|
id="schedule"
|
||||||
|
name="schedule"
|
||||||
|
placeholder="* * * * *">
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="schedule">Server's Timezone</label>
|
||||||
|
<select id="tz" name="tz" class="form-control">
|
||||||
|
<option>UTC</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div id="period-slider"></div>
|
|
||||||
|
|
||||||
<div class="update-timeout-info text-center">
|
<div class="update-timeout-info text-center">
|
||||||
<span
|
<span
|
||||||
@ -162,6 +184,17 @@
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
|
<div class="btn-group pull-left" data-toggle="buttons">
|
||||||
|
<label id="type-simple" class="btn btn-default">
|
||||||
|
<input type="radio" name="kind" value="simple" autocomplete="off">
|
||||||
|
Simple
|
||||||
|
</label>
|
||||||
|
<label id="type-cron" class="btn btn-default">
|
||||||
|
<input type="radio" name="kind" value="cron" autocomplete="off">
|
||||||
|
Cron
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
|
<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
|
||||||
<button type="submit" class="btn btn-primary">Save</button>
|
<button type="submit" class="btn btn-primary">Save</button>
|
||||||
</div>
|
</div>
|
||||||
|
@ -52,10 +52,17 @@
|
|||||||
<td class="timeout-cell">
|
<td class="timeout-cell">
|
||||||
<span
|
<span
|
||||||
data-url="{% url 'hc-update-timeout' check.code %}"
|
data-url="{% url 'hc-update-timeout' check.code %}"
|
||||||
|
data-kind="{{ check.kind }}"
|
||||||
data-timeout="{{ check.timeout.total_seconds }}"
|
data-timeout="{{ check.timeout.total_seconds }}"
|
||||||
data-grace="{{ check.grace.total_seconds }}"
|
data-grace="{{ check.grace.total_seconds }}"
|
||||||
|
data-schedule="{{ check.schedule }}"
|
||||||
|
data-tz="{{ check.tz }}"
|
||||||
class="timeout-grace">
|
class="timeout-grace">
|
||||||
{{ check.timeout|hc_duration }}
|
{% if check.kind == "simple" %}
|
||||||
|
{{ check.timeout|hc_duration }}
|
||||||
|
{% elif check.kind == "cron" %}
|
||||||
|
{{ check.schedule }}
|
||||||
|
{% endif %}
|
||||||
<br />
|
<br />
|
||||||
<span class="checks-subline">
|
<span class="checks-subline">
|
||||||
{{ check.grace|hc_duration }}
|
{{ check.grace|hc_duration }}
|
||||||
|
@ -46,10 +46,17 @@
|
|||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
{% if check.kind == "simple " %}
|
||||||
<tr>
|
<tr>
|
||||||
<th>Period</th>
|
<th>Period</th>
|
||||||
<td>{{ check.timeout|hc_duration }}</td>
|
<td>{{ check.timeout|hc_duration }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
{% elif check.kind == "cron" %}
|
||||||
|
<tr>
|
||||||
|
<th>Schedule</th>
|
||||||
|
<td>{{ check.schedule }}</td>
|
||||||
|
</tr>
|
||||||
|
{% endif %}
|
||||||
<tr>
|
<tr>
|
||||||
<th>Grace Time</th>
|
<th>Grace Time</th>
|
||||||
<td>{{ check.grace|hc_duration }}</td>
|
<td>{{ check.grace|hc_duration }}</td>
|
||||||
@ -79,9 +86,12 @@
|
|||||||
|
|
||||||
<a
|
<a
|
||||||
href="#"
|
href="#"
|
||||||
|
data-kind="{{ check.kind }}"
|
||||||
data-url="{% url 'hc-update-timeout' check.code %}"
|
data-url="{% url 'hc-update-timeout' check.code %}"
|
||||||
data-timeout="{{ check.timeout.total_seconds }}"
|
data-timeout="{{ check.timeout.total_seconds }}"
|
||||||
data-grace="{{ check.grace.total_seconds }}"
|
data-grace="{{ check.grace.total_seconds }}"
|
||||||
|
data-schedule="{{ check.schedule }}"
|
||||||
|
data-tz="{{ check.tz }}"
|
||||||
class="btn btn-default timeout-grace">Change Period</a>
|
class="btn btn-default timeout-grace">Change Period</a>
|
||||||
|
|
||||||
<a href="{% url 'hc-log' check.code %}" class="btn btn-default">Log</a>
|
<a href="{% url 'hc-log' check.code %}" class="btn btn-default">Log</a>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user