Repository: deis/deis

Issue:
Proposal feat(controller): add url to check status
Something like `/health-check` for `GET` and `HEAD` methods without authentication that returns 200 as status code and OK in the body.



Discussion:
:+1: makes sense. The root path used to be where the UI was, but since that's gone by default it just returns a 404. I can see the reason behind this.


Resolving patch:
diff --git a/controller/api/views.py b/controller/api/views.py
--- a/controller/api/views.py
+++ b/controller/api/views.py
@@ -4,9 +4,11 @@
 from django.conf import settings
 from django.core.exceptions import ValidationError
 from django.contrib.auth.models import User
+from django.http import HttpResponse
 from django.shortcuts import get_object_or_404
 from guardian.shortcuts import assign_perm, get_objects_for_user, \
     get_users_with_perms, remove_perm
+from django.views.generic import View
 from rest_framework import mixins, renderers, status
 from rest_framework.exceptions import PermissionDenied
 from rest_framework.permissions import IsAuthenticated
@@ -19,6 +21,16 @@
 import requests
 
 
+class HealthCheckView(View):
+    """Simple health check view to determine if the server
+       is responding to HTTP requests.
+    """
+
+    def get(self, request):
+        return HttpResponse("OK")
+    head = get
+
+
 class UserRegistrationViewSet(GenericViewSet,
                               mixins.CreateModelMixin):
     """ViewSet to handle registering new users. The logic is in the serializer."""
diff --git a/controller/deis/urls.py b/controller/deis/urls.py
--- a/controller/deis/urls.py
+++ b/controller/deis/urls.py
@@ -10,6 +10,7 @@
 from django.conf import settings
 from django.conf.urls import patterns, include, url
 from django.contrib import admin
+from api.views import HealthCheckView
 
 
 admin.autodiscover()
@@ -17,6 +18,7 @@
 
 urlpatterns = patterns(
     '',
+    url(r'^healthz$', HealthCheckView.as_view()),
     url(r'^v1/', include('api.urls')),
 )
 


Test patch:
diff --git a/controller/api/tests/__init__.py b/controller/api/tests/__init__.py
--- a/controller/api/tests/__init__.py
+++ b/controller/api/tests/__init__.py
@@ -57,6 +57,7 @@ def mock_status_ok(*args, **kwargs):
 from .test_config import *  # noqa
 from .test_container import *  # noqa
 from .test_domain import *  # noqa
+from .test_healthcheck import *  # noqa
 from .test_hooks import *  # noqa
 from .test_key import *  # noqa
 from .test_limits import *  # noqa
diff --git a/controller/api/tests/test_healthcheck.py b/controller/api/tests/test_healthcheck.py
new file mode 100644
--- /dev/null
+++ b/controller/api/tests/test_healthcheck.py
@@ -0,0 +1,24 @@
+from __future__ import unicode_literals
+
+from django.test import TestCase
+
+
+class HealthCheckTest(TestCase):
+
+    def setUp(self):
+        self.url = '/healthz'
+
+    def test_healthcheck(self):
+        # GET and HEAD (no auth required)
+        resp = self.client.get(self.url)
+        self.assertEqual(resp.status_code, 200)
+        self.assertEqual(resp.content, "OK")
+
+        resp = self.client.head(self.url)
+        self.assertEqual(resp.status_code, 200)
+
+    def test_healthcheck_invalid(self):
+        for method in ('put', 'post', 'patch', 'delete'):
+            resp = getattr(self.client, method)(self.url)
+            # method not allowed
+            self.assertEqual(resp.status_code, 405)