Why Gemfury? Push, build, and install  RubyGems npm packages Python packages Maven artifacts PHP packages Go Modules Debian packages RPM packages NuGet packages

Repository URL to install this package:

Details    
hub-client / dockerhub / appstatus / views.py
Size: Mime:
# 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()