serverless-consul-variables
Version:
Read variables from a Consul Server
166 lines (136 loc) • 5.8 kB
JavaScript
;
function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }
var path = _interopDefault(require('path'));
var debugLib = _interopDefault(require('debug'));
var consul = _interopDefault(require('consul'));
var Promise = _interopDefault(require('bluebird'));
var _ = _interopDefault(require('lodash'));
const PROJECT_DIR = path.resolve(__dirname, '..');
const createPrefix = function(filename) {
let prefix = 'serverless.consul.variables';
let pathDesc = path.relative(PROJECT_DIR, filename)
.replace('/', '.')
.replace(/(?:index)?\.js$/, '')
;
prefix += `:${pathDesc}`;
return prefix
.replace(/[^a-z0-9\\.:]/ig, '')
.replace(/[.]{2,}/g, '.')
.replace(/[.]$/, '')
;
};
/**
* Create debug instance
* @param {string} filepath File path
* @return {debug} New debug instance
*/
function createDebug(filepath) {
return debugLib(createPrefix(filepath));
}
const debug = createDebug(__filename);
const CONSUL_PREFIX = 'consul';
class ServerlessConsulVariables {
constructor(serverless, options) {
this._consulSettings = (serverless.service.custom && serverless.service.custom['serverless-consul-variables']['consul_settings']) ? serverless.service.custom['serverless-consul-variables']['consul_settings'] : {};
this._consulClient = consul({...this._consulSettings, promisify: true});
this._enableServiceRegistration = (serverless.service.custom && serverless.service.custom['serverless-consul-variables']['service']['enable_registration']) ? serverless.service.custom['serverless-consul-variables']['service']['enable_registration'] : false;
this._serviceEndpointFilter = (serverless.service.custom && serverless.service.custom['serverless-consul-variables']['service']['enpdoint_filters']) ? serverless.service.custom['serverless-consul-variables']['service']['enpdoint_filters'] : 'api';
this._consulEndpointKeyPath = (serverless.service.custom && serverless.service.custom['serverless-consul-variables']['service']['consul_endpoint_key_path']) ? serverless.service.custom['serverless-consul-variables']['service']['consul_endpoint_key_path'] : '/';
if (this._consulEndpointKeyPath.endsWith('/')) {
this._consulEndpointKeyPath = this._consulEndpointKeyPath
.substring(0, this._consulEndpointKeyPath.length - 1);
}
this.serverless = serverless;
this.options = options;
this.resolvedValues = {};
this.commands = {
consul: {
usage: 'Gets value for Key Path from consul KV',
lifecycleEvents: [
'getValue',
'getEndpoint',
'registerEndpoint'
],
options: {
'get-key': {
usage:
'Specify the Key Path you want to get '
+ '(e.g. "--get-key \'my_folder/key\'")',
required: true,
},
},
},
};
this.hooks = {
'consul:getValue': async () => {
const result = await this._getValueFromConsul.call(this, options['get-key']);
this.serverless.cli.log(result);
},
'after:deploy:deploy': async () => {
if (this._enableServiceRegistration) {
this._generateEndpoints();
}
}
};
const delegate = serverless.variables
.getValueFromSource.bind(serverless.variables);
serverless.variables.getValueFromSource = (variableString) => {
if (variableString.startsWith(`${CONSUL_PREFIX}:`)) {
return this._getValueFromConsul(variableString.split(`${CONSUL_PREFIX}:`)[1]);
}
return delegate(variableString);
};
}
async _getValueFromConsul(variable) {
if (this.resolvedValues[variable]) {
return this.resolvedValues[variable];
}
let data = await this._consulClient.kv.get(variable.startsWith('/') ? variable.substr(1) : variable);
if (!data) {
let errorMessage = `Error getting variable from Consul: ${variable}. Variable not found.`;
throw new this.serverless.classes.Error(errorMessage);
}
this.resolvedValues[variable] = data.Value;
return data.Value;
}
async _generateEndpoints() {
let infoPlugin = this.serverless.pluginManager.plugins.find((plugin) => plugin.constructor.name == 'AwsInfo');
let info = infoPlugin ? infoPlugin.gatheredData.info : null;
let filterRegExp = new RegExp(this._serviceEndpointFilter);
let endpoint;
if (info.endpoint) {
let events = [];
_.forEach(this.serverless.service.functions, (functionObject) => {
let httpEvents = functionObject.events.filter(event => event.http);
events = events.concat(httpEvents);
});
return Promise.map(events, (event) => {
let path$$1;
let method = '';
if (typeof event.http === 'object') {
path$$1 = event.http.path;
method = event.http.method;
} else {
path$$1 = event.http.split(' ')[1];
}
if (filterRegExp.test(path$$1)) {
endpoint = `${info.endpoint}/${path$$1}`;
let consulPath = `${this._consulEndpointKeyPath}/${path$$1}_${method}`;
return this._registerEndpoint(consulPath, endpoint);
}
});
}
}
async _registerEndpoint(consulPath, endpoint) {
debug(`Setting ${consulPath} to '${endpoint}'...`);
let result = await this._consulClient.kv.set(consulPath, endpoint);
if (!result) {
let errorMessage = `Error trying to set ${consulPath} to '${endpoint}'`;
debug(errorMessage);
throw new this.serverless.classes.Error(errorMessage);
}
debug(`Endpoint ${endpoint} registered succesfully in Consul ${consulPath}`);
return result;
}
}
module.exports = ServerlessConsulVariables;