Skip to content

Commit

Permalink
Add health checks to 0.9.0.dev (GovReady#775)
Browse files Browse the repository at this point in the history
* Add simple health checks.

URLs:

* /health/check-system
* /health/check-vendor-resources
* /health/list-vendor-resources

* remove a bit of leftover cruft

* switch from os.popen to subprocess and add #nosec, for bandit

* add new '/health/' and '/health/load-base' functions
  • Loading branch information
peterkaminski authored and gregelin committed Nov 21, 2019
1 parent d6513e5 commit b968549
Show file tree
Hide file tree
Showing 6 changed files with 365 additions and 0 deletions.
11 changes: 11 additions & 0 deletions check-system.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#!/bin/bash

# use 'head' to suppress display of actual processes, which might expose sensitive information
if [ "$(uname)" == "Darwin" ]; then
top -l 1 | head -10
elif [ "$(expr substr $(uname -s) 1 5)" == "Linux" ]; then
top -n 1 -b | head -5
else
echo "check-system.sh is not supported on this system"
fi

81 changes: 81 additions & 0 deletions check-vendor-resources.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
#!/bin/bash

set -euo pipefail

VENDOR=siteapp/static/vendor

SHACMD="sha256sum"
SHACMD_CHECK="$SHACMD --strict --check"
if ! command -v sha256sum > /dev/null 2>&1 ; then
# On macOS, sha256sum is not available. Use `shasum -a 256` instead.
# But shasum doesn't support --strict and uses --warn instead.
SHACMD="shasum -a 256"
SHACMD_CHECK="$SHACMD --warn --check"
fi

# generated with: $SHACMD `find $VENDOR -type f | sort -f`
$SHACMD_CHECK << EOF
756f2ee1dbc42834e1269591c0b806ba06c04670373b6c2a05c55eae583d2cc7 siteapp/static/vendor/autosize.min.js
ee9d222656eef25ad5e7b0e960a5c363d18084ca333c910e8c81579c45ca4ba5 siteapp/static/vendor/bootstrap-helpers.js
686ed86b10ad84abf3c5d4900f64998ff3f2a2f8765dc2b3032f23d91548df07 siteapp/static/vendor/bootstrap-responsive-tabs.js
c4ea52f9efdd111f33ef6c3eaabc8289e386cac408f1c10b015b773071b4a616 siteapp/static/vendor/bootstrap/css/bootstrap-theme.css
71941b253b8942374cd57b8a85d473489f21311438ba9e3dd4fefa22adbf0f53 siteapp/static/vendor/bootstrap/css/bootstrap-theme.css.map
653e073e97423adda5bc3917a241ee8497dd38a48f14bcde0098a4e54fd0fa5e siteapp/static/vendor/bootstrap/css/bootstrap-theme.min.css
9851e169f044bfc0d5e4a8a761ded531d415ceb7febc1ea585fab070b846d738 siteapp/static/vendor/bootstrap/css/bootstrap-theme.min.css.map
7e630d90c7234b0df1729f62b8f9e4bbfaf293d91a5a0ac46df25f2a6759e39a siteapp/static/vendor/bootstrap/css/bootstrap.css
9cd84a2a5162c816a8bbbd79cf9dc0605bc0ea86e4cd2bab43aee069bb83d266 siteapp/static/vendor/bootstrap/css/bootstrap.css.map
f75e846cc83bd11432f4b1e21a45f31bc85283d11d372f7b19accd1bf6a2635c siteapp/static/vendor/bootstrap/css/bootstrap.min.css
b4a35d19793de445f4622f4d28db279c0242b60228ef304340aad833d012a77d siteapp/static/vendor/bootstrap/css/bootstrap.min.css.map
13634da87d9e23f8c3ed9108ce1724d183a39ad072e73e1b3d8cbf646d2d0407 siteapp/static/vendor/bootstrap/fonts/glyphicons-halflings-regular.eot
42f60659d265c1a3c30f9fa42abcbb56bd4a53af4d83d316d6dd7a36903c43e5 siteapp/static/vendor/bootstrap/fonts/glyphicons-halflings-regular.svg
e395044093757d82afcb138957d06a1ea9361bdcf0b442d06a18a8051af57456 siteapp/static/vendor/bootstrap/fonts/glyphicons-halflings-regular.ttf
a26394f7ede100ca118eff2eda08596275a9839b959c226e15439557a5a80742 siteapp/static/vendor/bootstrap/fonts/glyphicons-halflings-regular.woff
fe185d11a49676890d47bb783312a0cda5a44c4039214094e7957b4c040ef11c siteapp/static/vendor/bootstrap/fonts/glyphicons-halflings-regular.woff2
0abe8deb334de1ba743b04d0399e99eba336afed9da72fc4c0a302c99f9238c8 siteapp/static/vendor/bootstrap/js/bootstrap.js
53964478a7c634e8dad34ecc303dd8048d00dce4993906de1bacf67f663486ef siteapp/static/vendor/bootstrap/js/bootstrap.min.js
c7aa82a1aa7d45224a38d926d2adaff7fe4aef5bcdafa2a47bdac057f4422c2d siteapp/static/vendor/bootstrap/js/npm.js
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 siteapp/static/vendor/emojione/emojione-sprite-24-activity.png
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 siteapp/static/vendor/emojione/[email protected]
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 siteapp/static/vendor/emojione/emojione-sprite-24-diversity.png
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 siteapp/static/vendor/emojione/[email protected]
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 siteapp/static/vendor/emojione/emojione-sprite-24-flags.png
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 siteapp/static/vendor/emojione/[email protected]
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 siteapp/static/vendor/emojione/emojione-sprite-24-food.png
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 siteapp/static/vendor/emojione/[email protected]
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 siteapp/static/vendor/emojione/emojione-sprite-24-nature.png
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 siteapp/static/vendor/emojione/[email protected]
b2a66a73e1a4c14a6b637a987d942c6b676c6033b365efc370fa9fc1a6fa8c8f siteapp/static/vendor/emojione/emojione-sprite-24-objects.png
40ac2aa1a1b90494431990689a69d8e114a7de27d5b8a6121fe0ce9f1f8b3e97 siteapp/static/vendor/emojione/[email protected]
f4324a31aabc175b083d4c136c6cd28fd0718f10d77519ba47525f1efee251b6 siteapp/static/vendor/emojione/emojione-sprite-24-people.png
031c43fb61be40004e1a2a1dc379fe7e0ade4cbf2998e10c9077950f1a58e8c5 siteapp/static/vendor/emojione/[email protected]
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 siteapp/static/vendor/emojione/emojione-sprite-24-regional.png
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 siteapp/static/vendor/emojione/[email protected]
21f2268645db0cf8b5fae40b3c6263840558da4d9277ecac72adbf44fddbea22 siteapp/static/vendor/emojione/emojione-sprite-24-symbols.png
4a2d61983164c43c33dc9b2af772447175fee8ad236d430f385caf3b79184661 siteapp/static/vendor/emojione/[email protected]
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 siteapp/static/vendor/emojione/emojione-sprite-24-travel.png
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 siteapp/static/vendor/emojione/[email protected]
9643c4f2b950f462f71ea15ffab848c949f3fe72a8a4a01e0a082f4d580ac754 siteapp/static/vendor/emojione/emojione-sprite-24.min.css
4f59f47836471cf3f02edfb217afdf107bf29cfe25c424c8c514a32712fc2ee8 siteapp/static/vendor/fontawesome.js
990e7373d100faee6fa7d92c1277695520e3c502f726bb28ce03d2b6d2cd3e6c siteapp/static/vendor/google-fonts.css
6375a7ecbb77ba42e2de22c99aab9fea1fea125d6d857512360a3a555ff74161 siteapp/static/vendor/Hind_400.woff
d7a3280717b1f82f46bee459863720a03de43b16dc8097ba1b133440e5fe0edc siteapp/static/vendor/Hind_400.woff2
a3ef4f13a191d01ecca06b8b997a666b28d4c614d6de256753fa9f4fbe15b726 siteapp/static/vendor/Hind_700.woff
e2f1a473a1649fe316dbddc5cf8f45c525d62b8373d1be395272864c0cf1e60f siteapp/static/vendor/Hind_700.woff2
160a426ff2894252cd7cebbdd6d6b7da8fcd319c65b70468f10b6690c45d02ef siteapp/static/vendor/jquery.js
d55680b958a58d88fb547b694a9dd37d4013ff7e2f64fe776d41c16a10c2f58e siteapp/static/vendor/js-yaml.min.js
7831e273f41fef8485564286f3578d2847754db375befdb48b8ce37e1e1f3a57 siteapp/static/vendor/Lato_900.woff
7d4243c8e973ec0cfc707904891ae4e3efc03dbc8923acb9755f9a35c92269a6 siteapp/static/vendor/Lato_900.woff2
88d16217819282c886700c2d2ed09ca93c4d7a857c5d4769ecb10ae61f72acf3 siteapp/static/vendor/push.js
3a1b43d7e6f821bc71dfad562ccc91fa5b25e7235d16705239a2b13c2165079f siteapp/static/vendor/quill/examples/bubble.html
2bb9bf4545854970d4e43e70b0a82ba3bdc0fc5c8965a215a56452c3446324f8 siteapp/static/vendor/quill/examples/full.html
201a6f13117c2653ab1c06efa2ab702a05d876775d5963548b746b6c5494715e siteapp/static/vendor/quill/examples/snow.html
48b1b42379c43ddbbf6ca013334f983068a10a62f6d223432a166872ec0ec0e9 siteapp/static/vendor/quill/quill.bubble.css
b6235e6b05b8c5d649479fe9f6113622410930ced252e5fceeea53caa3eab7d9 siteapp/static/vendor/quill/quill.core.css
f7089de1eb6ecb869a800a144a38e59c0a7349bf76a655a316af7645161b1531 siteapp/static/vendor/quill/quill.core.js
a4da70cd71b5a0e224e95865829a8356a93907c7d47ebb6b23cb8014c6ff9c48 siteapp/static/vendor/quill/quill.js
de86018869b5e845bdc101fc1b55611a1e375e08af6cee4a681d7446103da611 siteapp/static/vendor/quill/quill.min.js
be1d65da4425b26396c70033141dcbb88d8304738ae67d8625b9920e3b74b02b siteapp/static/vendor/quill/quill.min.js.map
892e299431955e9ae388ae257f72024ee76af2d52a7a97a868f70fbe50f16144 siteapp/static/vendor/quill/quill.snow.css
478b4685b0dd8fd1a7a6455714304b30234e39013c77c0c94a35fcf19e307178 siteapp/static/vendor/textcomplete.min.js
910673bac3223d8e8b8b8380882cc51b26d1727c199b013acc35eca65df7d7a5 siteapp/static/vendor/textcomplete.min.js.map
EOF
5 changes: 5 additions & 0 deletions list-vendor-resources.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#!/bin/bash

VENDOR=siteapp/static/vendor

ls -l `find $VENDOR -type f | sort -f`
8 changes: 8 additions & 0 deletions siteapp/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

import siteapp.views as views
import siteapp.views_landing as views_landing
import siteapp.views_health as views_health
from .good_settings_helpers import signup_wrapper

urlpatterns = [
Expand Down Expand Up @@ -67,6 +68,13 @@
# administration
url(r'^settings$', views.organization_settings),
url(r'^settings/_save$', views.organization_settings_save),

# health
url(r'^health/$', views_health.index),
url(r'^health/check-system$', views_health.check_system),
url(r'^health/check-vendor-resources$', views_health.check_vendor_resources),
url(r'^health/list-vendor-resources$', views_health.list_vendor_resources),
url(r'^health/load-base/(?P<args>.*)$', views_health.load_base),
]

if 'django.contrib.auth.backends.ModelBackend' in settings.AUTHENTICATION_BACKENDS:
Expand Down
37 changes: 37 additions & 0 deletions siteapp/views_health.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import subprocess #nosec

from django.http import HttpResponse
from django.shortcuts import render

def index(request):
output = subprocess.check_output(["./check-system.sh"]).decode("utf-8")
html = (
'<html><body><ul>'
'<li><a href="/health/check-system">check-system</a></li>'
'<li><a href="/health/check-vendor-resources">check-vendor-resources</a></li>'
'<li><a href="/health/list-vendor-resources">list-vendor-resources</a></li>'
'<li><a href="/health/load-base">load-base</a></li>'
'</body></html>' )
return HttpResponse(html)

def check_system(request):
output = subprocess.check_output(["./check-system.sh"]).decode("utf-8")
html = "<html><body><pre>{}</pre></body></html>".format(output)
return HttpResponse(html)

def check_vendor_resources(request):
output = subprocess.check_output(["./check-vendor-resources.sh"]).decode("utf-8")
html = "<html><body><pre>{}</pre></body></html>".format(output)
return HttpResponse(html)

def list_vendor_resources(request):
output = subprocess.check_output(["./list-vendor-resources.sh"]).decode("utf-8")
html = "<html><body><pre>{}</pre></body></html>".format(output)
return HttpResponse(html)

def load_base(request, args):
print(args)
args = args.split(",")
print(args)
context = {'args': args}
return render(request, 'base-conditional.html', context)
223 changes: 223 additions & 0 deletions templates/base-conditional.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,223 @@
{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">

<title>{% block title %}{% block head_title %}{% endblock %}{% endblock %} - GovReady-Q</title>

{% comment %}
<meta name="description" content="">
{% endcomment %}
<link rel="shortcut icon" href="/static/img/brand/favicon.ico" type="image/x-icon">
<link rel="icon" href="/static/img/brand/favicon.ico" type="image/x-icon">
{% comment %}replace with image and HTML generated by http://realfavicongenerator.net/ one day{% endcomment %}

{% if 'bootstrap.min.css' in args %}
<link rel="stylesheet" href="{% static "vendor/bootstrap/css/bootstrap.min.css" %}">
{% endif %}
{% if 'google-fonts.css' in args %}
<link rel="stylesheet" href="{% static "vendor/google-fonts.css" %}">
{% endif %}
{% if 'govready-q.css' in args %}
<link rel="stylesheet" href="{% static "css/govready-q.css" %}">
{% endif %}
{% if 'bootstrap-theme.min.css' in args %}
<link rel="stylesheet" href="{% static "vendor/bootstrap/css/bootstrap-theme.min.css" %}">
{% endif %}
{% if 'fontawesome.js' in args %}
<script defer src="{% static "vendor/fontawesome.js" %}"></script>
{% endif %}
{% include "head.html" %}

{% block head %}{% endblock %}

<!--[if lt IE 9]>
<script src="static/js/html5-3.6-respond-1.1.0.min.js"></script>
<![endif]-->
</head>
<body>

<!--[if lt IE 8]><p>Internet Explorer version 8 or any modern web browser is required to use this website, sorry.<![endif]-->
<!--[if gt IE 7]><!-->

{% include "navbar.html" %}

{% block contextbar %}
<div id="context-bar">
<div class="container-fluid">
<ol class="breadcrumb">
{% block breadcrumbs %}
{% endblock %}
</ol> <!-- .breadcrumb -->
</div><!-- container -->
</div><!-- div wrapper -->
{% endblock %}

{% if messages %}
<div class="container-fluid" style="margin-top: 1em;">
{% for message in messages %}
{# Django levels are: debug (map to Boostrap 'info'), info, success, warning, error (map to Boostrap 'danger') #}
{% if message.level_tag != "success" %}{# Skip success messages #}
<div class="alert fade in alert-{% if message.level_tag == "error" %}danger{% elif message.level_tag == "debug" %}info{% else %}{{message.level_tag}}{% endif %}">
<button type="button" class="close" data-dismiss="alert">&times;</button>
{{ message }}
</div>
{% endif %}
{% endfor %}
</div>
{% endif %}

<div>Loading <code>base-conditional.html</code> template with args <code>{{ args }}</code>.</div>
<hr />
<div>Choose args from: <code>bootstrap.min.css,google-fonts.css,govready-q.css,bootstrap-theme.min.css,fontawesome.js,jquery.js,bootstrap.min.js,bootstrap-responsive-tabs.js,autosize.min.js,textcomplete.min.js,discussion.js,main.js,bootstrap-helpers.js</code></div>
<hr />
<div>Load: <a href="/health/load-base/bootstrap.min.css,google-fonts.css,govready-q.css,bootstrap-theme.min.css,fontawesome.js,jquery.js,bootstrap.min.js,bootstrap-responsive-tabs.js,autosize.min.js,textcomplete.min.js,discussion.js,main.js,bootstrap-helpers.js">all</a> | <a href="/health/load-base">none</a></div>

{% block body-wide %}
<div class="container-fluid" style="min-height: 70vh;">
{% block body %}
{% endblock %}

{% block content %}
{# allauth uses this block #}
{% endblock %}
</div>

<!-- <hr> -->
<footer>
<div class="container-fluid">
<div class="row">
<div class="col-sm-12">
<div class="row">
<div class="col-xs-12">
&copy; GovReady {% now "Y" %}.
<a href="/privacy">Privacy Policy</a>.
<a href="/terms-of-service">Terms of Service</a>.
</div>
<div class="col-xs-12">
ver {{APP_VERSION_STRING}}
</div>
</div>
</div>
</div>
</footer>
{% endblock %}

{% include "bootstrap-helpers.html" %}

{% include "portfolios/select-portfolio-modal.html" %}

<div id="invitation_modal" class="modal" tabindex="-1" role="dialog" aria-labelledby="invitation_modal_title" aria-hidden="true" data-url="{% url "send_invitation" %}">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h4 class="modal-title" id="invitation_modal_title">...</h4>
</div>
<div class="modal-body">
<p style="margin: 1em 0 2em 0">Hmm text here.</p>
<form class="form" onsubmit="return send_invitation()">
{% csrf_token %}
<div class="form-group" style="margin-right: 1em">
<label for="invite-user-select">Select user to invite</label>
<br>
<select id="invite-user-select" class="form-control" name="user" onchange="invite_toggle_mode()">
</select>
</div>
<div class="form-group" style="margin-right: 1em; display: none">
<label for="invite-user-email">
Invite by Email
<span>(<a onclick="$('#invite-user-select').val(''); invite_toggle_mode()">back</a>)</span>
</label>
<br>
<input id="invite-user-email" class="form-control" name="email" type="email" placeholder="[email protected]" style="width: 25em;">
</div>
<div id="invite-message-container" style="margin-top: 1em">
<label for="invite-message">Include a message about why you are sending this invitation:</label>
<textarea id="invite-message" class="form-control" style="width: 100%; height: 4em"></textarea>
</div>
<div id="invite-addtoteam-container" style="display: none">
<div class="checkbox" style="margin-top: 1em">
<label>
<input name="add-to-team" type="checkbox"> Add user to system team
</label>
</div>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-success btn-submit" onclick="$('#invitation_modal form').submit()">Invite</button>
</div>
</div>
</div>
</div>

{% if 'jquery.js' in args %}
<script src="{% static "vendor/jquery.js" %}"></script>
{% endif %}
{% if 'bootstrap.min.js' in args %}
<script src="{% static "vendor/bootstrap/js/bootstrap.min.js" %}"></script>
{% endif %}
{% if 'bootstrap-responsive-tabs.js' in args %}
<script src="{% static "vendor/bootstrap-responsive-tabs.js" %}"></script>
{% endif %}
{% if 'autosize.min.js' in args %}
<script src="{% static "vendor/autosize.min.js" %}"> </script>
{% endif %}
{% if 'textcomplete.min.js' in args %}
<script src="{% static "vendor/textcomplete.min.js" %}"> </script>
{% endif %}

{% if 'discussion.js' in args %}
<script src="{% static "js/discussion.js" %}"></script>
{% endif %}
{% if 'main.js' in args %}
<script src="{% static "js/main.js" %}"></script>
{% endif %}
{% if 'bootstrap-helpers.js' in args %}
<script src="{% static "vendor/bootstrap-helpers.js" %}"></script>
{% endif %}
<script>
$(document).ajaxSend(function(event, xhr, settings) { if (!/^https?:.*/.test(settings.url)) xhr.setRequestHeader("X-CSRFToken", "{{ csrf_token }}"); });
</script>

{% if MOUSE_CURSOR_FOLLOWER %}
<script>enableMouseCursorFollower();</script>
{% endif %}

{% if authoring_tool_enabled %}
<script>
var upgrade_app_option_force = false; // can only be set to true in dev console
var upgrade_app_option_confirm = true; // is set to true in dev tools for headless scripting
function upgrade_app(app_id) {
if (upgrade_app_option_confirm
{% if is_question_page %}
&& !confirm("All instances of '{{task.project.root_task.title}}' will be updated.\n\nAre you sure you want to upgrade?"))
{% else %}
&& !confirm("All instances of '{{project.root_task.title}}' will be updated.\n\nAre you sure you want to upgrade?"))
{% endif %}
return;
ajax_with_indicator({
url: "/tasks/_upgrade-app",
method: "POST",
data: {
app: app_id,
force: upgrade_app_option_force ? "true" : "false"
},
keep_indicator_forever: true, // keep the ajax indicator up forever --- it'll go away when we issue the redirect or reload
success: function(res) {
location.reload();
}
})
}
</script>
{% endif %}

{% block scripts %}
{% endblock %}
</body>
</html>

0 comments on commit b968549

Please sign in to comment.