Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
9 changed files
with
380 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
.DS_Store | ||
|
||
# Sass compilation related files | ||
public/css/*.css | ||
|
||
|
||
# IDEA specific | ||
.idea | ||
*.iml | ||
|
||
# VS Code specific | ||
.vscode | ||
jsconfig.json | ||
typings/* | ||
typings.json | ||
|
||
# Eclipse specific | ||
.jshintrc | ||
.project | ||
.settings/ | ||
|
||
# Settings | ||
config/localSettings.js | ||
|
||
# Logs | ||
logs/* | ||
*.log | ||
|
||
# Dependency directory for NPM | ||
node_modules | ||
|
||
# Frontend files build on startup | ||
public/js/ckeditor | ||
public/js/dragula | ||
|
||
# Startup scripts (sets logging) | ||
dev.sh | ||
dev.bat | ||
debug.sh | ||
|
||
# Key value store database | ||
server/init/store.db | ||
|
||
# KTH Style imported files | ||
public/css/bootstrap | ||
public/css/font-awesome | ||
public/css/fonts | ||
public/css/kth-style | ||
public/img/kth-style | ||
|
||
# Built at startup | ||
bundles | ||
public/js/app/config*.js | ||
|
||
# Docker | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
language: node_js | ||
node_js: | ||
- "6.1" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
MIT License | ||
|
||
Copyright (c) 2016 jhsware | ||
|
||
Permission is hereby granted, free of charge, to any person obtaining a copy | ||
of this software and associated documentation files (the "Software"), to deal | ||
in the Software without restriction, including without limitation the rights | ||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
copies of the Software, and to permit persons to whom the Software is | ||
furnished to do so, subject to the following conditions: | ||
|
||
The above copyright notice and this permission notice shall be included in all | ||
copies or substantial portions of the Software. | ||
|
||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
SOFTWARE. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,61 @@ | ||
# kth-node-monitor | ||
Monitor module for Node.js projects. | ||
[![Build Status](https://travis-ci.org/jhsware/kth-node-monitor.svg?branch=master)](https://travis-ci.org/jhsware/kth-node-monitor) | ||
|
||
Helper utilities for KTH/node-projects (temp location) | ||
|
||
|
||
- no answer from _monitor | ||
- slow response times | ||
- circular dependencies | ||
- service down | ||
|
||
Circular dependecies | ||
- on start up and REQUIRED dep is down | ||
we add subsystems from ../init/api and a dependecy won't be checked | ||
until the apiClient has been correctly initiated so we will be staged | ||
by Netscaler | ||
|
||
- running and REQUIRED dep goes down | ||
we will report fail and be unstaged by Netscaler, when dep is started and | ||
staged again we will report OK and we will be staged by Netscaler again | ||
|
||
- running and REQUIRED dep goes up again | ||
|
||
- if circular deps and roundtrip takes more than 1s | ||
there will be an infinite call loop and the original caller will time out | ||
and if REQUIRED will cause unstaging by Netscaler. Then all deps will be unstaged. | ||
Services will need to be restarted in order to return OK and be staged by Netscaler | ||
|
||
|
||
### Development Notes ### | ||
|
||
If we have issues with recursive rependencies that resolve slowly we will need to implement one or both of the following: | ||
|
||
LIFECYCLE JSON CALLS | ||
|
||
PENDING -- starting shows pending for 30secs regardless of state of REQUIRED dependecies | ||
to allow consuming services to start up, OR if REQUIRED dependencies have status pending | ||
PENDING resolves to OK as text to please Netscaler | ||
OK -- all REQUIRED dependencies are OK | ||
ERROR -- at least one (1) REQUIRED dep is down | ||
|
||
pass formData on requests | ||
|
||
{ | ||
resolved: ['uri/to/service'] | ||
} | ||
|
||
To handle recursive references we need: | ||
|
||
Starting service: | ||
- if required are OK OK | OK | ||
- if required are PENDING OK | PENDING | ||
- if required are ERROR or down OK | PENDING | ||
After 30s: | ||
- if required are OK OK | OK | ||
- if required are PENDING OK | PENDING | ||
- if required are ERROR ERROR | ERROR | ||
Required goes down ERROR | ERROR | ||
Required goes up again | ||
- if required PENDING OK | PENDING | ||
- if required OK OK | OK |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
module.exports.interfaces = require('./interfaces') | ||
require('./utilities') |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
'use strict' | ||
const { createInterface } = require('component-registry') | ||
|
||
module.exports.IHealthCheck = createInterface({ name: 'IHealthCheck' }) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,154 @@ | ||
'use strict' | ||
|
||
/** | ||
* System controller support functions for /monitor | ||
*/ | ||
const { safeGet } = require('safe-utils') | ||
const registry = require('component-registry').globalRegistry | ||
const { createUtility } = require('component-registry') | ||
const Promise = require('bluebird') | ||
|
||
const IHealthCheck = require('./interfaces').IHealthCheck | ||
|
||
function _createApiStatusObj (key, statusCode, required, responseTime) { | ||
var message | ||
if (statusCode === 200) { | ||
message = `API_STATUS: ${key} is OK` + (responseTime ? ` (${responseTime}ms)` : '') + (required ? ' (required)' : '') | ||
} else { | ||
message = `API_STATUS: ${key} responded with status ${statusCode}` + (responseTime ? ` (${responseTime}ms)` : '') | ||
message += (required ? ' (WARNING! This API is required to be ok for system to report ok)' : ' (This API is NOT required to be ok for system to report ok)') | ||
} | ||
|
||
return { | ||
key: key, | ||
statusCode: statusCode, | ||
required: required, | ||
message: message, | ||
responseTime: responseTime | ||
} | ||
} | ||
|
||
const apiCallCache = {} | ||
function _setCache (key, statusObj) { | ||
apiCallCache[key] = { statusObj: statusObj, timestamp: Date.now() } | ||
} | ||
function _getCache (key) { | ||
return apiCallCache[key] | ||
} | ||
createUtility({ | ||
implements: IHealthCheck, | ||
name: 'kth-node-api', | ||
|
||
status: function (endpoint, options) { | ||
// Check that we haven't called this endpoint during the last 1000ms to avoid flooding monitor pages | ||
// or feedback loops | ||
const endpointBaseUri = safeGet(() => endpoint.config.proxyBasePath) | ||
|
||
if (endpointBaseUri === undefined) { | ||
// We couldn't resolve the endpoint | ||
return Promise.resolve(_createApiStatusObj(endpoint.key, 400, options && options.required)) | ||
} else if (safeGet(() => (Date.now() - _getCache(endpointBaseUri).timestamp) < 1000)) { | ||
// We got a hit in the cache | ||
const statusObj = safeGet(() => _getCache(endpointBaseUri).statusObj) | ||
|
||
// Adding cacheTimestamp props to returned object if result was taken from cache | ||
const outp = Object.assign({}, statusObj, { cacheTimestamp: _getCache(endpointBaseUri).timestamp}) | ||
outp.message = outp.message + ' --- cached (' + (Date.now() - _getCache(endpointBaseUri).timestamp) + 'ms)' | ||
return Promise.resolve(outp) | ||
} else { | ||
// We need to perform a request to api _monitor page | ||
// TODO: Set header accepts: application/json | ||
const t0 = Date.now() | ||
return endpoint.client.getAsync({ | ||
uri: endpointBaseUri + '/_monitor' | ||
}).then((data) => { | ||
const statusObj = _createApiStatusObj(endpoint.key, data.statusCode, options && options.required, Date.now() - t0) | ||
_setCache(endpointBaseUri, statusObj) | ||
return Promise.resolve(statusObj) | ||
}) | ||
.catch(() => { | ||
const statusObj = _createApiStatusObj(endpoint.key, 503, options && options.required) | ||
_setCache(endpointBaseUri, statusObj) | ||
return Promise.resolve(statusObj) | ||
}) | ||
} | ||
} | ||
}).registerWith(registry) | ||
|
||
createUtility({ | ||
implements: IHealthCheck, | ||
name: 'kth-node-ldap', | ||
|
||
status: function (ldap, options) { | ||
if (ldap.isOk()) { | ||
return Promise.resolve(_createApiStatusObj('ldap', 200, options && options.required)) | ||
} else { | ||
return Promise.resolve(_createApiStatusObj('ldap', 503, options && options.required)) | ||
} | ||
} | ||
}).registerWith(registry) | ||
|
||
createUtility({ | ||
implements: IHealthCheck, | ||
name: 'kth-node-mongodb', | ||
|
||
status: function (db, options) { | ||
if (db.isOk()) { | ||
return Promise.resolve(_createApiStatusObj('mongodb', 200, options && options.required)) | ||
} else { | ||
return Promise.resolve(_createApiStatusObj('mongodb', 503, options && options.required)) | ||
} | ||
} | ||
}).registerWith(registry) | ||
|
||
createUtility({ | ||
implements: IHealthCheck, | ||
name: 'kth-node-system-check', | ||
|
||
status: function (localSystems, subSystems) { | ||
// Handle if we don't have subsystems | ||
subSystems = subSystems || [Promise.resolve(undefined)] | ||
|
||
// Consolidate all results | ||
return Promise.all(subSystems) | ||
.then((results) => { | ||
const outp = {} | ||
results.forEach((status) => { | ||
if (typeof status === 'object') { | ||
outp[status.key] = status | ||
} | ||
}) | ||
return Promise.resolve(outp) | ||
}) | ||
.then((subSystems) => { | ||
return localSystems.then((result) => Promise.resolve({localSystems: result, subSystems: subSystems})) | ||
}) | ||
.then((result) => { | ||
const subSystems = result.subSystems | ||
const localSystems = result.localSystems | ||
|
||
var systemOk = Object.keys(subSystems).reduce((systemOk, apiKey) => { | ||
return systemOk && (subSystems[apiKey].required ? subSystems[apiKey].statusCode === 200 : true) | ||
}, localSystems.statusCode === 200) | ||
|
||
return Promise.resolve({ | ||
statusCode: (systemOk ? 200 : 503), | ||
message: (systemOk ? 'OK' : 'ERROR'), | ||
subSystems: subSystems | ||
}) | ||
}) | ||
}, | ||
|
||
renderJSON: function (systemHealth) { | ||
return systemHealth | ||
}, | ||
|
||
renderText: function (systemHealth) { | ||
var outp = `APPLICATION_STATUS: ${systemHealth.message}` + '\n' | ||
outp += Object.keys(systemHealth.subSystems).map((apiKey) => { | ||
return systemHealth.subSystems[apiKey].message | ||
}).join('\n') | ||
return outp | ||
} | ||
|
||
}).registerWith(registry) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
{ | ||
"//": "JSHint configuration - http://jshint.com/docs/options/", | ||
"name": "kth-node-monitor", | ||
"version": "0.1.2", | ||
"description": "Helper utilities for KTH/node-projects", | ||
"main": "lib/index.js", | ||
"author": { | ||
"name": "Sebastian Ware", | ||
"email": "sebastian@urbantalk.se", | ||
"url": "https://github.com/jhsware" | ||
}, | ||
"license": "MIT", | ||
"repository": { | ||
"type": "git", | ||
"uri": "https://github.com/jhsware/kth-node-monitor" | ||
}, | ||
"scripts": { | ||
"test": "NODE_ENV=development node_modules/mocha/bin/mocha ./test/test-*.js ./test/**/test-*.js", | ||
"debug-test": "NODE_ENV=development node_modules/mocha/bin/mocha --debug-brk --no-timeouts ./test/test-*.js ./test/**/test-*.js" | ||
}, | ||
"peerDependencies": { | ||
"component-registry": "^0.2.0", | ||
"safe-utils": "^0.1.0", | ||
"bluebird": "^3.4.6" | ||
}, | ||
"devDependencies": { | ||
"chai": "^3.5.0", | ||
"mocha": "^3.1.2", | ||
"component-registry": "^0.2.0", | ||
"safe-utils": "^0.1.0", | ||
"bluebird": "^3.4.6" | ||
}, | ||
"jshintConfig": { | ||
"maxerr": 5 | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
/* eslint-env mocha */ | ||
'use strict' | ||
|
||
// Testing libraries | ||
const expect = require('chai').expect | ||
|
||
// My code | ||
const Promise = require('bluebird') | ||
const registry = require('component-registry').globalRegistry | ||
require('../lib') | ||
const { IHealthCheck } = require('../lib').interfaces | ||
|
||
describe('Utilities', function () { | ||
it('can be found', function () { | ||
['kth-node-api', 'kth-node-ldap', 'kth-node-mongodb', 'kth-node-system-check'].forEach((name) => { | ||
const util = registry.getUtility(IHealthCheck, name) | ||
expect(util).not.to.equal(undefined) | ||
}) | ||
}) | ||
|
||
it('kth-node-system-check writes OK on first line when ok', function (done) { | ||
const systemHealthUtil = registry.getUtility(IHealthCheck, 'kth-node-system-check') | ||
const localSystems = Promise.resolve({ statusCode: 200, message: 'OK' }) | ||
|
||
systemHealthUtil.status(localSystems) | ||
.then((status) => { | ||
const outp = systemHealthUtil.renderText(status) | ||
expect(outp.split('\n')[0].indexOf('APPLICATION_STATUS: OK')).not.to.equal(-1) | ||
done() | ||
}) | ||
}) | ||
|
||
it('kth-node-system-check writes ERRROR on first line when not ok', function (done) { | ||
const systemHealthUtil = registry.getUtility(IHealthCheck, 'kth-node-system-check') | ||
const localSystems = Promise.resolve({ statusCode: 503, message: 'ERROR' }) | ||
|
||
systemHealthUtil.status(localSystems) | ||
.then((status) => { | ||
const outp = systemHealthUtil.renderText(status) | ||
expect(outp.split('\n')[0].indexOf('APPLICATION_STATUS: ERROR')).not.to.equal(-1) | ||
done() | ||
}) | ||
}) | ||
}) |