kongadmin
Version:
Kong admin GUI
262 lines (219 loc) • 10.1 kB
JavaScript
/**
* Created by pang on 22/4/2017.
*/
var events = require('events');
var _ = require('lodash')
var eventEmitter = new events.EventEmitter();
var cron = require('node-cron');
var path = require('path')
var tasks = {}
var KongService = require('../services/KongService')
var moment = require('moment')
var nodemailer = require('nodemailer');
var mg = require('nodemailer-mailgun-transport');
var notificationsInterval = 30;
var sendmail = require('sendmail')({
logger: {
debug: console.log,
info: console.info,
warn: console.warn,
error: console.error
},
silent: false
})
module.exports = {
emit : function(event,data) {
eventEmitter.emit(event,data)
},
addListener : function(event,fn) {
eventEmitter.addListener(event, fn);
},
start : function(node) {
if(tasks[node.id] && tasks[node.id].isStarted) return false;
sails.log('Start scheduled health checks for node ' + node.id);
var self = this;
sails.models.kongnode.findOne({
id:node.id
}).exec(function(err,node){
if(!err && node) {
if(node.health_check_details) {
tasks[node.id] = node.health_check_details
tasks[node.id].cron = self.createCron(node)
}else{
tasks[node.id] = {
cron : self.createCron(node)
}
}
tasks[node.id].timesFailed = 0;
tasks[node.id].cron.start()
tasks[node.id].isStarted = true;
}
})
},
stop : function(node) {
if(tasks[node.id]) {
sails.log('Stopping health check for node ' + node.id);
tasks[node.id].cron.stop()
tasks[node.id].isStarted = false;
//delete tasks[node.id]
}
},
createCron : function(node) {
var self = this;
return cron.schedule('* * * * *', function() {
sails.log('Performing health check for node ' + node.id);
KongService.nodeStatus(node,function(err,data){
tasks[node.id].lastChecked = new Date();
if(err) {
if(!tasks[node.id].firstFailed) tasks[node.id].firstFailed = new Date();
tasks[node.id].firstSucceeded = null;
tasks[node.id].lastFailed = new Date();
tasks[node.id].isHealthy = false;
tasks[node.id].timesFailed++;
sails.log('health_checks:cron:checkStatus => Health check for node ' + node.id + ' failed ' + tasks[node.id].timesFailed + ' times');
var timeDiff = self.getMinutesDiff(new Date(),tasks[node.id].lastNotified)
sails.log('health_checks:cron:checkStatus:last notified => ' + tasks[node.id].lastNotified);
sails.log('health_checks:cron:checkStatus => Checking if eligible for notification',timeDiff);
if(!tasks[node.id].lastNotified || timeDiff > notificationsInterval) {
self.notify(node)
}
}else{
sails.log('Health check for node ' + node.id + ' succeeded',data);
if(!tasks[node.id].firstSucceeded) tasks[node.id].firstSucceeded = new Date();
tasks[node.id].timesFailed = 0;
tasks[node.id].isHealthy = true;
tasks[node.id].firstFailed = null;
tasks[node.id].lastSucceeded = new Date();
}
self.updateNodeHealthCheckDetails(node.id)
})
})
},
updateNodeHealthCheckDetails : function(nodeId) {
var data = _.cloneDeep(tasks[nodeId])
delete(data.cron)
sails.models.kongnode.update({id: nodeId},{
health_check_details : data
}).exec(function(err,updated){
// Fire and forger for now
if(err) {
sails.log("health_checks:updateNodeHealthCheckDetails:failed",err)
}else{
sails.sockets.blast('node.health_checks', _.merge({node_id:nodeId},data));
}
})
},
createTransporter : function(cb) {
// Get active transport
sails.models.settings.find().limit(1)
.exec(function(err,settings){
if(err) return cb(err)
sails.log("helath_checks:createTransporter:settings =>",settings)
if(!settings.length
|| !settings[0].data
//|| !settings[0].data.email_notifications
|| !settings[0].data.notify_when.node_down.active) return cb()
sails.log("health_checks:createTransporter => trying to get transport",{
"notifications_enabled" : settings[0].data.email_notifications,
"transport_name" : settings[0].data.default_transport
})
sails.models.emailtransport.findOne({
name : settings[0].data.default_transport
}).exec(function(err,transport){
if(err) return cb(err)
sails.log("health_checks:createTransporter:transport =>",transport)
if(!transport) return cb()
var result = {
settings : settings[0].data,
}
switch(settings[0].data.default_transport) {
case "smtp":
result.transporter = nodemailer.createTransport(transport.settings)
break;
case "mailgun":
result.transporter = nodemailer.createTransport(mg(transport.settings))
break;
}
return cb(null,result);
})
})
},
notify : function(node) {
var self = this
self.createTransporter(function(err,result){
if(err || !result) {
sails.log("health_check:failed to create transporter. No notification will be sent.",err)
}else{
var transporter = result.transporter
var html = self.makeNotificationHTML(node)
var settings = result.settings
self.getAdminEmailsList(function(err,receivers){
sails.log("health_checks:notify:receivers => ", receivers)
if(!err && receivers.length) {
var mailOptions = {
from: '"' + settings.email_default_sender_name + '" <' + settings.email_default_sender + '>', // sender address
to: receivers.join(","), // list of receivers
subject: 'A node is down or unresponsive', // Subject line
html: html
};
if(settings.default_transport == 'sendmail') {
sendmail(mailOptions, function(err, reply) {
if(err){
sails.log.error("Health_checks:notify:error",err)
}else{
sails.log.info("Health_checks:notify:success",reply)
tasks[node.id].lastNotified = new Date();
self.updateNodeHealthCheckDetails(node.id)
}
});
}else{
transporter.sendMail(mailOptions, function(error, info){
if(error){
sails.log.error("Health_checks:notify:error",error)
}else{
sails.log.info("Health_checks:notify:success",info)
tasks[node.id].lastNotified = new Date();
self.updateNodeHealthCheckDetails(node.id)
}
});
}
}
})
}
})
},
makeNotificationHTML : function makeNotificationHTML(node) {
var duration = moment.duration(moment().diff(moment(tasks[node.id].lastSucceeded))).humanize()
var html = '<p>A Kong Node is down or unresponsive for more than ' + duration + '</p>' +
'<table style="border: 1px solid #ccc;background-color: #eaeaea">' +
'<tr>' +
'<th style="text-align: left">Id</th>' +
'<td style="text-align: left">' + node.id + '</td>' +
'</tr>' +
'<tr>' +
'<th style="text-align: left">Name</th>' +
'<td style="text-align: left">' + node.name + '</td>' +
'</tr>' +
'<tr>' +
'<th style="text-align: left">Kong Admin URL</th>' +
'<td style="text-align: left">' + node.kong_admin_url + '</td>' +
'</tr>' +
'</table>';
return html;
},
getMinutesDiff : function(start,end) {
var duration = moment.duration(moment(start).diff(moment(end)));
return duration.asMinutes();
},
getAdminEmailsList : function(cb) {
sails.models.user.find({
admin : true
}).exec(function(err,admins){
if(err) return cb(err)
if(!admins.length) return cb([])
return cb(null,admins.map(function(item){
return item.email;
}))
})
}
}