We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Add liveness and readiness endpoints
Add `/healthz/` and `/readiness/` to check the health of the app and it's ability to serve traffic. 

Needed for the k8s deployment.

See also mdn's example https://github.com/mozilla/kuma/blob/master/kuma/health/views.py

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `kitsune/sumo/urls.py`
Content:
```
1 from django.conf.urls import patterns, url, include
2 from django.views.generic.base import RedirectView
3 
4 from kitsune.sumo import views
5 
6 
7 services_patterns = patterns(
8     '',
9     url('^/monitor$', views.monitor, name='sumo.monitor'),
10     url('^/version$', views.version_check, name='sumo.version'),
11     url('^/error$', views.error, name='sumo.error'),
12 )
13 
14 
15 urlpatterns = patterns(
16     '',
17     url(r'^robots.txt$', views.robots, name='robots.txt'),
18     ('^services', include(services_patterns)),
19 
20     url('^locales$', views.locales, name='sumo.locales'),
21     url('^geoip-suggestion$', views.geoip_suggestion,
22         name='sumo.geoip_suggestion'),
23 
24     # Shortcuts:
25     url('^contribute/?$', RedirectView.as_view(url='/get-involved',
26                                                permanent=False)),
27     url(r'^windows7-support(?:\\/)?$',
28         RedirectView.as_view(url='/home/?as=u', permanent=False)),
29 )
30
```

