Browse Source

Added /api/v1/metrics/ endpoint, useful for monitoring the service itself

pull/366/head
Pēteris Caune 5 years ago
parent
commit
edbfd4b437
No known key found for this signature in database GPG Key ID: E28D7679E9A9EDE2
6 changed files with 74 additions and 1 deletions
  1. +1
    -0
      CHANGELOG.md
  2. +43
    -0
      hc/api/tests/test_metrics.py
  3. +12
    -0
      hc/api/tests/test_status.py
  4. +1
    -0
      hc/api/urls.py
  5. +16
    -1
      hc/api/views.py
  6. +1
    -0
      hc/settings.py

+ 1
- 0
CHANGELOG.md View File

@ -9,6 +9,7 @@ All notable changes to this project will be documented in this file.
- Users can edit their existing webhook integrations (#176) - Users can edit their existing webhook integrations (#176)
- Add a "Transfer Ownership" feature in Project Settings - Add a "Transfer Ownership" feature in Project Settings
- In checks list, the pause button asks for confirmation (#356) - In checks list, the pause button asks for confirmation (#356)
- Added /api/v1/metrics/ endpoint, useful for monitoring the service itself
### Bug Fixes ### Bug Fixes
- "Get a single check" API call now supports read-only API keys (#346) - "Get a single check" API call now supports read-only API keys (#346)


+ 43
- 0
hc/api/tests/test_metrics.py View File

@ -0,0 +1,43 @@
from django.test.utils import override_settings
from django.utils.timezone import now
from hc.api.models import Check, Flip, Ping
from hc.test import BaseTestCase
@override_settings(METRICS_KEY="foo")
class MetricsTestCase(BaseTestCase):
url = "/api/v1/metrics/"
def test_it_returns_num_unprocessed_flips(self):
check = Check.objects.create(project=self.project, status="down")
flip = Flip(owner=check)
flip.created = now()
flip.old_status = "up"
flip.new_status = "down"
flip.save()
r = self.client.get(self.url, HTTP_X_METRICS_KEY="foo")
self.assertEqual(r.status_code, 200)
doc = r.json()
self.assertEqual(doc["num_unprocessed_flips"], 1)
def test_it_returns_max_ping_id(self):
check = Check.objects.create(project=self.project, status="down")
Ping.objects.create(owner=check, n=1)
last_ping = Ping.objects.last()
r = self.client.get(self.url, HTTP_X_METRICS_KEY="foo")
self.assertEqual(r.status_code, 200)
doc = r.json()
self.assertEqual(doc["max_ping_id"], last_ping.id)
@override_settings(METRICS_KEY=None)
def test_it_handles_unset_metrics_key(self):
r = self.client.get(self.url, HTTP_X_METRICS_KEY="foo")
self.assertEqual(r.status_code, 403)
def test_it_handles_incorrect_metrics_key(self):
r = self.client.get(self.url, HTTP_X_METRICS_KEY="bar")
self.assertEqual(r.status_code, 403)

+ 12
- 0
hc/api/tests/test_status.py View File

@ -0,0 +1,12 @@
from hc.test import BaseTestCase
class StatusTestCase(BaseTestCase):
url = "/api/v1/status/"
def test_it_works(self):
r = self.client.get(self.url)
self.assertEqual(r.status_code, 200)
self.assertNumQueries(1)
self.assertEqual(r.content, b"OK")

+ 1
- 0
hc/api/urls.py View File

@ -37,5 +37,6 @@ urlpatterns = [
{"tag": "*"}, {"tag": "*"},
name="hc-badge-all", name="hc-badge-all",
), ),
path("api/v1/metrics/", views.metrics),
path("api/v1/status/", views.status), path("api/v1/status/", views.status),
] ]

+ 16
- 1
hc/api/views.py View File

@ -17,7 +17,7 @@ from django.views.decorators.http import require_POST
from hc.api import schemas from hc.api import schemas
from hc.api.decorators import authorize, authorize_read, cors, validate_json from hc.api.decorators import authorize, authorize_read, cors, validate_json
from hc.api.models import Check, Notification, Channel
from hc.api.models import Flip, Channel, Check, Notification, Ping
from hc.lib.badges import check_signature, get_badge_svg from hc.lib.badges import check_signature, get_badge_svg
@ -320,6 +320,21 @@ def bounce(request, code):
return HttpResponse() return HttpResponse()
def metrics(request):
if not settings.METRICS_KEY:
return HttpResponseForbidden()
key = request.META.get("HTTP_X_METRICS_KEY")
if key != settings.METRICS_KEY:
return HttpResponseForbidden()
doc = {}
doc["max_ping_id"] = Ping.objects.values_list("id", flat=True).last()
doc["num_unprocessed_flips"] = Flip.objects.filter(processed__isnull=True).count()
return JsonResponse(doc)
def status(request): def status(request):
with connection.cursor() as c: with connection.cursor() as c:
c.execute("SELECT 1") c.execute("SELECT 1")


+ 1
- 0
hc/settings.py View File

@ -28,6 +28,7 @@ def envint(s, default):
SECRET_KEY = os.getenv("SECRET_KEY", "---") SECRET_KEY = os.getenv("SECRET_KEY", "---")
METRICS_KEY = os.getenv("METRICS_KEY")
DEBUG = envbool("DEBUG", "True") DEBUG = envbool("DEBUG", "True")
ALLOWED_HOSTS = os.getenv("ALLOWED_HOSTS", "*").split(",") ALLOWED_HOSTS = os.getenv("ALLOWED_HOSTS", "*").split(",")
DEFAULT_FROM_EMAIL = os.getenv("DEFAULT_FROM_EMAIL", "[email protected]") DEFAULT_FROM_EMAIL = os.getenv("DEFAULT_FROM_EMAIL", "[email protected]")


Loading…
Cancel
Save