UNPKG

3.45 kBJavaScriptView Raw
1'use strict';
2
3class ServerlessAWSPseudoParameters {
4 constructor(serverless, options) {
5 this.serverless = serverless;
6 this.options = options;
7 this.hooks = {
8 'after:aws:package:finalize:mergeCustomProviderResources': this.addParameters.bind(this),
9 };
10 this.skipRegionReplace = get(serverless.service, 'custom.pseudoParameters.skipRegionReplace', false)
11 }
12
13 addParameters() {
14
15 const resources = this.serverless.service.provider.compiledCloudFormationTemplate.Resources;
16 const skipRegionReplace = this.skipRegionReplace;
17 const consoleLog = this.serverless.cli.consoleLog;
18
19 consoleLog(yellow(underline('AWS Pseudo Parameters')));
20
21 if (skipRegionReplace) {
22 consoleLog('Skipping automatic replacement of regions with account region!');
23 }
24
25 // loop through all resources, and check all (string) properties for any #{AWS::}
26 // reference. If found, replace the value with an Fn::Sub reference
27 Object.keys(resources).forEach(identifier => {
28 if ("Properties" in resources[identifier]) {
29 replaceChildNodes(resources[identifier].Properties, identifier)
30 }
31 });
32
33 function isDict(v) {
34 return typeof v === 'object' && v !== null && !(v instanceof Array) && !(v instanceof Date);
35 }
36
37 function isArray(v) {
38 return Object.prototype.toString.call(v) === '[object Array]'
39 }
40
41 function regions() {
42 return [
43 'eu-west-1',
44 'eu-west-2',
45 'us-east-1',
46 'us-east-2',
47 'us-west-2',
48 'ap-south-1',
49 'ap-northeast-2',
50 'ap-southeast-2',
51 'ap-northeast-1',
52 'eu-central-1'
53 ]
54 }
55
56 function containsRegion(v) {
57 return new RegExp(regions().join("|")).test(v);
58 }
59
60 function replaceChildNodes(dictionary, name) {
61 Object.keys(dictionary).forEach((key) => {
62
63 let value = dictionary[key];
64 // if a region name is mentioned, replace it with a reference (unless we are skipping automatic replacements)
65 if (typeof value === 'string' && !skipRegionReplace && containsRegion(value)) {
66 const regionFinder = new RegExp(regions().join("|"));
67 value = value.replace(regionFinder, '#{AWS::Region}');
68 }
69
70 // we only want to possibly replace strings with an Fn::Sub
71 if (typeof value === 'string' && value.search(/#{AWS::([a-zA-Z]+)}/) >= 0) {
72 const aws_regex = /#{AWS::([a-zA-Z]+)}/g;
73
74 dictionary[key] = {
75 "Fn::Sub": value.replace(aws_regex, '${AWS::$1}')
76 };
77
78 // do some fancy logging
79 let m = aws_regex.exec(value);
80 while (m) {
81 consoleLog('AWS Pseudo Parameter: ' + name + '::' + key + ' Replaced ' + yellow(m[1]) + ' with ' + yellow('${AWS::' + m[1] + '}'));
82 m = aws_regex.exec(value);
83 }
84 }
85
86 // dicts and arrays need to be looped through
87 if (isDict(value) || isArray(value)) {
88 dictionary[key] = replaceChildNodes(value, name + '::' + key);
89 }
90
91 });
92 return dictionary;
93 }
94
95 function yellow(str) {
96 return '\u001B[33m' + str + '\u001B[39m';
97 }
98
99 function underline(str) {
100 return '\u001B[4m' + str + '\u001B[24m';
101 }
102 }
103}
104
105function get(obj, path, def) {
106 return path.split('.').filter(Boolean).every(step => !(step && (obj = obj[step]) === undefined)) ? obj : def;
107}
108
109module.exports = ServerlessAWSPseudoParameters;