1 | 'use strict';
|
2 |
|
3 | class ServerlessAWSPseudoParameters {
|
4 | constructor(serverless, options) {
|
5 | this.serverless = serverless;
|
6 | this.options = options;
|
7 | this.hooks = {
|
8 | 'before:deploy:deploy': 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 |
|
26 |
|
27 | Object.keys(resources).forEach(identifier => replaceChildNodes(resources[identifier].Properties, identifier));
|
28 |
|
29 | function isDict(v) {
|
30 | return typeof v === 'object' && v !== null && !(v instanceof Array) && !(v instanceof Date);
|
31 | }
|
32 |
|
33 | function isArray(v) {
|
34 | return Object.prototype.toString.call(v) === '[object Array]'
|
35 | }
|
36 |
|
37 | function regions() {
|
38 | return [
|
39 | 'eu-west-1',
|
40 | 'eu-west-2',
|
41 | 'us-east-1',
|
42 | 'us-east-2',
|
43 | 'us-west-2',
|
44 | 'ap-south-1',
|
45 | 'ap-northeast-2',
|
46 | 'ap-southeast-2',
|
47 | 'ap-northeast-1',
|
48 | 'eu-central-1'
|
49 | ]
|
50 | }
|
51 |
|
52 | function containsRegion(v) {
|
53 | return new RegExp(regions().join("|")).test(v);
|
54 | }
|
55 |
|
56 | function replaceChildNodes(dictionary, name) {
|
57 | Object.keys(dictionary).forEach((key) => {
|
58 |
|
59 | let value = dictionary[key];
|
60 |
|
61 | if (typeof value === 'string' && !skipRegionReplace && containsRegion(value)) {
|
62 | const regionFinder = new RegExp(regions().join("|"));
|
63 | value = value.replace(regionFinder, '#{AWS::Region}');
|
64 | }
|
65 |
|
66 |
|
67 | if (typeof value === 'string' && value.search(/#{AWS::([a-zA-Z]+)}/) >= 0) {
|
68 | const aws_regex = /#{AWS::([a-zA-Z]+)}/g;
|
69 |
|
70 | dictionary[key] = {
|
71 | "Fn::Sub": value.replace(aws_regex, '${AWS::$1}')
|
72 | };
|
73 |
|
74 |
|
75 | let m = aws_regex.exec(value);
|
76 | while (m) {
|
77 | consoleLog('AWS Pseudo Parameter: ' + name + '::' + key + ' Replaced ' + yellow(m[1]) + ' with ' + yellow('${AWS::' + m[1] + '}'));
|
78 | m = aws_regex.exec(value);
|
79 | }
|
80 | }
|
81 |
|
82 |
|
83 | if (isDict(value) || isArray(value)) {
|
84 | dictionary[key] = replaceChildNodes(value, name + '::' + key);
|
85 | }
|
86 |
|
87 | });
|
88 | return dictionary;
|
89 | }
|
90 |
|
91 | function yellow(str) {
|
92 | return '\u001B[33m' + str + '\u001B[39m';
|
93 | }
|
94 |
|
95 | function underline(str) {
|
96 | return '\u001B[4m' + str + '\u001B[24m';
|
97 | }
|
98 | }
|
99 | }
|
100 |
|
101 | function get(obj, path, def) {
|
102 | return path.split('.').filter(Boolean).every(step => !(step && (obj = obj[step]) === undefined)) ? obj : def;
|
103 | }
|
104 |
|
105 | module.exports = ServerlessAWSPseudoParameters;
|