HealthCheckController.js 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125
  1. const RedisWrapper = require('../../infrastructure/RedisWrapper')
  2. const rclient = RedisWrapper.client('health_check')
  3. const settings = require('@overleaf/settings')
  4. const logger = require('logger-sharelatex')
  5. const UserGetter = require('../User/UserGetter')
  6. const {
  7. SmokeTestFailure,
  8. runSmokeTests,
  9. } = require('./../../../../test/smoke/src/SmokeTests')
  10. module.exports = {
  11. check(req, res, next) {
  12. if (!settings.siteIsOpen || !settings.editorIsOpen) {
  13. // always return successful health checks when site is closed
  14. res.contentType('application/json')
  15. res.sendStatus(200)
  16. } else {
  17. // detach from express for cleaner stack traces
  18. setTimeout(() => runSmokeTestsDetached(req, res).catch(next))
  19. }
  20. },
  21. checkActiveHandles(req, res, next) {
  22. if (!(settings.maxActiveHandles > 0) || !process._getActiveHandles) {
  23. return next()
  24. }
  25. const activeHandlesCount = (process._getActiveHandles() || []).length
  26. if (activeHandlesCount > settings.maxActiveHandles) {
  27. logger.err(
  28. { activeHandlesCount, maxActiveHandles: settings.maxActiveHandles },
  29. 'exceeded max active handles, failing health check'
  30. )
  31. return res.sendStatus(500)
  32. } else {
  33. logger.debug(
  34. { activeHandlesCount, maxActiveHandles: settings.maxActiveHandles },
  35. 'active handles are below maximum'
  36. )
  37. next()
  38. }
  39. },
  40. checkApi(req, res, next) {
  41. rclient.healthCheck(err => {
  42. if (err) {
  43. logger.err({ err }, 'failed api redis health check')
  44. return res.sendStatus(500)
  45. }
  46. UserGetter.getUserEmail(settings.smokeTest.userId, (err, email) => {
  47. if (err) {
  48. logger.err({ err }, 'failed api mongo health check')
  49. return res.sendStatus(500)
  50. }
  51. if (email == null) {
  52. logger.err({ err }, 'failed api mongo health check (no email)')
  53. return res.sendStatus(500)
  54. }
  55. res.sendStatus(200)
  56. })
  57. })
  58. },
  59. checkRedis(req, res, next) {
  60. return rclient.healthCheck(function (error) {
  61. if (error != null) {
  62. logger.err({ err: error }, 'failed redis health check')
  63. return res.sendStatus(500)
  64. } else {
  65. return res.sendStatus(200)
  66. }
  67. })
  68. },
  69. checkMongo(req, res, next) {
  70. return UserGetter.getUserEmail(
  71. settings.smokeTest.userId,
  72. function (err, email) {
  73. if (err != null) {
  74. logger.err({ err }, 'mongo health check failed, error present')
  75. return res.sendStatus(500)
  76. } else if (email == null) {
  77. logger.err(
  78. { err },
  79. 'mongo health check failed, no emai present in find result'
  80. )
  81. return res.sendStatus(500)
  82. } else {
  83. return res.sendStatus(200)
  84. }
  85. }
  86. )
  87. },
  88. }
  89. function prettyJSON(blob) {
  90. return JSON.stringify(blob, null, 2) + '\n'
  91. }
  92. async function runSmokeTestsDetached(req, res) {
  93. function isAborted() {
  94. return req.aborted
  95. }
  96. const stats = { start: new Date(), steps: [] }
  97. let status, response
  98. try {
  99. try {
  100. await runSmokeTests({ isAborted, stats })
  101. } finally {
  102. stats.end = new Date()
  103. stats.duration = stats.end - stats.start
  104. }
  105. status = 200
  106. response = { stats }
  107. } catch (e) {
  108. let err = e
  109. if (!(e instanceof SmokeTestFailure)) {
  110. err = new SmokeTestFailure('low level error', {}, e)
  111. }
  112. logger.err({ err, stats }, 'health check failed')
  113. status = 500
  114. response = { stats, error: err.message }
  115. }
  116. if (isAborted()) return
  117. res.contentType('application/json')
  118. res.status(status).send(prettyJSON(response))
  119. }