consul
Version:
Consul client
367 lines (286 loc) • 8.17 kB
JavaScript
/**
* Helper functions
*/
;
/**
* Module dependencies.
*/
var constants = require('./constants');
/**
* Body
*/
function body(request, next) {
if (request.err) return next(false, request.err, undefined, request.res);
next(false, undefined, request.res.body, request.res);
}
/**
* First item in body
*/
function bodyItem(request, next) {
if (request.err) return next(false, request.err, undefined, request.res);
if (request.res.body && request.res.body.length) {
return next(false, undefined, request.res.body[0], request.res);
}
next(false, undefined, undefined, request.res);
}
/**
* Empty
*/
function empty(request, next) {
if (request.err) return next(false, request.err, undefined, request.res);
next(false, undefined, undefined, request.res);
}
/**
* Normalize keys
*/
function normalizeKeys(obj) {
var result = {};
if (obj) {
for (var name in obj) {
if (obj.hasOwnProperty(name)) {
result[name.replace(/_/g, '').toLowerCase()] = obj[name];
}
}
}
return result;
}
/**
* Defaults
*/
function defaults(obj) {
if (!obj) obj = {};
var src;
for (var i = 0; i < arguments.length; i++) {
src = arguments[i];
for (var p in src) {
if (src.hasOwnProperty(p) && !obj.hasOwnProperty(p)) {
obj[p] = src[p];
}
}
}
return obj;
}
/**
* Parse duration
*/
function parseDuration(value) {
if (typeof value === 'number') return value / 1e6;
if (typeof value !== 'string') return;
var n;
var m = value.match(/^(\d*\.?\d*)$/);
if (m) {
n = parseFloat(m[1]);
if (!isNaN(n)) return n / 1e6;
}
m = value.match(/^([\d\.]*)(ns|us|ms|s|m|h)$/);
if (!m) return;
n = parseFloat(m[1]);
if (isNaN(n)) return;
return n * constants.DURATION_UNITS[m[2]] / 1e6;
}
/**
* Common options
*/
function options(req, opts, ignore) {
if (!opts) opts = {};
if (!ignore) ignore = {};
if (!req.headers) req.headers = {};
// headers
if (opts.hasOwnProperty('token') && !ignore.token) req.headers['x-consul-token'] = opts.token;
// query
if (!req.query) req.query = {};
if (opts.dc && !ignore.dc) req.query.dc = opts.dc;
if (opts.wan && !ignore.wan) req.query.wan = '1';
if (opts.consistent && !ignore.consistent) {
req.query.consistent = '1';
} else if (opts.stale && !ignore.stale) {
req.query.stale = '1';
}
if (opts.hasOwnProperty('index') && !ignore.index) req.query.index = opts.index;
if (opts.hasOwnProperty('wait') && !ignore.wait) req.query.wait = opts.wait;
if (opts.hasOwnProperty('near') && !ignore.near) req.query.near = opts.near;
if (opts.hasOwnProperty('node-meta') && !ignore['node-meta']) {
req.query['node-meta'] = opts['node-meta'];
}
// papi
if (opts.hasOwnProperty('ctx') && !ignore.ctx) req.ctx = opts.ctx;
if (opts.hasOwnProperty('timeout') && !ignore.timeout) {
if (typeof opts.timeout === 'string') {
req.timeout = parseDuration(opts.timeout);
} else {
req.timeout = opts.timeout;
}
}
}
/**
* Default common options
*/
function defaultCommonOptions(opts) {
opts = normalizeKeys(opts);
var defaults;
constants.DEFAULT_OPTIONS.forEach(function(key) {
if (!opts.hasOwnProperty(key)) return;
if (!defaults) defaults = {};
defaults[key] = opts[key];
});
return defaults;
}
/**
* Decode value
*/
function decode(value, opts) {
if (typeof value !== 'string') return value;
value = Buffer.from(value, 'base64');
if (!opts || !opts.buffer) value = value.toString();
return value;
}
/**
* Shallow clone
*/
function clone(src) {
var dst = {};
for (var key in src) {
if (src.hasOwnProperty(key)) {
dst[key] = src[key];
}
}
return dst;
}
/**
* Set timeout with cancel support
*/
function setTimeoutContext(fn, ctx, timeout) {
var id;
var cancel = function() {
clearTimeout(id);
};
id = setTimeout(function() {
ctx.removeListener('cancel', cancel);
fn();
}, timeout);
ctx.once('cancel', cancel);
}
/**
* Set interval with cancel support
*/
function setIntervalContext(fn, ctx, timeout) {
var id;
var cancel = function() {
clearInterval(id);
};
id = setInterval(function() { fn(); }, timeout);
ctx.once('cancel', cancel);
}
/**
* Create node/server-level check object
* Corresponds to CheckType in Consul Agent Endpoint:
* https://github.com/hashicorp/consul/blob/master/command/agent/check.go#L43
* Corresponds to AgentServiceCheck in Consul Go API (which currently omits Notes):
* https://github.com/hashicorp/consul/blob/master/api/agent.go#L66
* Currently omits ID and Name fields:
* https://github.com/hashicorp/consul/issues/2223
*/
function _createServiceCheck(src) {
var dst = {};
if ((src.grpc || src.http || src.tcp || src.args || src.script) && src.interval) {
if (src.grpc) {
dst.GRPC = src.grpc;
if (src.hasOwnProperty('grpcusetls')) dst.GRPCUseTLS = src.grpcusetls;
} else if (src.http) {
dst.HTTP = src.http;
if (src.hasOwnProperty('tlsskipverify')) dst.TLSSkipVerify = src.tlsskipverify;
} else if (src.tcp){
dst.TCP = src.tcp;
} else {
if (src.args) {
dst.Args = src.args;
} else {
dst.Script = src.script;
}
if (src.hasOwnProperty('dockercontainerid')) dst.DockerContainerID = src.dockercontainerid;
if (src.hasOwnProperty('shell')) dst.Shell = src.shell;
}
dst.Interval = src.interval;
if (src.hasOwnProperty('timeout')) dst.Timeout = src.timeout;
} else if (src.ttl) {
dst.TTL = src.ttl;
} else {
throw new Error('args/grpc/http/tcp and interval, or ttl');
}
if (src.hasOwnProperty('notes')) dst.Notes = src.notes;
if (src.hasOwnProperty('status')) dst.Status = src.status;
if (src.hasOwnProperty('deregistercriticalserviceafter')) {
dst.DeregisterCriticalServiceAfter = src.deregistercriticalserviceafter;
}
return dst;
}
function createServiceCheck(src) {
return _createServiceCheck(normalizeKeys(src));
}
/**
* Create standalone check object
* Corresponds to CheckDefinition in Consul Agent Endpoint:
* https://github.com/hashicorp/consul/blob/master/command/agent/structs.go#L47
* Corresponds to AgentCheckRegistration in Consul Go API:
* https://github.com/hashicorp/consul/blob/master/api/agent.go#L57
*/
function createCheck(src) {
src = normalizeKeys(src);
var dst = _createServiceCheck(src);
if (src.name) {
dst.Name = src.name;
} else {
throw new Error('name required');
}
if (src.hasOwnProperty('id')) dst.ID = src.id;
if (src.hasOwnProperty('serviceid')) dst.ServiceID = src.serviceid;
return dst;
}
/**
* Has the Consul index changed.
*/
function hasIndexChanged(index, prevIndex) {
if (typeof index !== 'string' || !index) return false;
if (typeof prevIndex !== 'string' || !prevIndex) return true;
return index !== prevIndex;
}
/**
* Parse query meta
*/
function parseQueryMeta(res) {
var meta = {};
if (res && res.headers) {
if (res.headers['x-consul-index']) {
meta.LastIndex = res.headers['x-consul-index'];
}
if (res.headers['x-consul-lastcontact']) {
meta.LastContact = parseInt(res.headers['x-consul-lastcontact'], 10);
}
if (res.headers['x-consul-knownleader']) {
meta.KnownLeader = res.headers['x-consul-knownleader'] === 'true';
}
if (res.headers['x-consul-translate-addresses']) {
meta.AddressTranslationEnabled = res.headers['x-consul-translate-addresses'] === 'true';
}
}
return meta;
}
/**
* Module exports
*/
exports.body = body;
exports.bodyItem = bodyItem;
exports.decode = decode;
exports.empty = empty;
exports.normalizeKeys = normalizeKeys;
exports.defaults = defaults;
exports.options = options;
exports.defaultCommonOptions = defaultCommonOptions;
exports.clone = clone;
exports.parseDuration = parseDuration;
exports.setTimeoutContext = setTimeoutContext;
exports.setIntervalContext = setIntervalContext;
exports.createServiceCheck = createServiceCheck;
exports.createCheck = createCheck;
exports.hasIndexChanged = hasIndexChanged;
exports.parseQueryMeta = parseQueryMeta;