UNPKG

resilient-consul

Version:

Resilient HTTP middleware for Consul service discovery and balancing

118 lines (96 loc) 3.48 kB
;(function (root, factory) { if (typeof define === 'function' && define.amd) { define(['exports'], factory) } else if (typeof exports === 'object') { factory(exports) if (typeof module === 'object' && module !== null) { module.exports = exports = exports.resilientConsul } } else { factory(root) } }(this, function (exports) { var requiredParams = ['service', 'servers'] var consulParams = ['service', 'datacenter', 'protocol', 'tag', 'mapServers', 'onlyHealthy'] exports.resilientConsul = function (params) { params = validateParams(params || {}) // Use the built-in servers mapper, if required var mapServers = params.mapServers || (params.onlyHealthy ? mapServersFromHealthEndpoint : mapServersFromCatalogEndpoint) // Define Consul base path based on the lookup type var basePath = params.onlyHealthy ? '/v1/health/service/' : '/v1/catalog/service/' // Define the service specific base path params.basePath = basePath + params.service // Enable self discovery capatibility if (params.discoveryService) { params.refreshPath = basePath + params.discoveryService params.enableSelfRefresh = true } // Middleware constructor function function consul (options, resilient) { defineResilientOptions(params, options) return { // Incoming traffic middleware 'in': function inHandler (err, res, next) { if (err) return next() // resilient.js sometimes calls the middleware function more than once, with the output // of the previous invokation; checking here the type of the items in the response to // only call `mapServers` with service objects, not URLs (strings) if (Array.isArray(res.data) && Object(res.data[0]) === res.data[0]) { res.data = mapServers(res.data) } next() }, // Outgoing traffic middleware 'out': function outHandler (options, next) { options.params = options.params || {} if (params.datacenter) { options.params.dc = params.datacenter } if (params.onlyHealthy) { options.params.passing = true } if (params.tag) { options.params.tag = params.tag } next() } } } // Define middleware type consul.type = 'discovery' // Expose the middleware function return consul function mapServersFromHealthEndpoint (list) { return list.map(function buildHealthServiceUrl (s) { return (params.protocol || 'http') + '://' + s.Service.Address + ':' + (+s.Service.Port || 80) }) } function mapServersFromCatalogEndpoint (list) { return list.map(function buildCatalogServiceUrl (s) { return (params.protocol || 'http') + '://' + (s.ServiceAddress || s.Address) + ':' + (+s.ServicePort || 80) }) } } function validateParams (params) { var missing = requiredParams.filter(function (key) { return !params[key] }) if (missing.length) { throw new TypeError('Missing required params: ' + missing.join(', ')) } return params } function defineResilientOptions (params, options) { Object.keys(params) .filter(function (key) { return !~consulParams.indexOf(key) }) .forEach(function (key) { options.set(key, params[key]) }) } }))