mirror of
https://github.com/serverless/serverless.git
synced 2025-12-08 19:46:03 +00:00
Add plugin hooks to define config variable getters
This commit is contained in:
parent
5dd35b7ba2
commit
02fa190d0b
@ -100,21 +100,29 @@ class Serverless {
|
||||
|
||||
// populate variables after --help, otherwise help may fail to print
|
||||
// (https://github.com/serverless/serverless/issues/2041)
|
||||
return this.variables.populateService(this.pluginManager.cliOptions).then(() => {
|
||||
// merge arrays after variables have been populated
|
||||
// (https://github.com/serverless/serverless/issues/3511)
|
||||
this.service.mergeArrays();
|
||||
return this.variables
|
||||
.populateService(this.pluginManager.cliOptions)
|
||||
.then(() => {
|
||||
// merge arrays after variables have been populated
|
||||
// (https://github.com/serverless/serverless/issues/3511)
|
||||
this.service.mergeArrays();
|
||||
|
||||
// populate function names after variables are loaded in case functions were externalized
|
||||
// (https://github.com/serverless/serverless/issues/2997)
|
||||
this.service.setFunctionNames(this.processedInput.options);
|
||||
// populate function names after variables are loaded in case functions were externalized
|
||||
// (https://github.com/serverless/serverless/issues/2997)
|
||||
this.service.setFunctionNames(this.processedInput.options);
|
||||
|
||||
// validate the service configuration, now that variables are loaded
|
||||
this.service.validate();
|
||||
// initialize hooks
|
||||
return BbPromise.mapSeries(this.pluginManager.getHooks(['initialize']), ({ hook }) =>
|
||||
hook()
|
||||
);
|
||||
})
|
||||
.then(() => {
|
||||
// validate the service configuration, now that variables are loaded
|
||||
this.service.validate();
|
||||
|
||||
// trigger the plugin lifecycle when there's something which should be processed
|
||||
return this.pluginManager.run(this.processedInput.commands);
|
||||
});
|
||||
// trigger the plugin lifecycle when there's something which should be processed
|
||||
return this.pluginManager.run(this.processedInput.commands);
|
||||
});
|
||||
}
|
||||
|
||||
setProvider(name, provider) {
|
||||
|
||||
@ -86,6 +86,7 @@ class PluginManager {
|
||||
|
||||
this.loadCommands(pluginInstance);
|
||||
this.loadHooks(pluginInstance);
|
||||
this.loadVariableGetters(pluginInstance);
|
||||
|
||||
this.plugins.push(pluginInstance);
|
||||
}
|
||||
@ -309,6 +310,21 @@ class PluginManager {
|
||||
});
|
||||
}
|
||||
|
||||
loadVariableGetters(pluginInstance) {
|
||||
_.forEach(pluginInstance.variableGetters || [], ([regex, getterFunc, options]) => {
|
||||
this.serverless.variables.customVariableResolverFuncs[getterFunc.name] = getterFunc.bind(
|
||||
this.serverless.variables
|
||||
);
|
||||
this.serverless.variables.variableResolvers.push([regex, getterFunc.name]);
|
||||
if (options && options.dependendServiceName) {
|
||||
this.serverless.variables.dependentServices.push({
|
||||
name: options.dependendServiceName,
|
||||
method: getterFunc.name,
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
getCommands() {
|
||||
const result = {};
|
||||
|
||||
|
||||
@ -55,6 +55,25 @@ class Variables {
|
||||
this.cfRefSyntax = RegExp(/^(?:\${)?cf(\.[a-zA-Z0-9-]+)?:/g);
|
||||
this.s3RefSyntax = RegExp(/^(?:\${)?s3:(.+?)\/(.+)$/);
|
||||
this.ssmRefSyntax = RegExp(/^(?:\${)?ssm:([a-zA-Z0-9_.\-/]+)[~]?(true|false|split)?/);
|
||||
|
||||
this.customVariableResolverFuncs = {};
|
||||
this.variableResolvers = [
|
||||
[this.slsRefSyntax, 'getValueFromSls'],
|
||||
[this.envRefSyntax, 'getValueFromEnv'],
|
||||
[this.optRefSyntax, 'getValueFromOptions'],
|
||||
[this.selfRefSyntax, 'getValueFromSelf'],
|
||||
[this.fileRefSyntax, 'getValueFromFile'],
|
||||
[this.cfRefSyntax, 'getValueFromCf'],
|
||||
[this.s3RefSyntax, 'getValueFromS3'],
|
||||
[this.stringRefSyntax, 'getValueFromString'],
|
||||
[this.ssmRefSyntax, 'getValueFromSsm'],
|
||||
[this.deepRefSyntax, 'getValueFromDeep'],
|
||||
];
|
||||
this.dependentServices = [
|
||||
{ name: 'CloudFormation', method: 'getValueFromCf' },
|
||||
{ name: 'S3', method: 'getValueFromS3' },
|
||||
{ name: 'SSM', method: 'getValueFromSsm' },
|
||||
];
|
||||
}
|
||||
|
||||
loadVariableSyntax() {
|
||||
@ -74,26 +93,33 @@ class Variables {
|
||||
// ## SERVICE ##
|
||||
// #############
|
||||
disableDepedentServices(func) {
|
||||
const dependentServices = [
|
||||
{ name: 'CloudFormation', method: 'getValueFromCf', original: this.getValueFromCf },
|
||||
{ name: 'S3', method: 'getValueFromS3', original: this.getValueFromS3 },
|
||||
{ name: 'SSM', method: 'getValueFromSsm', original: this.getValueFromSsm },
|
||||
];
|
||||
const dependencyMessage = (configValue, serviceName) =>
|
||||
`Variable dependency failure: variable '${configValue}' references service ${serviceName} but using that service requires a concrete value to be called.`;
|
||||
`Variable dependency failure: variable '${configValue}' references ${serviceName} but using that service requires a concrete value to be called.`;
|
||||
// replace and then restore the methods for obtaining values from dependent services. the
|
||||
// replacement naturally rejects dependencies on these services that occur during prepopulation.
|
||||
// prepopulation is, of course, the process of obtaining the required configuration for using
|
||||
// these services.
|
||||
dependentServices.forEach(dependentService => {
|
||||
this.dependentServices.forEach(dependentService => {
|
||||
// save original
|
||||
dependentService.original =
|
||||
this[dependentService.method] || this.customVariableResolverFuncs[dependentService.method];
|
||||
// knock out
|
||||
this[dependentService.method] = variableString =>
|
||||
BbPromise.reject(dependencyMessage(variableString, dependentService.name));
|
||||
if (this[dependentService.method]) {
|
||||
this[dependentService.method] = variableString =>
|
||||
BbPromise.reject(dependencyMessage(variableString, dependentService.name));
|
||||
} else {
|
||||
this.customVariableResolverFuncs[dependentService.method] = variableString =>
|
||||
BbPromise.reject(dependencyMessage(variableString, dependentService.name));
|
||||
}
|
||||
});
|
||||
return func().finally(() => {
|
||||
dependentServices.forEach(dependentService => {
|
||||
this.dependentServices.forEach(dependentService => {
|
||||
// restore
|
||||
this[dependentService.method] = dependentService.original;
|
||||
if (this[dependentService.method]) {
|
||||
this[dependentService.method] = dependentService.original;
|
||||
} else {
|
||||
this.customVariableResolverFuncs[dependentService.method] = dependentService.original;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
@ -525,27 +551,17 @@ class Variables {
|
||||
if (this.tracker.contains(variableString)) {
|
||||
ret = this.tracker.get(variableString, propertyString);
|
||||
} else {
|
||||
if (variableString.match(this.slsRefSyntax)) {
|
||||
ret = this.getValueFromSls(variableString);
|
||||
} else if (variableString.match(this.envRefSyntax)) {
|
||||
ret = this.getValueFromEnv(variableString);
|
||||
} else if (variableString.match(this.optRefSyntax)) {
|
||||
ret = this.getValueFromOptions(variableString);
|
||||
} else if (variableString.match(this.selfRefSyntax)) {
|
||||
ret = this.getValueFromSelf(variableString);
|
||||
} else if (variableString.match(this.fileRefSyntax)) {
|
||||
ret = this.getValueFromFile(variableString);
|
||||
} else if (variableString.match(this.cfRefSyntax)) {
|
||||
ret = this.getValueFromCf(variableString);
|
||||
} else if (variableString.match(this.s3RefSyntax)) {
|
||||
ret = this.getValueFromS3(variableString);
|
||||
} else if (variableString.match(this.stringRefSyntax)) {
|
||||
ret = this.getValueFromString(variableString);
|
||||
} else if (variableString.match(this.ssmRefSyntax)) {
|
||||
ret = this.getValueFromSsm(variableString);
|
||||
} else if (variableString.match(this.deepRefSyntax)) {
|
||||
ret = this.getValueFromDeep(variableString);
|
||||
} else {
|
||||
for (const [regex, getter] of this.variableResolvers) {
|
||||
if (variableString.match(regex)) {
|
||||
if (this[getter]) {
|
||||
ret = this[getter].bind(this)(variableString);
|
||||
} else {
|
||||
ret = this.customVariableResolverFuncs[getter](variableString);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!ret) {
|
||||
const errorMessage = [
|
||||
`Invalid variable reference syntax for variable ${variableString}.`,
|
||||
' You can only reference env vars, options, & files.',
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user