Repository URL to install this package:
|
Version:
0.32.0 ▾
|
# Copyright 2015 Docker, Inc. All rights reserved.
import functools
import json
import logging
import os
import six
from . import utils
import django
from django.conf import settings
from django.conf.urls import url
from django.http import HttpResponse
logger = logging.getLogger(__name__)
def check_internal_single_hop(fn):
"""Decorator around view methods to check permission. The request must be an
internal single-hop request, with no ``X-Forwarded-For`` header.
"""
@functools.wraps(fn)
def wrapper(self, request, *args, **kwargs):
if not settings.USE_X_FORWARDED_HOST:
logger.warn("Rejecting call since source verification failed. Please enable USE_X_FORWARDED_HOST.")
return HttpResponse(status=404)
xff = request.META.get('HTTP_X_FORWARDED_FOR', None)
if xff:
logger.warn("Rejecting call to '%s' due to source address restriction, X-Forwarded-For: %s",
request.get_full_path(), xff)
return HttpResponse(status=404)
return fn(self, request, *args, **kwargs)
return wrapper
class AppStatusViews(object):
"""A common class to provide basic endpoints to get the app's status.
All these endpoints are restricted by connection source. The request must
*not* have an ``X-Forwarded-For`` header, which means that the client must
be within the VPC without going through our proxy.
/_health
Returns 200 for good health.
/_echo
Returns the metadata (headers, etc) on the request.
/_stacks
Displays stack trace of all threads.
/_heap
Displays heap usage.
Example:
Put this in your ``urls.py``::
urlpatterns = AppStatusViews(health_check=my_health_check).urls()
"""
def __init__(self, health_check=None):
"""
Args:
health_check (callable): A health check method that takes no
arguments, and returns a dictionary of
``{subsystem, status_string}``. The ``/_health`` endpoint will
report failure unless all the status_strings begin with "ok".
"""
if health_check is None:
# Given something generic
self._health_check = lambda: {'appserver': 'ok'}
else:
if not callable(health_check):
raise ValueError("health_check param is not callable: {}".format(repr(health_check)))
self._health_check = health_check
def urls(self):
"""
Returns:
The urlpatterns to be added to the main app
"""
return [
url(r'^_health$', self.health, name='_health'),
url(r'^_echo$', self.echo, name='_echo'),
url(r'^_stacks$', self.stacks, name='_stacks'),
url(r'^_heap$', self.heap, name='_heap'),
url(r'^_version$', self.version, name='_version')
]
@check_internal_single_hop
def health(self, request):
"""
Return the health of the app.
"""
try:
result = self._health_check()
except Exception:
logger.exception("Error performing health check")
result = {'appserver': 'health check error'}
sc = 200
for status in result.values():
if not status.startswith('ok'):
sc = 500
break
return HttpResponse(json.dumps(result),
content_type='application/json',
status=sc)
@check_internal_single_hop
def echo(self, request):
"""
Echoes the request information, e.g. headers, cookies, and internal
Django request metadata.
"""
res = {}
res['meta'] = {key: value for key, value in request.META.items()
if isinstance(value, six.string_types)}
res['cookies'] = {key: str(value) for key, value in request.COOKIES.items()}
return HttpResponse(json.dumps(res, indent=2),
content_type='application/json',
status=200)
@check_internal_single_hop
def stacks(self, request):
"""
Dump the stack traces for all threads. The result contains:
pid
Process ID
threads
A dictionary of ``{thread_name, [stack_frames]}``
"""
res = {}
res['threads'] = utils.dump_stacks()
res['pid'] = os.getpid()
return HttpResponse(json.dumps(res, indent=2),
content_type='application/json',
status=200)
@check_internal_single_hop
def heap(self, request):
"""
Display heap usage.
"""
res = utils.analyze_heap()
res['pid'] = os.getpid()
return HttpResponse(json.dumps(res, indent=2),
content_type='application/json',
status=200)
@check_internal_single_hop
def version(self, request):
"""
Display service name ad version based on project convention
"""
res = {}
res['service_name'] = getattr(settings, 'SERVICE_NAME', '<UNKNOWN>')
res['version'] = getattr(settings, 'VERSION_NUMBER', '<UNKNOWN>')
return HttpResponse(json.dumps(res),
content_type='application/json',
status=200)
urlpatterns = AppStatusViews().urls()