From 6b776f5df0c28193792a551f33de2b2292458183 Mon Sep 17 00:00:00 2001
From: Zachary Carter <zack.carter@gmail.com>
Date: Tue, 19 Jun 2012 15:51:41 -0700
Subject: [PATCH] Add a flag to heartbeat to "deep check" dependent processes -
 issue #1767

---
 bin/router       |  6 ++--
 lib/heartbeat.js | 73 ++++++++++++++++++++++++++++++++++++++++--------
 2 files changed, 66 insertions(+), 13 deletions(-)

diff --git a/bin/router b/bin/router
index 56db15569..dc409f512 100755
--- a/bin/router
+++ b/bin/router
@@ -49,7 +49,10 @@ if (!config.get('browserid_url')) {
 
 // #1 - Setup health check / heartbeat middleware.
 // This is in front of logging on purpose.  see issue #537
-heartbeat.setup(app);
+var browserid_url = urlparse(config.get('browserid_url')).validate().normalize().originOnly();
+heartbeat.setup(app, {
+  dependencies: [browserid_url]
+});
 
 // #2 - logging!  all requests other than __heartbeat__ are logged
 app.use(express.logger({
@@ -119,7 +122,6 @@ wsapi.setup({
 }, app);
 
 // Forward all leftover requests to browserid
-var browserid_url = urlparse(config.get('browserid_url')).validate().normalize().originOnly();
 app.use(function(req, res, next) {
   forward(
     browserid_url+req.url, req, res,
diff --git a/lib/heartbeat.js b/lib/heartbeat.js
index 666e77767..885a435bf 100644
--- a/lib/heartbeat.js
+++ b/lib/heartbeat.js
@@ -4,26 +4,76 @@
 
 const
 urlparse = require('urlparse'),
-logger = require('./logging.js').logger;
+logger = require('./logging.js').logger,
+url = require('url');
 
 // the path that heartbeats live at
 exports.path = '/__heartbeat__';
 
 // a helper function to set up a heartbeat check
-exports.setup = function(app, cb) {
+exports.setup = function(app, options, cb) {
+  var dependencies = [];
+
+  if (typeof options == 'function') {
+    cb = options;
+  } else if (options) {
+    dependencies = options.dependencies;
+  }
+  var count = dependencies.length;
+
   app.use(function(req, res, next) {
     if (req.method === 'GET' && req.path === exports.path) {
-      function ok(yeah) {
+      var checked = 0;
+      var query = url.parse(req.url, true).query;
+      var deep = typeof query.deep != 'undefined';
+      var notOk = [];
+
+      // callback for checking a dependency
+      function checkCB (num) {
+        return function (isOk) {
+          checked++;
+          if (!isOk) {
+            notOk.push(dependencies[num]);
+          }
+
+          // if all dependencies have been checked
+          if (checked == count) {
+            if (notOk.length === 0) {
+              try {
+                if (cb) cb(ok);
+                else ok(true);
+              } catch(e) {
+                logger.error("Exception caught in heartbeat handler: " + e.toString());
+                ok(false, e);
+              }
+            } else {
+                logger.error("Not all dependencies available");
+                ok(false, notOk.join(': failed\n') + ': failed');
+            }
+          }
+        };
+      }
+
+      function ok(yeah, msg) {
         res.writeHead(yeah ? 200 : 500);
-        res.write(yeah ? 'ok' : 'not ok');
+        res.write(yeah ? 'ok' : 'bad');
+        if (msg) res.write(msg);
         res.end();
       }
-      try {
-        if (cb) cb(ok);
-        else ok(true);
-      } catch(e) {
-        logger.error("Exception caught in heartbeat handler: " + e.toString());
-        ok(false);
+
+      // check all dependencies if deep
+      if (deep) {
+        for (var i = 0; i < count; i++) {
+          check(dependencies[i] + exports.path, checkCB(i));
+        }
+      } else {
+        try {
+          if (cb) cb(ok);
+          else ok(true);
+        } catch(e) {
+          logger.error("Exception caught in heartbeat handler: " + e.toString());
+          ok(false);
+        }
       }
     } else {
       return next();
@@ -31,8 +81,9 @@ exports.setup = function(app, cb) {
   });
 };
 
+
 // a function to check the heartbeat of a remote server
-exports.check = function(url, cb) {
+var check = exports.check = function(url, cb) {
   if (typeof url === 'string') url = urlparse(url).normalize().validate();
   else if (typeof url !== 'object') throw "url string or object required as argumnet to heartbeat.check";
   if (!url.port) url.port = (url.scheme === 'http') ? 80 : 443;
-- 
GitLab