Path: `kitsune/sumo/views.py`
Content:
```
1 import json
2 import logging
3 import os
4 import socket
5 import StringIO
6 
7 import django
8 from django.conf import settings
9 from django.contrib.sites.models import Site
10 from django.http import (HttpResponsePermanentRedirect, HttpResponseRedirect,
11                          HttpResponse, Http404)
12 from django.shortcuts import render
13 from django.utils.translation import ugettext as _
14 from django.views.decorators.cache import never_cache
15 from django.views.decorators.http import require_GET
16 
17 from celery.messaging import establish_connection
18 from mobility.decorators import mobile_template
19 from PIL import Image
20 from session_csrf import anonymous_csrf
21 
22 from kitsune.lib.sumo_locales import LOCALES
23 from kitsune.search import es_utils
24 from kitsune.sumo.decorators import cors_enabled
25 from kitsune.sumo.redis_utils import redis_client, RedisError
26 from kitsune.sumo.urlresolvers import reverse
27 from kitsune.sumo.utils import get_next_url, uselocale
28 from kitsune.users.forms import AuthenticationForm
29 
30 
31 log = logging.getLogger('k.services')
32 
33 
34 @never_cache
35 @mobile_template('sumo/{mobile/}locales.html')
36 def locales(request, template):
37     """The locale switcher page."""
38 
39     return render(request, template, dict(
40         next_url=get_next_url(request) or reverse('home')))
41 
42 
43 def geoip_suggestion(request):
44     """
45     Ajax view to return the localized text for GeoIP locale change suggestion.
46 
47     Takes one parameter from the querystring:
48 
49         * locales - a form encoded list of locales to translate to.
50 
51     Example url: /localize?locales[]=es&locales[]=en-US
52     """
53     locales = request.GET.getlist('locales[]')
54 
55     response = {'locales': {}}
56     for locale in locales:
57         # English and native names for the language
58         response['locales'][locale] = LOCALES[locale]
59         with uselocale(locale):
60             # This is using our JS-style string formatting.
61             response[locale] = {
62                 'suggestion': _('Would you like to view this page in '
63                                 '%(language)s instead?'),
64                 'confirm': _('Yes'),
65                 'cancel': _('No'),
66             }
67 
68     return HttpResponse(json.dumps(response), content_type='application/json')
69 
70 
71 @anonymous_csrf
72 def handle403(request):
73     """A 403 message that looks nicer than the normal Apache forbidden page"""
74     no_cookies = False
75     referer = request.META.get('HTTP_REFERER')
76     if referer:
77         no_cookies = (referer.endswith(reverse('users.login')) or
78                       referer.endswith(reverse('users.register')))
79 
80     return render(request, 'handlers/403.html', {
81         'form': AuthenticationForm(),
82         'no_cookies': no_cookies},
83         status=403)
84 
85 
86 def handle404(request):
87     """A handler for 404s"""
88     return render(request, 'handlers/404.html', status=404)
89 
90 
91 def handle500(request):
92     """A 500 message that looks nicer than the normal Apache error page"""
93     return render(request, 'handlers/500.html', status=500)
94 
95 
96 def redirect_to(request, url, permanent=True, **kwargs):
97     """Like Django's redirect_to except that 'url' is passed to reverse."""
98     dest = reverse(url, kwargs=kwargs)
99     if permanent:
100         return HttpResponsePermanentRedirect(dest)
101 
102     return HttpResponseRedirect(dest)
103 
104 
105 def deprecated_redirect(request, url, **kwargs):
106     """Redirect with an interstitial page telling folks to update their
107     bookmarks.
108     """
109     dest = reverse(url, kwargs=kwargs)
110     proto = 'https://' if request.is_secure() else 'http://'
111     host = Site.objects.get_current().domain
112     return render(request, 'sumo/deprecated.html', {
113         'dest': dest, 'proto': proto, 'host': host})
114 
115 
116 def robots(request):
117     """Generate a robots.txt."""
118     if not settings.ENGAGE_ROBOTS:
119         template = 'User-Agent: *\nDisallow: /'
120     else:
121         template = render(request, 'sumo/robots.html')
122     return HttpResponse(template, content_type='text/plain')
123 
124 
125 def test_memcached(host, port):
126     """Connect to memcached.
127 
128     :returns: True if test passed, False if test failed.
129 
130     """
131     try:
132         s = socket.socket()
133         s.connect((host, port))
134         return True
135     except Exception as exc:
136         log.critical('Failed to connect to memcached (%r): %s' %
137                      ((host, port), exc))
138         return False
139     finally:
140         s.close()
141 
142 
143 ERROR = 'ERROR'
144 INFO = 'INFO'
145 
146 
147 @never_cache
148 def monitor(request):
149     """View for services monitor."""
150     status = {}
151 
152     # Note: To add a new component to the services monitor, do your
153     # testing and then add a name -> list of output tuples map to
154     # status.
155 
156     # Check memcached.
157     memcache_results = []
158     try:
159         for cache_name, cache_props in settings.CACHES.items():
160             result = True
161             backend = cache_props['BACKEND']
162             location = cache_props['LOCATION']
163 
164             # LOCATION can be a string or a list of strings
165             if isinstance(location, basestring):
166                 location = location.split(';')
167 
168             if 'memcache' in backend:
169                 for loc in location:
170                     # TODO: this doesn't handle unix: variant
171                     ip, port = loc.split(':')
172                     result = test_memcached(ip, int(port))
173                     memcache_results.append(
174                         (INFO, '%s:%s %s' % (ip, port, result)))
175 
176         if not memcache_results:
177             memcache_results.append((ERROR, 'memcache is not configured.'))
178 
179         elif len(memcache_results) < 2:
180             memcache_results.append(
181                 (ERROR, ('You should have at least 2 memcache servers. '
182                          'You have %s.' % len(memcache_results))))
183 
184         else:
185             memcache_results.append((INFO, 'memcached servers look good.'))
186 
187     except Exception as exc:
188         memcache_results.append(
189             (ERROR, 'Exception while looking at memcached: %s' % str(exc)))
190 
191     status['memcached'] = memcache_results
192 
193     # Check Libraries and versions
194     libraries_results = []
195     try:
196         Image.new('RGB', (16, 16)).save(StringIO.StringIO(), 'JPEG')
197         libraries_results.append((INFO, 'PIL+JPEG: Got it!'))
198     except Exception as exc:
199         libraries_results.append(
200             (ERROR,
201              'PIL+JPEG: Probably missing: '
202              'Failed to create a jpeg image: %s' % exc))
203 
204     status['libraries'] = libraries_results
205 
206     # Check file paths.
207     msg = 'We want read + write.'
208     filepaths = (
209         (settings.USER_AVATAR_PATH, os.R_OK | os.W_OK, msg),
210         (settings.IMAGE_UPLOAD_PATH, os.R_OK | os.W_OK, msg),
211         (settings.THUMBNAIL_UPLOAD_PATH, os.R_OK | os.W_OK, msg),
212         (settings.GALLERY_IMAGE_PATH, os.R_OK | os.W_OK, msg),
213         (settings.GALLERY_IMAGE_THUMBNAIL_PATH, os.R_OK | os.W_OK, msg),
214         (settings.GALLERY_VIDEO_PATH, os.R_OK | os.W_OK, msg),
215         (settings.GALLERY_VIDEO_THUMBNAIL_PATH, os.R_OK | os.W_OK, msg),
216         (settings.GROUP_AVATAR_PATH, os.R_OK | os.W_OK, msg),
217     )
218 
219     filepath_results = []
220     for path, perms, notes in filepaths:
221         path = os.path.join(settings.MEDIA_ROOT, path)
222         path_exists = os.path.isdir(path)
223         path_perms = os.access(path, perms)
224 
225         if path_exists and path_perms:
226             filepath_results.append(
227                 (INFO, '%s: %s %s %s' % (path, path_exists, path_perms,
228                                          notes)))
229 
230     status['filepaths'] = filepath_results
231 
232     # Check RabbitMQ.
233     rabbitmq_results = []
234     try:
235         rabbit_conn = establish_connection(connect_timeout=5)
236         rabbit_conn.connect()
237         rabbitmq_results.append(
238             (INFO, 'Successfully connected to RabbitMQ.'))
239     except (socket.error, IOError) as exc:
240         rabbitmq_results.append(
241             (ERROR, 'Error connecting to RabbitMQ: %s' % str(exc)))
242 
243     except Exception as exc:
244         rabbitmq_results.append(
245             (ERROR, 'Exception while looking at RabbitMQ: %s' % str(exc)))
246 
247     status['RabbitMQ'] = rabbitmq_results
248 
249     # Check ES.
250     es_results = []
251     try:
252         es_utils.get_doctype_stats(es_utils.all_read_indexes()[0])
253         es_results.append(
254             (INFO, ('Successfully connected to ElasticSearch and index '
255                     'exists.')))
256 
257     except es_utils.ES_EXCEPTIONS as exc:
258         es_results.append(
259             (ERROR, 'ElasticSearch problem: %s' % str(exc)))
260 
261     except Exception as exc:
262         es_results.append(
263             (ERROR, 'Exception while looking at ElasticSearch: %s' % str(exc)))
264 
265     status['ElasticSearch'] = es_results
266 
267     # Check Celery.
268     # start = time.time()
269     # pong = celery.task.ping()
270     # rabbit_results = r = {'duration': time.time() - start}
271     # status_summary['rabbit'] = pong == 'pong' and r['duration'] < 1
272 
273     # Check Redis.
274     redis_results = []
275     if hasattr(settings, 'REDIS_BACKENDS'):
276         for backend in settings.REDIS_BACKENDS:
277             try:
278                 redis_client(backend)
279                 redis_results.append((INFO, '%s: Pass!' % backend))
280             except RedisError:
281                 redis_results.append((ERROR, '%s: Fail!' % backend))
282     status['Redis'] = redis_results
283 
284     status_code = 200
285 
286     status_summary = {}
287     for component, output in status.items():
288         if ERROR in [item[0] for item in output]:
289             status_code = 500
290             status_summary[component] = False
291         else:
292             status_summary[component] = True
293 
294     return render(request, 'services/monitor.html', {
295         'component_status': status,
296         'status_summary': status_summary},
297         status=status_code)
298 
299 
300 @never_cache
301 def error(request):
302     if not getattr(settings, 'STAGE', False):
303         raise Http404
304     # Do something stupid.
305     fu  # noqa
306 
307 
308 @require_GET
309 @never_cache
310 def version_check(request):
311     content_type = 'application/x-json'
312     token = settings.VERSION_CHECK_TOKEN
313     if (token is None or 'token' not in request.GET or
314             token != request.GET['token']):
315         return HttpResponse(status=403, content_type=content_type)
316 
317     versions = {
318         'django': '.'.join(map(str, django.VERSION)),
319     }
320     return HttpResponse(json.dumps(versions), content_type=content_type)
321 
322 
323 @cors_enabled('*')
324 def serve_cors(*args, **kwargs):
325     """A wrapper around django.views.static.serve that adds CORS headers."""
326     if not settings.DEBUG:
327         raise RuntimeError("Don't use kitsune.sumo.views.serve_cors "
328                            "in production.")
329     from django.views.static import serve
330     return serve(*args, **kwargs)
331
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/kitsune/sumo/urls.py b/kitsune/sumo/urls.py
--- a/kitsune/sumo/urls.py
+++ b/kitsune/sumo/urls.py
@@ -20,6 +20,8 @@
     url('^locales$', views.locales, name='sumo.locales'),
     url('^geoip-suggestion$', views.geoip_suggestion,
         name='sumo.geoip_suggestion'),
+    url('^healthz/$', views.liveness, name='sumo.liveness'),
+    url('^readiness/$', views.readiness, name='sumo.readiness'),
 
     # Shortcuts:
     url('^contribute/?$', RedirectView.as_view(url='/get-involved',
diff --git a/kitsune/sumo/views.py b/kitsune/sumo/views.py
--- a/kitsune/sumo/views.py
+++ b/kitsune/sumo/views.py
@@ -7,12 +7,13 @@
 import django
 from django.conf import settings
 from django.contrib.sites.models import Site
+from django.db import DatabaseError
 from django.http import (HttpResponsePermanentRedirect, HttpResponseRedirect,
                          HttpResponse, Http404)
 from django.shortcuts import render
 from django.utils.translation import ugettext as _
-from django.views.decorators.cache import never_cache
-from django.views.decorators.http import require_GET
+from django.views.decorators.cache import never_cache, cache_page
+from django.views.decorators.http import require_GET, require_safe
 
 from celery.messaging import establish_connection
 from mobility.decorators import mobile_template
@@ -26,11 +27,54 @@
 from kitsune.sumo.urlresolvers import reverse
 from kitsune.sumo.utils import get_next_url, uselocale
 from kitsune.users.forms import AuthenticationForm
+from kitsune.wiki.models import Document
 
 
 log = logging.getLogger('k.services')
 
 
+# copied from
+# https://github.com/mozilla/kuma/blob/master/kuma/health/views.py
+@require_safe
+def liveness(request):
+    """
+    A successful response from this endpoint simply proves
+    that Django is up and running. It doesn't mean that its
+    supporting services (like MySQL, Redis, Celery) can
+    be successfully used from within this service.
+    """
+    return HttpResponse(status=204)
+
+
+# copied from
+# https://github.com/mozilla/kuma/blob/master/kuma/health/views.py
+@require_safe
+@cache_page(10)
+def readiness(request):
+    """
+    A successful response from this endpoint goes a step further
+    and means not only that Django is up and running, but also that
+    the database can be successfully used from within this service.
+    Some other supporting services (like ElasticSearch) are not checked,
+    but we may find that we want/need to add them later.
+
+    Wrapped with `cache_page` to test redis cache.
+    """
+    try:
+        # Confirm that we can use the database by making a fast query
+        # against the Document table. It's not important that the document
+        # with the requested primary key exists or not, just that the query
+        # completes without error.
+        Document.objects.filter(pk=1).exists()
+    except DatabaseError as e:
+        reason_tmpl = 'service unavailable due to database issue ({!s})'
+        status, reason = 503, reason_tmpl.format(e)
+    else:
+        status, reason = 204, None
+
+    return HttpResponse(status=status, reason=reason)
+
+
 @never_cache
 @mobile_template('sumo/{mobile/}locales.html')
 def locales(request, template):

{"golden_diff": "diff --git a/kitsune/sumo/urls.py b/kitsune/sumo/urls.py\n--- a/kitsune/sumo/urls.py\n+++ b/kitsune/sumo/urls.py\n@@ -20,6 +20,8 @@\n     url('^locales$', views.locales, name='sumo.locales'),\n     url('^geoip-suggestion$', views.geoip_suggestion,\n         name='sumo.geoip_suggestion'),\n+    url('^healthz/$', views.liveness, name='sumo.liveness'),\n+    url('^readiness/$', views.readiness, name='sumo.readiness'),\n \n     # Shortcuts:\n     url('^contribute/?$', RedirectView.as_view(url='/get-involved',\ndiff --git a/kitsune/sumo/views.py b/kitsune/sumo/views.py\n--- a/kitsune/sumo/views.py\n+++ b/kitsune/sumo/views.py\n@@ -7,12 +7,13 @@\n import django\n from django.conf import settings\n from django.contrib.sites.models import Site\n+from django.db import DatabaseError\n from django.http import (HttpResponsePermanentRedirect, HttpResponseRedirect,\n                          HttpResponse, Http404)\n from django.shortcuts import render\n from django.utils.translation import ugettext as _\n-from django.views.decorators.cache import never_cache\n-from django.views.decorators.http import require_GET\n+from django.views.decorators.cache import never_cache, cache_page\n+from django.views.decorators.http import require_GET, require_safe\n \n from celery.messaging import establish_connection\n from mobility.decorators import mobile_template\n@@ -26,11 +27,54 @@\n from kitsune.sumo.urlresolvers import reverse\n from kitsune.sumo.utils import get_next_url, uselocale\n from kitsune.users.forms import AuthenticationForm\n+from kitsune.wiki.models import Document\n \n \n log = logging.getLogger('k.services')\n \n \n+# copied from\n+# https://github.com/mozilla/kuma/blob/master/kuma/health/views.py\n+@require_safe\n+def liveness(request):\n+    \"\"\"\n+    A successful response from this endpoint simply proves\n+    that Django is up and running. It doesn't mean that its\n+    supporting services (like MySQL, Redis, Celery) can\n+    be successfully used from within this service.\n+    \"\"\"\n+    return HttpResponse(status=204)\n+\n+\n+# copied from\n+# https://github.com/mozilla/kuma/blob/master/kuma/health/views.py\n+@require_safe\n+@cache_page(10)\n+def readiness(request):\n+    \"\"\"\n+    A successful response from this endpoint goes a step further\n+    and means not only that Django is up and running, but also that\n+    the database can be successfully used from within this service.\n+    Some other supporting services (like ElasticSearch) are not checked,\n+    but we may find that we want/need to add them later.\n+\n+    Wrapped with `cache_page` to test redis cache.\n+    \"\"\"\n+    try:\n+        # Confirm that we can use the database by making a fast query\n+        # against the Document table. It's not important that the document\n+        # with the requested primary key exists or not, just that the query\n+        # completes without error.\n+        Document.objects.filter(pk=1).exists()\n+    except DatabaseError as e:\n+        reason_tmpl = 'service unavailable due to database issue ({!s})'\n+        status, reason = 503, reason_tmpl.format(e)\n+    else:\n+        status, reason = 204, None\n+\n+    return HttpResponse(status=status, reason=reason)\n+\n+\n @never_cache\n @mobile_template('sumo/{mobile/}locales.html')\n def locales(request, template):\n", "issue": "Add liveness and readiness endpoints\nAdd `/healthz/` and `/readiness/` to check the health of the app and it's ability to serve traffic. \r\n\r\nNeeded for the k8s deployment.\r\n\r\nSee also mdn's example https://github.com/mozilla/kuma/blob/master/kuma/health/views.py\n", "before_files": [{"content": "from django.conf.urls import patterns, url, include\nfrom django.views.generic.base import RedirectView\n\nfrom kitsune.sumo import views\n\n\nservices_patterns = patterns(\n    '',\n    url('^/monitor$', views.monitor, name='sumo.monitor'),\n    url('^/version$', views.version_check, name='sumo.version'),\n    url('^/error$', views.error, name='sumo.error'),\n)\n\n\nurlpatterns = patterns(\n    '',\n    url(r'^robots.txt$', views.robots, name='robots.txt'),\n    ('^services', include(services_patterns)),\n\n    url('^locales$', views.locales, name='sumo.locales'),\n    url('^geoip-suggestion$', views.geoip_suggestion,\n        name='sumo.geoip_suggestion'),\n\n    # Shortcuts:\n    url('^contribute/?$', RedirectView.as_view(url='/get-involved',\n                                               permanent=False)),\n    url(r'^windows7-support(?:\\\\/)?$',\n        RedirectView.as_view(url='/home/?as=u', permanent=False)),\n)\n", "path": "kitsune/sumo/urls.py"}, {"content": "import json\nimport logging\nimport os\nimport socket\nimport StringIO\n\nimport django\nfrom django.conf import settings\nfrom django.contrib.sites.models import Site\nfrom django.http import (HttpResponsePermanentRedirect, HttpResponseRedirect,\n                         HttpResponse, Http404)\nfrom django.shortcuts import render\nfrom django.utils.translation import ugettext as _\nfrom django.views.decorators.cache import never_cache\nfrom django.views.decorators.http import require_GET\n\nfrom celery.messaging import establish_connection\nfrom mobility.decorators import mobile_template\nfrom PIL import Image\nfrom session_csrf import anonymous_csrf\n\nfrom kitsune.lib.sumo_locales import LOCALES\nfrom kitsune.search import es_utils\nfrom kitsune.sumo.decorators import cors_enabled\nfrom kitsune.sumo.redis_utils import redis_client, RedisError\nfrom kitsune.sumo.urlresolvers import reverse\nfrom kitsune.sumo.utils import get_next_url, uselocale\nfrom kitsune.users.forms import AuthenticationForm\n\n\nlog = logging.getLogger('k.services')\n\n\n@never_cache\n@mobile_template('sumo/{mobile/}locales.html')\ndef locales(request, template):\n    \"\"\"The locale switcher page.\"\"\"\n\n    return render(request, template, dict(\n        next_url=get_next_url(request) or reverse('home')))\n\n\ndef geoip_suggestion(request):\n    \"\"\"\n    Ajax view to return the localized text for GeoIP locale change suggestion.\n\n    Takes one parameter from the querystring:\n\n        * locales - a form encoded list of locales to translate to.\n\n    Example url: /localize?locales[]=es&locales[]=en-US\n    \"\"\"\n    locales = request.GET.getlist('locales[]')\n\n    response = {'locales': {}}\n    for locale in locales:\n        # English and native names for the language\n        response['locales'][locale] = LOCALES[locale]\n        with uselocale(locale):\n            # This is using our JS-style string formatting.\n            response[locale] = {\n                'suggestion': _('Would you like to view this page in '\n                                '%(language)s instead?'),\n                'confirm': _('Yes'),\n                'cancel': _('No'),\n            }\n\n    return HttpResponse(json.dumps(response), content_type='application/json')\n\n\n@anonymous_csrf\ndef handle403(request):\n    \"\"\"A 403 message that looks nicer than the normal Apache forbidden page\"\"\"\n    no_cookies = False\n    referer = request.META.get('HTTP_REFERER')\n    if referer:\n        no_cookies = (referer.endswith(reverse('users.login')) or\n                      referer.endswith(reverse('users.register')))\n\n    return render(request, 'handlers/403.html', {\n        'form': AuthenticationForm(),\n        'no_cookies': no_cookies},\n        status=403)\n\n\ndef handle404(request):\n    \"\"\"A handler for 404s\"\"\"\n    return render(request, 'handlers/404.html', status=404)\n\n\ndef handle500(request):\n    \"\"\"A 500 message that looks nicer than the normal Apache error page\"\"\"\n    return render(request, 'handlers/500.html', status=500)\n\n\ndef redirect_to(request, url, permanent=True, **kwargs):\n    \"\"\"Like Django's redirect_to except that 'url' is passed to reverse.\"\"\"\n    dest = reverse(url, kwargs=kwargs)\n    if permanent:\n        return HttpResponsePermanentRedirect(dest)\n\n    return HttpResponseRedirect(dest)\n\n\ndef deprecated_redirect(request, url, **kwargs):\n    \"\"\"Redirect with an interstitial page telling folks to update their\n    bookmarks.\n    \"\"\"\n    dest = reverse(url, kwargs=kwargs)\n    proto = 'https://' if request.is_secure() else 'http://'\n    host = Site.objects.get_current().domain\n    return render(request, 'sumo/deprecated.html', {\n        'dest': dest, 'proto': proto, 'host': host})\n\n\ndef robots(request):\n    \"\"\"Generate a robots.txt.\"\"\"\n    if not settings.ENGAGE_ROBOTS:\n        template = 'User-Agent: *\\nDisallow: /'\n    else:\n        template = render(request, 'sumo/robots.html')\n    return HttpResponse(template, content_type='text/plain')\n\n\ndef test_memcached(host, port):\n    \"\"\"Connect to memcached.\n\n    :returns: True if test passed, False if test failed.\n\n    \"\"\"\n    try:\n        s = socket.socket()\n        s.connect((host, port))\n        return True\n    except Exception as exc:\n        log.critical('Failed to connect to memcached (%r): %s' %\n                     ((host, port), exc))\n        return False\n    finally:\n        s.close()\n\n\nERROR = 'ERROR'\nINFO = 'INFO'\n\n\n@never_cache\ndef monitor(request):\n    \"\"\"View for services monitor.\"\"\"\n    status = {}\n\n    # Note: To add a new component to the services monitor, do your\n    # testing and then add a name -> list of output tuples map to\n    # status.\n\n    # Check memcached.\n    memcache_results = []\n    try:\n        for cache_name, cache_props in settings.CACHES.items():\n            result = True\n            backend = cache_props['BACKEND']\n            location = cache_props['LOCATION']\n\n            # LOCATION can be a string or a list of strings\n            if isinstance(location, basestring):\n                location = location.split(';')\n\n            if 'memcache' in backend:\n                for loc in location:\n                    # TODO: this doesn't handle unix: variant\n                    ip, port = loc.split(':')\n                    result = test_memcached(ip, int(port))\n                    memcache_results.append(\n                        (INFO, '%s:%s %s' % (ip, port, result)))\n\n        if not memcache_results:\n            memcache_results.append((ERROR, 'memcache is not configured.'))\n\n        elif len(memcache_results) < 2:\n            memcache_results.append(\n                (ERROR, ('You should have at least 2 memcache servers. '\n                         'You have %s.' % len(memcache_results))))\n\n        else:\n            memcache_results.append((INFO, 'memcached servers look good.'))\n\n    except Exception as exc:\n        memcache_results.append(\n            (ERROR, 'Exception while looking at memcached: %s' % str(exc)))\n\n    status['memcached'] = memcache_results\n\n    # Check Libraries and versions\n    libraries_results = []\n    try:\n        Image.new('RGB', (16, 16)).save(StringIO.StringIO(), 'JPEG')\n        libraries_results.append((INFO, 'PIL+JPEG: Got it!'))\n    except Exception as exc:\n        libraries_results.append(\n            (ERROR,\n             'PIL+JPEG: Probably missing: '\n             'Failed to create a jpeg image: %s' % exc))\n\n    status['libraries'] = libraries_results\n\n    # Check file paths.\n    msg = 'We want read + write.'\n    filepaths = (\n        (settings.USER_AVATAR_PATH, os.R_OK | os.W_OK, msg),\n        (settings.IMAGE_UPLOAD_PATH, os.R_OK | os.W_OK, msg),\n        (settings.THUMBNAIL_UPLOAD_PATH, os.R_OK | os.W_OK, msg),\n        (settings.GALLERY_IMAGE_PATH, os.R_OK | os.W_OK, msg),\n        (settings.GALLERY_IMAGE_THUMBNAIL_PATH, os.R_OK | os.W_OK, msg),\n        (settings.GALLERY_VIDEO_PATH, os.R_OK | os.W_OK, msg),\n        (settings.GALLERY_VIDEO_THUMBNAIL_PATH, os.R_OK | os.W_OK, msg),\n        (settings.GROUP_AVATAR_PATH, os.R_OK | os.W_OK, msg),\n    )\n\n    filepath_results = []\n    for path, perms, notes in filepaths:\n        path = os.path.join(settings.MEDIA_ROOT, path)\n        path_exists = os.path.isdir(path)\n        path_perms = os.access(path, perms)\n\n        if path_exists and path_perms:\n            filepath_results.append(\n                (INFO, '%s: %s %s %s' % (path, path_exists, path_perms,\n                                         notes)))\n\n    status['filepaths'] = filepath_results\n\n    # Check RabbitMQ.\n    rabbitmq_results = []\n    try:\n        rabbit_conn = establish_connection(connect_timeout=5)\n        rabbit_conn.connect()\n        rabbitmq_results.append(\n            (INFO, 'Successfully connected to RabbitMQ.'))\n    except (socket.error, IOError) as exc:\n        rabbitmq_results.append(\n            (ERROR, 'Error connecting to RabbitMQ: %s' % str(exc)))\n\n    except Exception as exc:\n        rabbitmq_results.append(\n            (ERROR, 'Exception while looking at RabbitMQ: %s' % str(exc)))\n\n    status['RabbitMQ'] = rabbitmq_results\n\n    # Check ES.\n    es_results = []\n    try:\n        es_utils.get_doctype_stats(es_utils.all_read_indexes()[0])\n        es_results.append(\n            (INFO, ('Successfully connected to ElasticSearch and index '\n                    'exists.')))\n\n    except es_utils.ES_EXCEPTIONS as exc:\n        es_results.append(\n            (ERROR, 'ElasticSearch problem: %s' % str(exc)))\n\n    except Exception as exc:\n        es_results.append(\n            (ERROR, 'Exception while looking at ElasticSearch: %s' % str(exc)))\n\n    status['ElasticSearch'] = es_results\n\n    # Check Celery.\n    # start = time.time()\n    # pong = celery.task.ping()\n    # rabbit_results = r = {'duration': time.time() - start}\n    # status_summary['rabbit'] = pong == 'pong' and r['duration'] < 1\n\n    # Check Redis.\n    redis_results = []\n    if hasattr(settings, 'REDIS_BACKENDS'):\n        for backend in settings.REDIS_BACKENDS:\n            try:\n                redis_client(backend)\n                redis_results.append((INFO, '%s: Pass!' % backend))\n            except RedisError:\n                redis_results.append((ERROR, '%s: Fail!' % backend))\n    status['Redis'] = redis_results\n\n    status_code = 200\n\n    status_summary = {}\n    for component, output in status.items():\n        if ERROR in [item[0] for item in output]:\n            status_code = 500\n            status_summary[component] = False\n        else:\n            status_summary[component] = True\n\n    return render(request, 'services/monitor.html', {\n        'component_status': status,\n        'status_summary': status_summary},\n        status=status_code)\n\n\n@never_cache\ndef error(request):\n    if not getattr(settings, 'STAGE', False):\n        raise Http404\n    # Do something stupid.\n    fu  # noqa\n\n\n@require_GET\n@never_cache\ndef version_check(request):\n    content_type = 'application/x-json'\n    token = settings.VERSION_CHECK_TOKEN\n    if (token is None or 'token' not in request.GET or\n            token != request.GET['token']):\n        return HttpResponse(status=403, content_type=content_type)\n\n    versions = {\n        'django': '.'.join(map(str, django.VERSION)),\n    }\n    return HttpResponse(json.dumps(versions), content_type=content_type)\n\n\n@cors_enabled('*')\ndef serve_cors(*args, **kwargs):\n    \"\"\"A wrapper around django.views.static.serve that adds CORS headers.\"\"\"\n    if not settings.DEBUG:\n        raise RuntimeError(\"Don't use kitsune.sumo.views.serve_cors \"\n                           \"in production.\")\n    from django.views.static import serve\n    return serve(*args, **kwargs)\n", "path": "kitsune/sumo/views.py"}], "after_files": [{"content": "from django.conf.urls import patterns, url, include\nfrom django.views.generic.base import RedirectView\n\nfrom kitsune.sumo import views\n\n\nservices_patterns = patterns(\n    '',\n    url('^/monitor$', views.monitor, name='sumo.monitor'),\n    url('^/version$', views.version_check, name='sumo.version'),\n    url('^/error$', views.error, name='sumo.error'),\n)\n\n\nurlpatterns = patterns(\n    '',\n    url(r'^robots.txt$', views.robots, name='robots.txt'),\n    ('^services', include(services_patterns)),\n\n    url('^locales$', views.locales, name='sumo.locales'),\n    url('^geoip-suggestion$', views.geoip_suggestion,\n        name='sumo.geoip_suggestion'),\n    url('^healthz/$', views.liveness, name='sumo.liveness'),\n    url('^readiness/$', views.readiness, name='sumo.readiness'),\n\n    # Shortcuts:\n    url('^contribute/?$', RedirectView.as_view(url='/get-involved',\n                                               permanent=False)),\n    url(r'^windows7-support(?:\\\\/)?$',\n        RedirectView.as_view(url='/home/?as=u', permanent=False)),\n)\n", "path": "kitsune/sumo/urls.py"}, {"content": "import json\nimport logging\nimport os\nimport socket\nimport StringIO\n\nimport django\nfrom django.conf import settings\nfrom django.contrib.sites.models import Site\nfrom django.db import DatabaseError\nfrom django.http import (HttpResponsePermanentRedirect, HttpResponseRedirect,\n                         HttpResponse, Http404)\nfrom django.shortcuts import render\nfrom django.utils.translation import ugettext as _\nfrom django.views.decorators.cache import never_cache, cache_page\nfrom django.views.decorators.http import require_GET, require_safe\n\nfrom celery.messaging import establish_connection\nfrom mobility.decorators import mobile_template\nfrom PIL import Image\nfrom session_csrf import anonymous_csrf\n\nfrom kitsune.lib.sumo_locales import LOCALES\nfrom kitsune.search import es_utils\nfrom kitsune.sumo.decorators import cors_enabled\nfrom kitsune.sumo.redis_utils import redis_client, RedisError\nfrom kitsune.sumo.urlresolvers import reverse\nfrom kitsune.sumo.utils import get_next_url, uselocale\nfrom kitsune.users.forms import AuthenticationForm\nfrom kitsune.wiki.models import Document\n\n\nlog = logging.getLogger('k.services')\n\n\n# copied from\n# https://github.com/mozilla/kuma/blob/master/kuma/health/views.py\n@require_safe\ndef liveness(request):\n    \"\"\"\n    A successful response from this endpoint simply proves\n    that Django is up and running. It doesn't mean that its\n    supporting services (like MySQL, Redis, Celery) can\n    be successfully used from within this service.\n    \"\"\"\n    return HttpResponse(status=204)\n\n\n# copied from\n# https://github.com/mozilla/kuma/blob/master/kuma/health/views.py\n@require_safe\n@cache_page(10)\ndef readiness(request):\n    \"\"\"\n    A successful response from this endpoint goes a step further\n    and means not only that Django is up and running, but also that\n    the database can be successfully used from within this service.\n    Some other supporting services (like ElasticSearch) are not checked,\n    but we may find that we want/need to add them later.\n\n    Wrapped with `cache_page` to test redis cache.\n    \"\"\"\n    try:\n        # Confirm that we can use the database by making a fast query\n        # against the Document table. It's not important that the document\n        # with the requested primary key exists or not, just that the query\n        # completes without error.\n        Document.objects.filter(pk=1).exists()\n    except DatabaseError as e:\n        reason_tmpl = 'service unavailable due to database issue ({!s})'\n        status, reason = 503, reason_tmpl.format(e)\n    else:\n        status, reason = 204, None\n\n    return HttpResponse(status=status, reason=reason)\n\n\n@never_cache\n@mobile_template('sumo/{mobile/}locales.html')\ndef locales(request, template):\n    \"\"\"The locale switcher page.\"\"\"\n\n    return render(request, template, dict(\n        next_url=get_next_url(request) or reverse('home')))\n\n\ndef geoip_suggestion(request):\n    \"\"\"\n    Ajax view to return the localized text for GeoIP locale change suggestion.\n\n    Takes one parameter from the querystring:\n\n        * locales - a form encoded list of locales to translate to.\n\n    Example url: /localize?locales[]=es&locales[]=en-US\n    \"\"\"\n    locales = request.GET.getlist('locales[]')\n\n    response = {'locales': {}}\n    for locale in locales:\n        # English and native names for the language\n        response['locales'][locale] = LOCALES[locale]\n        with uselocale(locale):\n            # This is using our JS-style string formatting.\n            response[locale] = {\n                'suggestion': _('Would you like to view this page in '\n                                '%(language)s instead?'),\n                'confirm': _('Yes'),\n                'cancel': _('No'),\n            }\n\n    return HttpResponse(json.dumps(response), content_type='application/json')\n\n\n@anonymous_csrf\ndef handle403(request):\n    \"\"\"A 403 message that looks nicer than the normal Apache forbidden page\"\"\"\n    no_cookies = False\n    referer = request.META.get('HTTP_REFERER')\n    if referer:\n        no_cookies = (referer.endswith(reverse('users.login')) or\n                      referer.endswith(reverse('users.register')))\n\n    return render(request, 'handlers/403.html', {\n        'form': AuthenticationForm(),\n        'no_cookies': no_cookies},\n        status=403)\n\n\ndef handle404(request):\n    \"\"\"A handler for 404s\"\"\"\n    return render(request, 'handlers/404.html', status=404)\n\n\ndef handle500(request):\n    \"\"\"A 500 message that looks nicer than the normal Apache error page\"\"\"\n    return render(request, 'handlers/500.html', status=500)\n\n\ndef redirect_to(request, url, permanent=True, **kwargs):\n    \"\"\"Like Django's redirect_to except that 'url' is passed to reverse.\"\"\"\n    dest = reverse(url, kwargs=kwargs)\n    if permanent:\n        return HttpResponsePermanentRedirect(dest)\n\n    return HttpResponseRedirect(dest)\n\n\ndef deprecated_redirect(request, url, **kwargs):\n    \"\"\"Redirect with an interstitial page telling folks to update their\n    bookmarks.\n    \"\"\"\n    dest = reverse(url, kwargs=kwargs)\n    proto = 'https://' if request.is_secure() else 'http://'\n    host = Site.objects.get_current().domain\n    return render(request, 'sumo/deprecated.html', {\n        'dest': dest, 'proto': proto, 'host': host})\n\n\ndef robots(request):\n    \"\"\"Generate a robots.txt.\"\"\"\n    if not settings.ENGAGE_ROBOTS:\n        template = 'User-Agent: *\\nDisallow: /'\n    else:\n        template = render(request, 'sumo/robots.html')\n    return HttpResponse(template, content_type='text/plain')\n\n\ndef test_memcached(host, port):\n    \"\"\"Connect to memcached.\n\n    :returns: True if test passed, False if test failed.\n\n    \"\"\"\n    try:\n        s = socket.socket()\n        s.connect((host, port))\n        return True\n    except Exception as exc:\n        log.critical('Failed to connect to memcached (%r): %s' %\n                     ((host, port), exc))\n        return False\n    finally:\n        s.close()\n\n\nERROR = 'ERROR'\nINFO = 'INFO'\n\n\n@never_cache\ndef monitor(request):\n    \"\"\"View for services monitor.\"\"\"\n    status = {}\n\n    # Note: To add a new component to the services monitor, do your\n    # testing and then add a name -> list of output tuples map to\n    # status.\n\n    # Check memcached.\n    memcache_results = []\n    try:\n        for cache_name, cache_props in settings.CACHES.items():\n            result = True\n            backend = cache_props['BACKEND']\n            location = cache_props['LOCATION']\n\n            # LOCATION can be a string or a list of strings\n            if isinstance(location, basestring):\n                location = location.split(';')\n\n            if 'memcache' in backend:\n                for loc in location:\n                    # TODO: this doesn't handle unix: variant\n                    ip, port = loc.split(':')\n                    result = test_memcached(ip, int(port))\n                    memcache_results.append(\n                        (INFO, '%s:%s %s' % (ip, port, result)))\n\n        if not memcache_results:\n            memcache_results.append((ERROR, 'memcache is not configured.'))\n\n        elif len(memcache_results) < 2:\n            memcache_results.append(\n                (ERROR, ('You should have at least 2 memcache servers. '\n                         'You have %s.' % len(memcache_results))))\n\n        else:\n            memcache_results.append((INFO, 'memcached servers look good.'))\n\n    except Exception as exc:\n        memcache_results.append(\n            (ERROR, 'Exception while looking at memcached: %s' % str(exc)))\n\n    status['memcached'] = memcache_results\n\n    # Check Libraries and versions\n    libraries_results = []\n    try:\n        Image.new('RGB', (16, 16)).save(StringIO.StringIO(), 'JPEG')\n        libraries_results.append((INFO, 'PIL+JPEG: Got it!'))\n    except Exception as exc:\n        libraries_results.append(\n            (ERROR,\n             'PIL+JPEG: Probably missing: '\n             'Failed to create a jpeg image: %s' % exc))\n\n    status['libraries'] = libraries_results\n\n    # Check file paths.\n    msg = 'We want read + write.'\n    filepaths = (\n        (settings.USER_AVATAR_PATH, os.R_OK | os.W_OK, msg),\n        (settings.IMAGE_UPLOAD_PATH, os.R_OK | os.W_OK, msg),\n        (settings.THUMBNAIL_UPLOAD_PATH, os.R_OK | os.W_OK, msg),\n        (settings.GALLERY_IMAGE_PATH, os.R_OK | os.W_OK, msg),\n        (settings.GALLERY_IMAGE_THUMBNAIL_PATH, os.R_OK | os.W_OK, msg),\n        (settings.GALLERY_VIDEO_PATH, os.R_OK | os.W_OK, msg),\n        (settings.GALLERY_VIDEO_THUMBNAIL_PATH, os.R_OK | os.W_OK, msg),\n        (settings.GROUP_AVATAR_PATH, os.R_OK | os.W_OK, msg),\n    )\n\n    filepath_results = []\n    for path, perms, notes in filepaths:\n        path = os.path.join(settings.MEDIA_ROOT, path)\n        path_exists = os.path.isdir(path)\n        path_perms = os.access(path, perms)\n\n        if path_exists and path_perms:\n            filepath_results.append(\n                (INFO, '%s: %s %s %s' % (path, path_exists, path_perms,\n                                         notes)))\n\n    status['filepaths'] = filepath_results\n\n    # Check RabbitMQ.\n    rabbitmq_results = []\n    try:\n        rabbit_conn = establish_connection(connect_timeout=5)\n        rabbit_conn.connect()\n        rabbitmq_results.append(\n            (INFO, 'Successfully connected to RabbitMQ.'))\n    except (socket.error, IOError) as exc:\n        rabbitmq_results.append(\n            (ERROR, 'Error connecting to RabbitMQ: %s' % str(exc)))\n\n    except Exception as exc:\n        rabbitmq_results.append(\n            (ERROR, 'Exception while looking at RabbitMQ: %s' % str(exc)))\n\n    status['RabbitMQ'] = rabbitmq_results\n\n    # Check ES.\n    es_results = []\n    try:\n        es_utils.get_doctype_stats(es_utils.all_read_indexes()[0])\n        es_results.append(\n            (INFO, ('Successfully connected to ElasticSearch and index '\n                    'exists.')))\n\n    except es_utils.ES_EXCEPTIONS as exc:\n        es_results.append(\n            (ERROR, 'ElasticSearch problem: %s' % str(exc)))\n\n    except Exception as exc:\n        es_results.append(\n            (ERROR, 'Exception while looking at ElasticSearch: %s' % str(exc)))\n\n    status['ElasticSearch'] = es_results\n\n    # Check Celery.\n    # start = time.time()\n    # pong = celery.task.ping()\n    # rabbit_results = r = {'duration': time.time() - start}\n    # status_summary['rabbit'] = pong == 'pong' and r['duration'] < 1\n\n    # Check Redis.\n    redis_results = []\n    if hasattr(settings, 'REDIS_BACKENDS'):\n        for backend in settings.REDIS_BACKENDS:\n            try:\n                redis_client(backend)\n                redis_results.append((INFO, '%s: Pass!' % backend))\n            except RedisError:\n                redis_results.append((ERROR, '%s: Fail!' % backend))\n    status['Redis'] = redis_results\n\n    status_code = 200\n\n    status_summary = {}\n    for component, output in status.items():\n        if ERROR in [item[0] for item in output]:\n            status_code = 500\n            status_summary[component] = False\n        else:\n            status_summary[component] = True\n\n    return render(request, 'services/monitor.html', {\n        'component_status': status,\n        'status_summary': status_summary},\n        status=status_code)\n\n\n@never_cache\ndef error(request):\n    if not getattr(settings, 'STAGE', False):\n        raise Http404\n    # Do something stupid.\n    fu  # noqa\n\n\n@require_GET\n@never_cache\ndef version_check(request):\n    content_type = 'application/x-json'\n    token = settings.VERSION_CHECK_TOKEN\n    if (token is None or 'token' not in request.GET or\n            token != request.GET['token']):\n        return HttpResponse(status=403, content_type=content_type)\n\n    versions = {\n        'django': '.'.join(map(str, django.VERSION)),\n    }\n    return HttpResponse(json.dumps(versions), content_type=content_type)\n\n\n@cors_enabled('*')\ndef serve_cors(*args, **kwargs):\n    \"\"\"A wrapper around django.views.static.serve that adds CORS headers.\"\"\"\n    if not settings.DEBUG:\n        raise RuntimeError(\"Don't use kitsune.sumo.views.serve_cors \"\n                           \"in production.\")\n    from django.views.static import serve\n    return serve(*args, **kwargs)\n", "path": "kitsune/sumo/views.py"}]}