cloudns-cloudformation-sync 1.2.0 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.prettierrc +5 -0
- package/README.md +4 -2
- package/lib/cloudns-cloudformation-sync.js +45 -30
- package/package.json +5 -5
- package/src/cloudns-cloudformation-sync.ts +76 -42
package/.prettierrc
ADDED
package/README.md
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
# ClouDNS CloudFormation Sync
|
|
2
|
-
|
|
2
|
+
|
|
3
|
+
Copyright (C) Clouden Oy 2020-2023, author Kenneth Falck <kennu@clouden.net>.
|
|
3
4
|
|
|
4
5
|
Released under the MIT license.
|
|
5
6
|
|
|
@@ -29,7 +30,7 @@ Other resource types are also allowed (A, AAAA, ALIAS, etc).
|
|
|
29
30
|
|
|
30
31
|
Use the cloudns-cloudformation-sync command to synchronize ClouDNS records.
|
|
31
32
|
|
|
32
|
-
AWS_PROFILE=xxx cloudns-cloudformation-sync <cloudns-username> <cloudns-password-parameter-name> [ttl]
|
|
33
|
+
AWS_PROFILE=xxx cloudns-cloudformation-sync <cloudns-username> <cloudns-password-parameter-name> [ttl [stackname]]
|
|
33
34
|
|
|
34
35
|
Options:
|
|
35
36
|
|
|
@@ -37,5 +38,6 @@ Options:
|
|
|
37
38
|
<cloudns-username> - ClouDNS API sub-auth-user
|
|
38
39
|
<cloudns-password-parameter-name> - SSM Parameter with the encrypted ClouDNS API password
|
|
39
40
|
[ttl] - Optional TTL for generated records (defaults to 300)
|
|
41
|
+
[stackName] - Optional CloudFormation stack name to limit the exports to scan (defaults to all stacks)
|
|
40
42
|
|
|
41
43
|
You can create your ClouDNS API credentials in the ClouDNS management console.
|
|
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.main = void 0;
|
|
4
4
|
/**
|
|
5
5
|
* Read AWS CloudFormation Exports and autogenerate ClouDNS records based on their names and values.
|
|
6
|
-
* Kenneth Falck <kennu@clouden.net> (C) Clouden Oy 2020
|
|
6
|
+
* Kenneth Falck <kennu@clouden.net> (C) Clouden Oy 2020-2023
|
|
7
7
|
*
|
|
8
8
|
* This tool can be used to autogenerate ClouDNS records for CloudFormation resources like
|
|
9
9
|
* CloudFront distributions and API Gateway domains.
|
|
@@ -19,12 +19,13 @@ exports.main = void 0;
|
|
|
19
19
|
*
|
|
20
20
|
* Other resource types are also allowed (A, AAAA, ALIAS, etc).
|
|
21
21
|
*
|
|
22
|
-
* Command line usage: AWS_PROFILE=xxx ts-node cloudns-cloudformation-sync.ts <cloudns-username> <cloudns-password-parameter-name> [ttl]
|
|
22
|
+
* Command line usage: AWS_PROFILE=xxx ts-node cloudns-cloudformation-sync.ts <cloudns-username> <cloudns-password-parameter-name> [ttl [stackName]]
|
|
23
23
|
*
|
|
24
24
|
* AWS_PROFILE=xxx - Specify your AWS profile in ~/.aws/credentials as an environment variable
|
|
25
25
|
* <cloudns-username> - ClouDNS API sub-auth-user
|
|
26
26
|
* <cloudns-password-parameter-name> - SSM Parameter with the encrypted ClouDNS API password
|
|
27
27
|
* [ttl] - Optional TTL for generated records (defaults to 300)
|
|
28
|
+
* [stackName] - Optional CloudFormation stack name to limit the exports to scan (defaults to all stacks)
|
|
28
29
|
*/
|
|
29
30
|
const client_ssm_1 = require("@aws-sdk/client-ssm");
|
|
30
31
|
const client_cloudformation_1 = require("@aws-sdk/client-cloudformation");
|
|
@@ -33,17 +34,20 @@ const querystring = require("querystring");
|
|
|
33
34
|
// Load ~/.aws/config
|
|
34
35
|
process.env.AWS_SDK_LOAD_CONFIG = '1';
|
|
35
36
|
async function cloudnsRestCall(cloudnsUsername, cloudnsPassword, method, relativeUrl, queryOptions) {
|
|
36
|
-
let fullUrl = 'https://api.cloudns.net' +
|
|
37
|
-
|
|
38
|
-
'
|
|
39
|
-
|
|
37
|
+
let fullUrl = 'https://api.cloudns.net' +
|
|
38
|
+
relativeUrl +
|
|
39
|
+
'?' +
|
|
40
|
+
querystring.stringify(Object.assign({
|
|
41
|
+
'sub-auth-user': cloudnsUsername,
|
|
42
|
+
'auth-password': cloudnsPassword,
|
|
43
|
+
}, queryOptions || {}));
|
|
40
44
|
// console.log('Note: Calling', fullUrl)
|
|
41
45
|
const response = await (0, node_fetch_1.default)(fullUrl, {
|
|
42
46
|
method: method,
|
|
43
47
|
headers: {
|
|
44
48
|
'Content-Type': 'application/json',
|
|
45
|
-
|
|
46
|
-
}
|
|
49
|
+
Accept: 'application/json',
|
|
50
|
+
},
|
|
47
51
|
});
|
|
48
52
|
if (!response.ok) {
|
|
49
53
|
const errorText = await response.text();
|
|
@@ -61,18 +65,20 @@ async function autoDetectCloudnsHostAndZone(cloudnsUsername, cloudnsPassword, na
|
|
|
61
65
|
const hostName2 = nameParts.slice(0, nameParts.length - 3).join('.');
|
|
62
66
|
const zoneName2 = nameParts.slice(nameParts.length - 3).join('.');
|
|
63
67
|
// Check which zone exists
|
|
64
|
-
const zoneResponse1 = zoneCache[zoneName1] ||
|
|
65
|
-
'
|
|
66
|
-
|
|
68
|
+
const zoneResponse1 = zoneCache[zoneName1] ||
|
|
69
|
+
(await cloudnsRestCall(cloudnsUsername, cloudnsPassword, 'GET', '/dns/get-zone-info.json', {
|
|
70
|
+
'domain-name': zoneName1,
|
|
71
|
+
}));
|
|
67
72
|
zoneCache[zoneName1] = zoneResponse1;
|
|
68
|
-
const zoneResponse2 = zoneCache[zoneName2] ||
|
|
69
|
-
'
|
|
70
|
-
|
|
73
|
+
const zoneResponse2 = zoneCache[zoneName2] ||
|
|
74
|
+
(await cloudnsRestCall(cloudnsUsername, cloudnsPassword, 'GET', '/dns/get-zone-info.json', {
|
|
75
|
+
'domain-name': zoneName2,
|
|
76
|
+
}));
|
|
71
77
|
zoneCache[zoneName2] = zoneResponse2;
|
|
72
78
|
// console.log('Note: Response for host', hostName1, 'in zone', zoneName1, ':', zoneResponse1)
|
|
73
79
|
// console.log('Note: Response for host', hostName2, 'in zone', zoneName2, ':', zoneResponse2)
|
|
74
|
-
const zoneName =
|
|
75
|
-
const hostName =
|
|
80
|
+
const zoneName = zoneResponse1.status === '1' ? zoneName1 : zoneResponse2.status === '1' ? zoneName2 : '';
|
|
81
|
+
const hostName = zoneResponse1.status === '1' ? hostName1 : zoneResponse2.status === '1' ? hostName2 : '';
|
|
76
82
|
if (!zoneName) {
|
|
77
83
|
// Neither zone exists
|
|
78
84
|
throw new Error('Zone Not Found: ' + name);
|
|
@@ -87,8 +93,8 @@ async function createOrUpdateCloudnsResource(cloudnsUsername, cloudnsPassword, n
|
|
|
87
93
|
// Does the record exist?
|
|
88
94
|
const recordsResponse = await cloudnsRestCall(cloudnsUsername, cloudnsPassword, 'GET', '/dns/records.json', {
|
|
89
95
|
'domain-name': zoneName,
|
|
90
|
-
|
|
91
|
-
|
|
96
|
+
host: hostName,
|
|
97
|
+
type: type,
|
|
92
98
|
});
|
|
93
99
|
const existingRecord = Object.values(recordsResponse)[0];
|
|
94
100
|
if ((existingRecord === null || existingRecord === void 0 ? void 0 : existingRecord.host) === hostName && (existingRecord === null || existingRecord === void 0 ? void 0 : existingRecord.type) === type && (existingRecord === null || existingRecord === void 0 ? void 0 : existingRecord.ttl) === ttlValue && (existingRecord === null || existingRecord === void 0 ? void 0 : existingRecord.record) === value) {
|
|
@@ -101,10 +107,10 @@ async function createOrUpdateCloudnsResource(cloudnsUsername, cloudnsPassword, n
|
|
|
101
107
|
const result = await cloudnsRestCall(cloudnsUsername, cloudnsPassword, 'POST', '/dns/mod-record.json', {
|
|
102
108
|
'domain-name': zoneName,
|
|
103
109
|
'record-id': existingRecord === null || existingRecord === void 0 ? void 0 : existingRecord.id,
|
|
104
|
-
|
|
110
|
+
host: hostName,
|
|
105
111
|
'record-type': type,
|
|
106
|
-
|
|
107
|
-
|
|
112
|
+
record: value,
|
|
113
|
+
ttl: ttlValue,
|
|
108
114
|
});
|
|
109
115
|
if (result.status === 'Failed') {
|
|
110
116
|
throw new Error('Modify record failed: ' + (result.statusMessage || result.statusDescription));
|
|
@@ -115,10 +121,10 @@ async function createOrUpdateCloudnsResource(cloudnsUsername, cloudnsPassword, n
|
|
|
115
121
|
console.log('CREATE', name, type, ttlValue, value, 'ZONE', zoneName, 'HOST', hostName);
|
|
116
122
|
const result = await cloudnsRestCall(cloudnsUsername, cloudnsPassword, 'POST', '/dns/add-record.json', {
|
|
117
123
|
'domain-name': zoneName,
|
|
118
|
-
|
|
124
|
+
host: hostName,
|
|
119
125
|
'record-type': type,
|
|
120
|
-
|
|
121
|
-
|
|
126
|
+
record: value,
|
|
127
|
+
ttl: ttlValue,
|
|
122
128
|
});
|
|
123
129
|
if (result.status === 'Failed') {
|
|
124
130
|
throw new Error('Add record failed: ' + (result.statusMessage || result.statusDescription));
|
|
@@ -126,17 +132,18 @@ async function createOrUpdateCloudnsResource(cloudnsUsername, cloudnsPassword, n
|
|
|
126
132
|
}
|
|
127
133
|
}
|
|
128
134
|
async function main() {
|
|
129
|
-
var _a, _b;
|
|
130
|
-
console.log('ClouDNS CloudFormation Sync by Kenneth Falck <kennu@clouden.net> (C) Clouden Oy
|
|
135
|
+
var _a, _b, _c;
|
|
136
|
+
console.log('ClouDNS CloudFormation Sync by Kenneth Falck <kennu@clouden.net> (C) Clouden Oy 2020-2023');
|
|
131
137
|
const cloudnsUsername = process.argv[2];
|
|
132
138
|
const cloudnsPasswordParameter = process.argv[3];
|
|
133
139
|
const ttlValue = process.argv[4] || '300';
|
|
140
|
+
const stackName = process.argv[5] || '';
|
|
134
141
|
if (!cloudnsUsername) {
|
|
135
|
-
console.error('Usage: cloudns-cloudformation-sync <cloudns-username> <cloudns-password-parameter-name> [ttl]');
|
|
142
|
+
console.error('Usage: cloudns-cloudformation-sync <cloudns-username> <cloudns-password-parameter-name> [ttl [stackName]]');
|
|
136
143
|
process.exit(1);
|
|
137
144
|
}
|
|
138
145
|
if (!cloudnsPasswordParameter) {
|
|
139
|
-
console.error('Usage: cloudns-cloudformation-sync <cloudns-username> <cloudns-password-parameter-name> [ttl]');
|
|
146
|
+
console.error('Usage: cloudns-cloudformation-sync <cloudns-username> <cloudns-password-parameter-name> [ttl [stackName]]');
|
|
140
147
|
process.exit(1);
|
|
141
148
|
}
|
|
142
149
|
const ssm = new client_ssm_1.SSMClient({});
|
|
@@ -153,7 +160,15 @@ async function main() {
|
|
|
153
160
|
NextToken: nextToken,
|
|
154
161
|
}));
|
|
155
162
|
for (const exportObj of response.Exports || []) {
|
|
156
|
-
if (
|
|
163
|
+
if (stackName && exportObj.ExportingStackId !== stackName) {
|
|
164
|
+
// Check if the name part of the ID matches arn:aws:cloudformation:eu-west-1:<xxx>:stack/<name>/<xxx>
|
|
165
|
+
const m = (_b = exportObj.ExportingStackId) === null || _b === void 0 ? void 0 : _b.match(/^arn:[^:]+:cloudformation:[^:]+:[^:]+:stack\/([^\/]+)\//);
|
|
166
|
+
if (!m || m[1] !== stackName) {
|
|
167
|
+
// Stack ID name part didn't match given stackName, so skip it
|
|
168
|
+
continue;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
if ((_c = exportObj.Name) === null || _c === void 0 ? void 0 : _c.match(/^ClouDNS:/)) {
|
|
157
172
|
const nameParts = exportObj.Name.split(':');
|
|
158
173
|
const resourceType = nameParts[1];
|
|
159
174
|
const resourceName = nameParts.slice(2).join('.');
|
|
@@ -165,4 +180,4 @@ async function main() {
|
|
|
165
180
|
} while (nextToken);
|
|
166
181
|
}
|
|
167
182
|
exports.main = main;
|
|
168
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xvdWRucy1jbG91ZGZvcm1hdGlvbi1zeW5jLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vc3JjL2Nsb3VkbnMtY2xvdWRmb3JtYXRpb24tc3luYy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFBQTs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0dBd0JHO0FBQ0gsb0RBQW9FO0FBQ3BFLDBFQUE0RztBQUM1RywyQ0FBOEI7QUFDOUIsMkNBQTBDO0FBRTFDLHFCQUFxQjtBQUNyQixPQUFPLENBQUMsR0FBRyxDQUFDLG1CQUFtQixHQUFHLEdBQUcsQ0FBQTtBQUVyQyxLQUFLLFVBQVUsZUFBZSxDQUFDLGVBQXVCLEVBQUUsZUFBdUIsRUFBRSxNQUFjLEVBQUUsV0FBbUIsRUFBRSxZQUFpQjtJQUNySSxJQUFJLE9BQU8sR0FBRyx5QkFBeUIsR0FBRyxXQUFXLEdBQUcsR0FBRyxHQUFHLFdBQVcsQ0FBQyxTQUFTLENBQUMsTUFBTSxDQUFDLE1BQU0sQ0FBQztRQUNoRyxlQUFlLEVBQUUsZUFBZTtRQUNoQyxlQUFlLEVBQUUsZUFBZTtLQUNqQyxFQUFFLFlBQVksSUFBSSxFQUFFLENBQUMsQ0FBQyxDQUFBO0lBRXZCLHdDQUF3QztJQUV4QyxNQUFNLFFBQVEsR0FBRyxNQUFNLElBQUEsb0JBQUssRUFBQyxPQUFPLEVBQUU7UUFDcEMsTUFBTSxFQUFFLE1BQU07UUFDZCxPQUFPLEVBQUU7WUFDUCxjQUFjLEVBQUUsa0JBQWtCO1lBQ2xDLFFBQVEsRUFBRSxrQkFBa0I7U0FDN0I7S0FDRixDQUFDLENBQUE7SUFDRixJQUFJLENBQUMsUUFBUSxDQUFDLEVBQUUsRUFBRTtRQUNoQixNQUFNLFNBQVMsR0FBRyxNQUFNLFFBQVEsQ0FBQyxJQUFJLEVBQUUsQ0FBQTtRQUN2QyxPQUFPLENBQUMsS0FBSyxDQUFDLFlBQVksRUFBRSxRQUFRLENBQUMsTUFBTSxFQUFFLFFBQVEsQ0FBQyxVQUFVLEVBQUUsU0FBUyxDQUFDLENBQUE7UUFDNUUsTUFBTSxJQUFJLEtBQUssQ0FBQyxTQUFTLENBQUMsQ0FBQTtLQUMzQjtJQUNELE9BQU8sUUFBUSxDQUFDLElBQUksRUFBRSxDQUFBO0FBQ3hCLENBQUM7QUFFRCxLQUFLLFVBQVUsNEJBQTRCLENBQUMsZUFBdUIsRUFBRSxlQUF1QixFQUFFLElBQVksRUFBRSxTQUFjO0lBQ3hILE1BQU0sU0FBUyxHQUFHLElBQUksQ0FBQyxLQUFLLENBQUMsR0FBRyxDQUFDLENBQUE7SUFFakMsaUNBQWlDO0lBQ2pDLE1BQU0sU0FBUyxHQUFHLFNBQVMsQ0FBQyxLQUFLLENBQUMsQ0FBQyxFQUFFLFNBQVMsQ0FBQyxNQUFNLEdBQUMsQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxDQUFBO0lBQ2xFLE1BQU0sU0FBUyxHQUFHLFNBQVMsQ0FBQyxLQUFLLENBQUMsU0FBUyxDQUFDLE1BQU0sR0FBQyxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLENBQUE7SUFFL0Qsd0NBQXdDO0lBQ3hDLE1BQU0sU0FBUyxHQUFHLFNBQVMsQ0FBQyxLQUFLLENBQUMsQ0FBQyxFQUFFLFNBQVMsQ0FBQyxNQUFNLEdBQUMsQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxDQUFBO0lBQ2xFLE1BQU0sU0FBUyxHQUFHLFNBQVMsQ0FBQyxLQUFLLENBQUMsU0FBUyxDQUFDLE1BQU0sR0FBQyxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLENBQUE7SUFFL0QsMEJBQTBCO0lBQzFCLE1BQU0sYUFBYSxHQUFHLFNBQVMsQ0FBQyxTQUFTLENBQUMsSUFBSSxNQUFNLGVBQWUsQ0FBQyxlQUFlLEVBQUUsZUFBZSxFQUFFLEtBQUssRUFBRSx5QkFBeUIsRUFBRTtRQUN0SSxhQUFhLEVBQUUsU0FBUztLQUN6QixDQUFDLENBQUE7SUFDRixTQUFTLENBQUMsU0FBUyxDQUFDLEdBQUcsYUFBYSxDQUFBO0lBQ3BDLE1BQU0sYUFBYSxHQUFHLFNBQVMsQ0FBQyxTQUFTLENBQUMsSUFBSSxNQUFNLGVBQWUsQ0FBQyxlQUFlLEVBQUUsZUFBZSxFQUFFLEtBQUssRUFBRSx5QkFBeUIsRUFBRTtRQUN0SSxhQUFhLEVBQUUsU0FBUztLQUN6QixDQUFDLENBQUE7SUFDRixTQUFTLENBQUMsU0FBUyxDQUFDLEdBQUcsYUFBYSxDQUFBO0lBRXBDLDhGQUE4RjtJQUM5Riw4RkFBOEY7SUFFOUYsTUFBTSxRQUFRLEdBQUcsQ0FBQyxhQUFhLENBQUMsTUFBTSxLQUFLLEdBQUcsQ0FBQyxDQUFDLENBQUMsU0FBUyxDQUFDLENBQUMsQ0FBQyxhQUFhLENBQUMsTUFBTSxLQUFLLEdBQUcsQ0FBQyxDQUFDLENBQUMsU0FBUyxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQTtJQUMzRyxNQUFNLFFBQVEsR0FBRyxDQUFDLGFBQWEsQ0FBQyxNQUFNLEtBQUssR0FBRyxDQUFDLENBQUMsQ0FBQyxTQUFTLENBQUMsQ0FBQyxDQUFDLGFBQWEsQ0FBQyxNQUFNLEtBQUssR0FBRyxDQUFDLENBQUMsQ0FBQyxTQUFTLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFBO0lBQzNHLElBQUksQ0FBQyxRQUFRLEVBQUU7UUFDYixzQkFBc0I7UUFDdEIsTUFBTSxJQUFJLEtBQUssQ0FBQyxrQkFBa0IsR0FBRyxJQUFJLENBQUMsQ0FBQTtLQUMzQztJQUNELE9BQU87UUFDTCxRQUFRLEVBQUUsUUFBUTtRQUNsQixRQUFRLEVBQUUsUUFBUTtLQUNuQixDQUFBO0FBQ0gsQ0FBQztBQUVELEtBQUssVUFBVSw2QkFBNkIsQ0FBQyxlQUF1QixFQUFFLGVBQXVCLEVBQUUsSUFBWSxFQUFFLElBQVksRUFBRSxLQUFhLEVBQUUsUUFBZ0IsRUFBRSxTQUFjO0lBQ3hLLE1BQU0sRUFBRSxRQUFRLEVBQUUsUUFBUSxFQUFFLEdBQUcsTUFBTSw0QkFBNEIsQ0FBQyxlQUFlLEVBQUUsZUFBZSxFQUFFLElBQUksRUFBRSxTQUFTLENBQUMsQ0FBQTtJQUNwSCx5QkFBeUI7SUFDekIsTUFBTSxlQUFlLEdBQUcsTUFBTSxlQUFlLENBQUMsZUFBZSxFQUFFLGVBQWUsRUFBRSxLQUFLLEVBQUUsbUJBQW1CLEVBQUU7UUFDMUcsYUFBYSxFQUFFLFFBQVE7UUFDdkIsTUFBTSxFQUFFLFFBQVE7UUFDaEIsTUFBTSxFQUFFLElBQUk7S0FDYixDQUFDLENBQUE7SUFDRixNQUFNLGNBQWMsR0FBUSxNQUFNLENBQUMsTUFBTSxDQUFDLGVBQWUsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFBO0lBQzdELElBQUksQ0FBQSxjQUFjLGFBQWQsY0FBYyx1QkFBZCxjQUFjLENBQUUsSUFBSSxNQUFLLFFBQVEsSUFBSSxDQUFBLGNBQWMsYUFBZCxjQUFjLHVCQUFkLGNBQWMsQ0FBRSxJQUFJLE1BQUssSUFBSSxJQUFJLENBQUEsY0FBYyxhQUFkLGNBQWMsdUJBQWQsY0FBYyxDQUFFLEdBQUcsTUFBSyxRQUFRLElBQUksQ0FBQSxjQUFjLGFBQWQsY0FBYyx1QkFBZCxjQUFjLENBQUUsTUFBTSxNQUFLLEtBQUssRUFBRTtRQUM5SSxvQ0FBb0M7UUFDcEMsT0FBTyxDQUFDLEdBQUcsQ0FBQyxJQUFJLEVBQUUsSUFBSSxFQUFFLElBQUksRUFBRSxRQUFRLEVBQUUsS0FBSyxFQUFFLE1BQU0sRUFBRSxRQUFRLEVBQUUsTUFBTSxFQUFFLFFBQVEsQ0FBQyxDQUFBO0tBQ25GO1NBQU0sSUFBSSxjQUFjLGFBQWQsY0FBYyx1QkFBZCxjQUFjLENBQUUsRUFBRSxFQUFFO1FBQzdCLGdCQUFnQjtRQUNoQixPQUFPLENBQUMsR0FBRyxDQUFDLFFBQVEsRUFBRSxJQUFJLEVBQUUsSUFBSSxFQUFFLFFBQVEsRUFBRSxLQUFLLEVBQUUsTUFBTSxFQUFFLFFBQVEsRUFBRSxNQUFNLEVBQUUsUUFBUSxDQUFDLENBQUE7UUFDdEYsTUFBTSxNQUFNLEdBQUcsTUFBTSxlQUFlLENBQUMsZUFBZSxFQUFFLGVBQWUsRUFBRSxNQUFNLEVBQUUsc0JBQXNCLEVBQUU7WUFDckcsYUFBYSxFQUFFLFFBQVE7WUFDdkIsV0FBVyxFQUFFLGNBQWMsYUFBZCxjQUFjLHVCQUFkLGNBQWMsQ0FBRSxFQUFFO1lBQy9CLE1BQU0sRUFBRSxRQUFRO1lBQ2hCLGFBQWEsRUFBRSxJQUFJO1lBQ25CLFFBQVEsRUFBRSxLQUFLO1lBQ2YsS0FBSyxFQUFFLFFBQVE7U0FDaEIsQ0FBQyxDQUFBO1FBQ0YsSUFBSSxNQUFNLENBQUMsTUFBTSxLQUFLLFFBQVEsRUFBRTtZQUM5QixNQUFNLElBQUksS0FBSyxDQUFFLHdCQUF3QixHQUFHLENBQUMsTUFBTSxDQUFDLGFBQWEsSUFBSSxNQUFNLENBQUMsaUJBQWlCLENBQUMsQ0FBQyxDQUFBO1NBQ2hHO0tBQ0Y7U0FBTTtRQUNMLGdCQUFnQjtRQUNoQixPQUFPLENBQUMsR0FBRyxDQUFDLFFBQVEsRUFBRSxJQUFJLEVBQUUsSUFBSSxFQUFFLFFBQVEsRUFBRSxLQUFLLEVBQUUsTUFBTSxFQUFFLFFBQVEsRUFBRSxNQUFNLEVBQUUsUUFBUSxDQUFDLENBQUE7UUFDdEYsTUFBTSxNQUFNLEdBQUcsTUFBTSxlQUFlLENBQUMsZUFBZSxFQUFFLGVBQWUsRUFBRSxNQUFNLEVBQUUsc0JBQXNCLEVBQUU7WUFDckcsYUFBYSxFQUFFLFFBQVE7WUFDdkIsTUFBTSxFQUFFLFFBQVE7WUFDaEIsYUFBYSxFQUFFLElBQUk7WUFDbkIsUUFBUSxFQUFFLEtBQUs7WUFDZixLQUFLLEVBQUUsUUFBUTtTQUNoQixDQUFDLENBQUE7UUFDRixJQUFJLE1BQU0sQ0FBQyxNQUFNLEtBQUssUUFBUSxFQUFFO1lBQzlCLE1BQU0sSUFBSSxLQUFLLENBQUUscUJBQXFCLEdBQUcsQ0FBQyxNQUFNLENBQUMsYUFBYSxJQUFJLE1BQU0sQ0FBQyxpQkFBaUIsQ0FBQyxDQUFDLENBQUE7U0FDN0Y7S0FDRjtBQUNILENBQUM7QUFFTSxLQUFLLFVBQVUsSUFBSTs7SUFDeEIsT0FBTyxDQUFDLEdBQUcsQ0FBQyxzRkFBc0YsQ0FBQyxDQUFBO0lBQ25HLE1BQU0sZUFBZSxHQUFHLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLENBQUE7SUFDdkMsTUFBTSx3QkFBd0IsR0FBRyxPQUFPLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxDQUFBO0lBQ2hELE1BQU0sUUFBUSxHQUFHLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLElBQUksS0FBSyxDQUFBO0lBQ3pDLElBQUksQ0FBQyxlQUFlLEVBQUU7UUFDcEIsT0FBTyxDQUFDLEtBQUssQ0FBQywrRkFBK0YsQ0FBQyxDQUFBO1FBQzlHLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLENBQUE7S0FDaEI7SUFDRCxJQUFJLENBQUMsd0JBQXdCLEVBQUU7UUFDN0IsT0FBTyxDQUFDLEtBQUssQ0FBQywrRkFBK0YsQ0FBQyxDQUFBO1FBQzlHLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLENBQUE7S0FDaEI7SUFFRCxNQUFNLEdBQUcsR0FBRyxJQUFJLHNCQUFTLENBQUMsRUFBRSxDQUFDLENBQUE7SUFDN0IsTUFBTSxTQUFTLEdBQUcsRUFBRSxDQUFBO0lBRXBCLE1BQU0sUUFBUSxHQUFHLE1BQU0sR0FBRyxDQUFDLElBQUksQ0FBQyxJQUFJLGdDQUFtQixDQUFDO1FBQ3RELElBQUksRUFBRSx3QkFBd0I7UUFDOUIsY0FBYyxFQUFFLElBQUk7S0FDckIsQ0FBQyxDQUFDLENBQUE7SUFDSCxNQUFNLGVBQWUsR0FBRyxDQUFBLE1BQUEsUUFBUSxDQUFDLFNBQVMsMENBQUUsS0FBSyxLQUFJLEVBQUUsQ0FBQTtJQUV2RCxNQUFNLGNBQWMsR0FBRyxJQUFJLDRDQUFvQixDQUFDLEVBQUUsQ0FBQyxDQUFBO0lBQ25ELElBQUksU0FBUyxDQUFBO0lBQ2IsR0FBRztRQUNELE1BQU0sUUFBUSxHQUFzQixNQUFNLGNBQWMsQ0FBQyxJQUFJLENBQUMsSUFBSSwwQ0FBa0IsQ0FBQztZQUNuRixTQUFTLEVBQUUsU0FBUztTQUNyQixDQUFDLENBQUMsQ0FBQTtRQUNILEtBQUssTUFBTSxTQUFTLElBQUksUUFBUSxDQUFDLE9BQU8sSUFBSSxFQUFFLEVBQUU7WUFDOUMsSUFBSSxNQUFBLFNBQVMsQ0FBQyxJQUFJLDBDQUFFLEtBQUssQ0FBQyxXQUFXLENBQUMsRUFBRTtnQkFDdEMsTUFBTSxTQUFTLEdBQUcsU0FBUyxDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsR0FBRyxDQUFDLENBQUE7Z0JBQzNDLE1BQU0sWUFBWSxHQUFHLFNBQVMsQ0FBQyxDQUFDLENBQUMsQ0FBQTtnQkFDakMsTUFBTSxZQUFZLEdBQUcsU0FBUyxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLENBQUE7Z0JBQ2pELE1BQU0sYUFBYSxHQUFHLFNBQVMsQ0FBQyxLQUFNLENBQUE7Z0JBQ3RDLE1BQU0sNkJBQTZCLENBQUMsZUFBZSxFQUFFLGVBQWUsRUFBRSxZQUFZLEVBQUUsWUFBWSxFQUFFLGFBQWEsRUFBRSxRQUFRLEVBQUUsU0FBUyxDQUFDLENBQUE7YUFDdEk7U0FDRjtRQUNELFNBQVMsR0FBRyxRQUFRLENBQUMsU0FBUyxDQUFBO0tBQy9CLFFBQVEsU0FBUyxFQUFDO0FBQ3JCLENBQUM7QUF4Q0Qsb0JBd0NDIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBSZWFkIEFXUyBDbG91ZEZvcm1hdGlvbiBFeHBvcnRzIGFuZCBhdXRvZ2VuZXJhdGUgQ2xvdUROUyByZWNvcmRzIGJhc2VkIG9uIHRoZWlyIG5hbWVzIGFuZCB2YWx1ZXMuXG4gKiBLZW5uZXRoIEZhbGNrIDxrZW5udUBjbG91ZGVuLm5ldD4gKEMpIENsb3VkZW4gT3kgMjAyMFxuICpcbiAqIFRoaXMgdG9vbCBjYW4gYmUgdXNlZCB0byBhdXRvZ2VuZXJhdGUgQ2xvdUROUyByZWNvcmRzIGZvciBDbG91ZEZvcm1hdGlvbiByZXNvdXJjZXMgbGlrZVxuICogQ2xvdWRGcm9udCBkaXN0cmlidXRpb25zIGFuZCBBUEkgR2F0ZXdheSBkb21haW5zLlxuICpcbiAqIENsb3VkRm9ybWF0aW9uIGV4cG9ydCBuYW1lIG11c3Qgc3BlY2lmeSB0aGUgcmVzb3VyY2UgdHlwZSBhbmQgcmVjb3JkIGhvc3RuYW1lIGFzIGZvbGxvd3M6XG4gKiBDbG91RE5TOkNOQU1FOm15aG9zdDpleGFtcGxlOm9yZ1xuICpcbiAqIENsb3VkRm9ybWF0aW9uIGV4cG9ydCB2YWx1ZSBtdXN0IHNwZWNpZnkgdGhlIHJlY29yZCB2YWx1ZSBhcy1pcyAoZm9yIGluc3RhbmNlLCBhIGRpc3RyaWJ1dGlvbiBkb21haW4gbmFtZSk6XG4gKiB4eHh4eHh4eHh4eHh4eC5jbG91ZGZyb250Lm5ldFxuICpcbiAqIFRoZSBhYm92ZSBleGFtcGxlIHdpbGwgZ2VuZXJhdGUgdGhlIGZvbGxvd2luZyByZWNvcmQgaW4gdGhlIENsb3VETlMgem9uZSBleGFtcGxlLm9yZzpcbiAqIG15aG9zdC5leGFtcGxlLm9yZyBDTkFNRSB4eHh4eHh4eHh4eHh4eC5jbG91ZGZyb250Lm5ldFxuICpcbiAqIE90aGVyIHJlc291cmNlIHR5cGVzIGFyZSBhbHNvIGFsbG93ZWQgKEEsIEFBQUEsIEFMSUFTLCBldGMpLlxuICpcbiAqIENvbW1hbmQgbGluZSB1c2FnZTogQVdTX1BST0ZJTEU9eHh4IHRzLW5vZGUgY2xvdWRucy1jbG91ZGZvcm1hdGlvbi1zeW5jLnRzIDxjbG91ZG5zLXVzZXJuYW1lPiA8Y2xvdWRucy1wYXNzd29yZC1wYXJhbWV0ZXItbmFtZT4gW3R0bF1cbiAqXG4gKiBBV1NfUFJPRklMRT14eHggLSBTcGVjaWZ5IHlvdXIgQVdTIHByb2ZpbGUgaW4gfi8uYXdzL2NyZWRlbnRpYWxzIGFzIGFuIGVudmlyb25tZW50IHZhcmlhYmxlXG4gKiA8Y2xvdWRucy11c2VybmFtZT4gLSBDbG91RE5TIEFQSSBzdWItYXV0aC11c2VyXG4gKiA8Y2xvdWRucy1wYXNzd29yZC1wYXJhbWV0ZXItbmFtZT4gLSBTU00gUGFyYW1ldGVyIHdpdGggdGhlIGVuY3J5cHRlZCBDbG91RE5TIEFQSSBwYXNzd29yZFxuICogW3R0bF0gLSBPcHRpb25hbCBUVEwgZm9yIGdlbmVyYXRlZCByZWNvcmRzIChkZWZhdWx0cyB0byAzMDApXG4gKi9cbmltcG9ydCB7IFNTTUNsaWVudCwgR2V0UGFyYW1ldGVyQ29tbWFuZCB9IGZyb20gJ0Bhd3Mtc2RrL2NsaWVudC1zc20nXG5pbXBvcnQgeyBDbG91ZEZvcm1hdGlvbkNsaWVudCwgTGlzdEV4cG9ydHNDb21tYW5kLCBMaXN0RXhwb3J0c091dHB1dCB9IGZyb20gJ0Bhd3Mtc2RrL2NsaWVudC1jbG91ZGZvcm1hdGlvbidcbmltcG9ydCBmZXRjaCBmcm9tICdub2RlLWZldGNoJ1xuaW1wb3J0ICogYXMgcXVlcnlzdHJpbmcgZnJvbSAncXVlcnlzdHJpbmcnXG5cbi8vIExvYWQgfi8uYXdzL2NvbmZpZ1xucHJvY2Vzcy5lbnYuQVdTX1NES19MT0FEX0NPTkZJRyA9ICcxJ1xuXG5hc3luYyBmdW5jdGlvbiBjbG91ZG5zUmVzdENhbGwoY2xvdWRuc1VzZXJuYW1lOiBzdHJpbmcsIGNsb3VkbnNQYXNzd29yZDogc3RyaW5nLCBtZXRob2Q6IHN0cmluZywgcmVsYXRpdmVVcmw6IHN0cmluZywgcXVlcnlPcHRpb25zOiBhbnkpIHtcbiAgbGV0IGZ1bGxVcmwgPSAnaHR0cHM6Ly9hcGkuY2xvdWRucy5uZXQnICsgcmVsYXRpdmVVcmwgKyAnPycgKyBxdWVyeXN0cmluZy5zdHJpbmdpZnkoT2JqZWN0LmFzc2lnbih7XG4gICAgJ3N1Yi1hdXRoLXVzZXInOiBjbG91ZG5zVXNlcm5hbWUsXG4gICAgJ2F1dGgtcGFzc3dvcmQnOiBjbG91ZG5zUGFzc3dvcmQsXG4gIH0sIHF1ZXJ5T3B0aW9ucyB8fCB7fSkpXG5cbiAgLy8gY29uc29sZS5sb2coJ05vdGU6IENhbGxpbmcnLCBmdWxsVXJsKVxuXG4gIGNvbnN0IHJlc3BvbnNlID0gYXdhaXQgZmV0Y2goZnVsbFVybCwge1xuICAgIG1ldGhvZDogbWV0aG9kLFxuICAgIGhlYWRlcnM6IHtcbiAgICAgICdDb250ZW50LVR5cGUnOiAnYXBwbGljYXRpb24vanNvbicsXG4gICAgICAnQWNjZXB0JzogJ2FwcGxpY2F0aW9uL2pzb24nLFxuICAgIH1cbiAgfSlcbiAgaWYgKCFyZXNwb25zZS5vaykge1xuICAgIGNvbnN0IGVycm9yVGV4dCA9IGF3YWl0IHJlc3BvbnNlLnRleHQoKVxuICAgIGNvbnNvbGUuZXJyb3IoJ0hUVFAgRXJyb3InLCByZXNwb25zZS5zdGF0dXMsIHJlc3BvbnNlLnN0YXR1c1RleHQsIGVycm9yVGV4dClcbiAgICB0aHJvdyBuZXcgRXJyb3IoZXJyb3JUZXh0KVxuICB9XG4gIHJldHVybiByZXNwb25zZS5qc29uKClcbn1cblxuYXN5bmMgZnVuY3Rpb24gYXV0b0RldGVjdENsb3VkbnNIb3N0QW5kWm9uZShjbG91ZG5zVXNlcm5hbWU6IHN0cmluZywgY2xvdWRuc1Bhc3N3b3JkOiBzdHJpbmcsIG5hbWU6IHN0cmluZywgem9uZUNhY2hlOiBhbnkpIHtcbiAgY29uc3QgbmFtZVBhcnRzID0gbmFtZS5zcGxpdCgnLicpXG5cbiAgLy8gWm9uZSBhbmQgaG9zdCBuYW1lIGZvciB4eHgudGxkXG4gIGNvbnN0IGhvc3ROYW1lMSA9IG5hbWVQYXJ0cy5zbGljZSgwLCBuYW1lUGFydHMubGVuZ3RoLTIpLmpvaW4oJy4nKVxuICBjb25zdCB6b25lTmFtZTEgPSBuYW1lUGFydHMuc2xpY2UobmFtZVBhcnRzLmxlbmd0aC0yKS5qb2luKCcuJylcblxuICAvLyBab25lIGFuZCBob3N0IG5hbWUgZm9yIHh4eC5zdWJ0bGQudGxkXG4gIGNvbnN0IGhvc3ROYW1lMiA9IG5hbWVQYXJ0cy5zbGljZSgwLCBuYW1lUGFydHMubGVuZ3RoLTMpLmpvaW4oJy4nKVxuICBjb25zdCB6b25lTmFtZTIgPSBuYW1lUGFydHMuc2xpY2UobmFtZVBhcnRzLmxlbmd0aC0zKS5qb2luKCcuJylcblxuICAvLyBDaGVjayB3aGljaCB6b25lIGV4aXN0c1xuICBjb25zdCB6b25lUmVzcG9uc2UxID0gem9uZUNhY2hlW3pvbmVOYW1lMV0gfHwgYXdhaXQgY2xvdWRuc1Jlc3RDYWxsKGNsb3VkbnNVc2VybmFtZSwgY2xvdWRuc1Bhc3N3b3JkLCAnR0VUJywgJy9kbnMvZ2V0LXpvbmUtaW5mby5qc29uJywge1xuICAgICdkb21haW4tbmFtZSc6IHpvbmVOYW1lMSxcbiAgfSlcbiAgem9uZUNhY2hlW3pvbmVOYW1lMV0gPSB6b25lUmVzcG9uc2UxXG4gIGNvbnN0IHpvbmVSZXNwb25zZTIgPSB6b25lQ2FjaGVbem9uZU5hbWUyXSB8fCBhd2FpdCBjbG91ZG5zUmVzdENhbGwoY2xvdWRuc1VzZXJuYW1lLCBjbG91ZG5zUGFzc3dvcmQsICdHRVQnLCAnL2Rucy9nZXQtem9uZS1pbmZvLmpzb24nLCB7XG4gICAgJ2RvbWFpbi1uYW1lJzogem9uZU5hbWUyLFxuICB9KVxuICB6b25lQ2FjaGVbem9uZU5hbWUyXSA9IHpvbmVSZXNwb25zZTJcblxuICAvLyBjb25zb2xlLmxvZygnTm90ZTogUmVzcG9uc2UgZm9yIGhvc3QnLCBob3N0TmFtZTEsICdpbiB6b25lJywgem9uZU5hbWUxLCAnOicsIHpvbmVSZXNwb25zZTEpXG4gIC8vIGNvbnNvbGUubG9nKCdOb3RlOiBSZXNwb25zZSBmb3IgaG9zdCcsIGhvc3ROYW1lMiwgJ2luIHpvbmUnLCB6b25lTmFtZTIsICc6Jywgem9uZVJlc3BvbnNlMilcblxuICBjb25zdCB6b25lTmFtZSA9ICh6b25lUmVzcG9uc2UxLnN0YXR1cyA9PT0gJzEnID8gem9uZU5hbWUxIDogem9uZVJlc3BvbnNlMi5zdGF0dXMgPT09ICcxJyA/IHpvbmVOYW1lMiA6ICcnKVxuICBjb25zdCBob3N0TmFtZSA9ICh6b25lUmVzcG9uc2UxLnN0YXR1cyA9PT0gJzEnID8gaG9zdE5hbWUxIDogem9uZVJlc3BvbnNlMi5zdGF0dXMgPT09ICcxJyA/IGhvc3ROYW1lMiA6ICcnKVxuICBpZiAoIXpvbmVOYW1lKSB7XG4gICAgLy8gTmVpdGhlciB6b25lIGV4aXN0c1xuICAgIHRocm93IG5ldyBFcnJvcignWm9uZSBOb3QgRm91bmQ6ICcgKyBuYW1lKVxuICB9XG4gIHJldHVybiB7XG4gICAgaG9zdE5hbWU6IGhvc3ROYW1lLFxuICAgIHpvbmVOYW1lOiB6b25lTmFtZSxcbiAgfVxufVxuXG5hc3luYyBmdW5jdGlvbiBjcmVhdGVPclVwZGF0ZUNsb3VkbnNSZXNvdXJjZShjbG91ZG5zVXNlcm5hbWU6IHN0cmluZywgY2xvdWRuc1Bhc3N3b3JkOiBzdHJpbmcsIG5hbWU6IHN0cmluZywgdHlwZTogc3RyaW5nLCB2YWx1ZTogc3RyaW5nLCB0dGxWYWx1ZTogc3RyaW5nLCB6b25lQ2FjaGU6IGFueSkge1xuICBjb25zdCB7IHpvbmVOYW1lLCBob3N0TmFtZSB9ID0gYXdhaXQgYXV0b0RldGVjdENsb3VkbnNIb3N0QW5kWm9uZShjbG91ZG5zVXNlcm5hbWUsIGNsb3VkbnNQYXNzd29yZCwgbmFtZSwgem9uZUNhY2hlKVxuICAvLyBEb2VzIHRoZSByZWNvcmQgZXhpc3Q/XG4gIGNvbnN0IHJlY29yZHNSZXNwb25zZSA9IGF3YWl0IGNsb3VkbnNSZXN0Q2FsbChjbG91ZG5zVXNlcm5hbWUsIGNsb3VkbnNQYXNzd29yZCwgJ0dFVCcsICcvZG5zL3JlY29yZHMuanNvbicsIHtcbiAgICAnZG9tYWluLW5hbWUnOiB6b25lTmFtZSxcbiAgICAnaG9zdCc6IGhvc3ROYW1lLFxuICAgICd0eXBlJzogdHlwZSxcbiAgfSlcbiAgY29uc3QgZXhpc3RpbmdSZWNvcmQ6IGFueSA9IE9iamVjdC52YWx1ZXMocmVjb3Jkc1Jlc3BvbnNlKVswXVxuICBpZiAoZXhpc3RpbmdSZWNvcmQ/Lmhvc3QgPT09IGhvc3ROYW1lICYmIGV4aXN0aW5nUmVjb3JkPy50eXBlID09PSB0eXBlICYmIGV4aXN0aW5nUmVjb3JkPy50dGwgPT09IHR0bFZhbHVlICYmIGV4aXN0aW5nUmVjb3JkPy5yZWNvcmQgPT09IHZhbHVlKSB7XG4gICAgLy8gUmVjb3JkIGV4aXN0cyBhbHJlYWR5IC0gbm8gY2hhbmdlXG4gICAgY29uc29sZS5sb2coJ09LJywgbmFtZSwgdHlwZSwgdHRsVmFsdWUsIHZhbHVlLCAnWk9ORScsIHpvbmVOYW1lLCAnSE9TVCcsIGhvc3ROYW1lKVxuICB9IGVsc2UgaWYgKGV4aXN0aW5nUmVjb3JkPy5pZCkge1xuICAgIC8vIFVwZGF0ZSByZWNvcmRcbiAgICBjb25zb2xlLmxvZygnVVBEQVRFJywgbmFtZSwgdHlwZSwgdHRsVmFsdWUsIHZhbHVlLCAnWk9ORScsIHpvbmVOYW1lLCAnSE9TVCcsIGhvc3ROYW1lKVxuICAgIGNvbnN0IHJlc3VsdCA9IGF3YWl0IGNsb3VkbnNSZXN0Q2FsbChjbG91ZG5zVXNlcm5hbWUsIGNsb3VkbnNQYXNzd29yZCwgJ1BPU1QnLCAnL2Rucy9tb2QtcmVjb3JkLmpzb24nLCB7XG4gICAgICAnZG9tYWluLW5hbWUnOiB6b25lTmFtZSxcbiAgICAgICdyZWNvcmQtaWQnOiBleGlzdGluZ1JlY29yZD8uaWQsXG4gICAgICAnaG9zdCc6IGhvc3ROYW1lLFxuICAgICAgJ3JlY29yZC10eXBlJzogdHlwZSxcbiAgICAgICdyZWNvcmQnOiB2YWx1ZSxcbiAgICAgICd0dGwnOiB0dGxWYWx1ZSxcbiAgICB9KVxuICAgIGlmIChyZXN1bHQuc3RhdHVzID09PSAnRmFpbGVkJykge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCAnTW9kaWZ5IHJlY29yZCBmYWlsZWQ6ICcgKyAocmVzdWx0LnN0YXR1c01lc3NhZ2UgfHwgcmVzdWx0LnN0YXR1c0Rlc2NyaXB0aW9uKSlcbiAgICB9XG4gIH0gZWxzZSB7XG4gICAgLy8gQ3JlYXRlIHJlY29yZFxuICAgIGNvbnNvbGUubG9nKCdDUkVBVEUnLCBuYW1lLCB0eXBlLCB0dGxWYWx1ZSwgdmFsdWUsICdaT05FJywgem9uZU5hbWUsICdIT1NUJywgaG9zdE5hbWUpXG4gICAgY29uc3QgcmVzdWx0ID0gYXdhaXQgY2xvdWRuc1Jlc3RDYWxsKGNsb3VkbnNVc2VybmFtZSwgY2xvdWRuc1Bhc3N3b3JkLCAnUE9TVCcsICcvZG5zL2FkZC1yZWNvcmQuanNvbicsIHtcbiAgICAgICdkb21haW4tbmFtZSc6IHpvbmVOYW1lLFxuICAgICAgJ2hvc3QnOiBob3N0TmFtZSxcbiAgICAgICdyZWNvcmQtdHlwZSc6IHR5cGUsXG4gICAgICAncmVjb3JkJzogdmFsdWUsXG4gICAgICAndHRsJzogdHRsVmFsdWUsXG4gICAgfSlcbiAgICBpZiAocmVzdWx0LnN0YXR1cyA9PT0gJ0ZhaWxlZCcpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvciggJ0FkZCByZWNvcmQgZmFpbGVkOiAnICsgKHJlc3VsdC5zdGF0dXNNZXNzYWdlIHx8IHJlc3VsdC5zdGF0dXNEZXNjcmlwdGlvbikpXG4gICAgfVxuICB9XG59XG5cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBtYWluKCkge1xuICBjb25zb2xlLmxvZygnQ2xvdUROUyBDbG91ZEZvcm1hdGlvbiBTeW5jIGJ5IEtlbm5ldGggRmFsY2sgPGtlbm51QGNsb3VkZW4ubmV0PiAoQykgQ2xvdWRlbiBPeSAyMDIxJylcbiAgY29uc3QgY2xvdWRuc1VzZXJuYW1lID0gcHJvY2Vzcy5hcmd2WzJdXG4gIGNvbnN0IGNsb3VkbnNQYXNzd29yZFBhcmFtZXRlciA9IHByb2Nlc3MuYXJndlszXVxuICBjb25zdCB0dGxWYWx1ZSA9IHByb2Nlc3MuYXJndls0XSB8fCAnMzAwJ1xuICBpZiAoIWNsb3VkbnNVc2VybmFtZSkge1xuICAgIGNvbnNvbGUuZXJyb3IoJ1VzYWdlOiBjbG91ZG5zLWNsb3VkZm9ybWF0aW9uLXN5bmMgPGNsb3VkbnMtdXNlcm5hbWU+IDxjbG91ZG5zLXBhc3N3b3JkLXBhcmFtZXRlci1uYW1lPiBbdHRsXScpXG4gICAgcHJvY2Vzcy5leGl0KDEpXG4gIH1cbiAgaWYgKCFjbG91ZG5zUGFzc3dvcmRQYXJhbWV0ZXIpIHtcbiAgICBjb25zb2xlLmVycm9yKCdVc2FnZTogY2xvdWRucy1jbG91ZGZvcm1hdGlvbi1zeW5jIDxjbG91ZG5zLXVzZXJuYW1lPiA8Y2xvdWRucy1wYXNzd29yZC1wYXJhbWV0ZXItbmFtZT4gW3R0bF0nKVxuICAgIHByb2Nlc3MuZXhpdCgxKVxuICB9XG5cbiAgY29uc3Qgc3NtID0gbmV3IFNTTUNsaWVudCh7fSlcbiAgY29uc3Qgem9uZUNhY2hlID0ge31cblxuICBjb25zdCByZXNwb25zZSA9IGF3YWl0IHNzbS5zZW5kKG5ldyBHZXRQYXJhbWV0ZXJDb21tYW5kKHtcbiAgICBOYW1lOiBjbG91ZG5zUGFzc3dvcmRQYXJhbWV0ZXIsXG4gICAgV2l0aERlY3J5cHRpb246IHRydWUsXG4gIH0pKVxuICBjb25zdCBjbG91ZG5zUGFzc3dvcmQgPSByZXNwb25zZS5QYXJhbWV0ZXI/LlZhbHVlIHx8ICcnXG5cbiAgY29uc3QgY2xvdWRGb3JtYXRpb24gPSBuZXcgQ2xvdWRGb3JtYXRpb25DbGllbnQoe30pXG4gIGxldCBuZXh0VG9rZW5cbiAgZG8ge1xuICAgIGNvbnN0IHJlc3BvbnNlOiBMaXN0RXhwb3J0c091dHB1dCA9IGF3YWl0IGNsb3VkRm9ybWF0aW9uLnNlbmQobmV3IExpc3RFeHBvcnRzQ29tbWFuZCh7XG4gICAgICBOZXh0VG9rZW46IG5leHRUb2tlbixcbiAgICB9KSlcbiAgICBmb3IgKGNvbnN0IGV4cG9ydE9iaiBvZiByZXNwb25zZS5FeHBvcnRzIHx8IFtdKSB7XG4gICAgICBpZiAoZXhwb3J0T2JqLk5hbWU/Lm1hdGNoKC9eQ2xvdUROUzovKSkge1xuICAgICAgICBjb25zdCBuYW1lUGFydHMgPSBleHBvcnRPYmouTmFtZS5zcGxpdCgnOicpXG4gICAgICAgIGNvbnN0IHJlc291cmNlVHlwZSA9IG5hbWVQYXJ0c1sxXVxuICAgICAgICBjb25zdCByZXNvdXJjZU5hbWUgPSBuYW1lUGFydHMuc2xpY2UoMikuam9pbignLicpXG4gICAgICAgIGNvbnN0IHJlc291cmNlVmFsdWUgPSBleHBvcnRPYmouVmFsdWUhXG4gICAgICAgIGF3YWl0IGNyZWF0ZU9yVXBkYXRlQ2xvdWRuc1Jlc291cmNlKGNsb3VkbnNVc2VybmFtZSwgY2xvdWRuc1Bhc3N3b3JkLCByZXNvdXJjZU5hbWUsIHJlc291cmNlVHlwZSwgcmVzb3VyY2VWYWx1ZSwgdHRsVmFsdWUsIHpvbmVDYWNoZSlcbiAgICAgIH1cbiAgICB9XG4gICAgbmV4dFRva2VuID0gcmVzcG9uc2UuTmV4dFRva2VuXG4gIH0gd2hpbGUgKG5leHRUb2tlbilcbn1cblxuIl19
|
|
183
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xvdWRucy1jbG91ZGZvcm1hdGlvbi1zeW5jLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vc3JjL2Nsb3VkbnMtY2xvdWRmb3JtYXRpb24tc3luYy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFBQTs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQXlCRztBQUNILG9EQUFvRTtBQUNwRSwwRUFBNEc7QUFDNUcsMkNBQThCO0FBQzlCLDJDQUEwQztBQUUxQyxxQkFBcUI7QUFDckIsT0FBTyxDQUFDLEdBQUcsQ0FBQyxtQkFBbUIsR0FBRyxHQUFHLENBQUE7QUFFckMsS0FBSyxVQUFVLGVBQWUsQ0FBQyxlQUF1QixFQUFFLGVBQXVCLEVBQUUsTUFBYyxFQUFFLFdBQW1CLEVBQUUsWUFBaUI7SUFDckksSUFBSSxPQUFPLEdBQ1QseUJBQXlCO1FBQ3pCLFdBQVc7UUFDWCxHQUFHO1FBQ0gsV0FBVyxDQUFDLFNBQVMsQ0FDbkIsTUFBTSxDQUFDLE1BQU0sQ0FDWDtZQUNFLGVBQWUsRUFBRSxlQUFlO1lBQ2hDLGVBQWUsRUFBRSxlQUFlO1NBQ2pDLEVBQ0QsWUFBWSxJQUFJLEVBQUUsQ0FDbkIsQ0FDRixDQUFBO0lBRUgsd0NBQXdDO0lBRXhDLE1BQU0sUUFBUSxHQUFHLE1BQU0sSUFBQSxvQkFBSyxFQUFDLE9BQU8sRUFBRTtRQUNwQyxNQUFNLEVBQUUsTUFBTTtRQUNkLE9BQU8sRUFBRTtZQUNQLGNBQWMsRUFBRSxrQkFBa0I7WUFDbEMsTUFBTSxFQUFFLGtCQUFrQjtTQUMzQjtLQUNGLENBQUMsQ0FBQTtJQUNGLElBQUksQ0FBQyxRQUFRLENBQUMsRUFBRSxFQUFFLENBQUM7UUFDakIsTUFBTSxTQUFTLEdBQUcsTUFBTSxRQUFRLENBQUMsSUFBSSxFQUFFLENBQUE7UUFDdkMsT0FBTyxDQUFDLEtBQUssQ0FBQyxZQUFZLEVBQUUsUUFBUSxDQUFDLE1BQU0sRUFBRSxRQUFRLENBQUMsVUFBVSxFQUFFLFNBQVMsQ0FBQyxDQUFBO1FBQzVF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UmVhZCBBV1MgQ2xvdWRGb3JtYXRpb24gRXhwb3J0cyBhbmQgYXV0b2dlbmVyYXRlIENsb3VETlMgcmVjb3JkcyBiYXNlZCBvbiB0aGVpciBuYW1lcyBhbmQgdmFsdWVzLlxuICogS2VubmV0aCBGYWxjayA8a2VubnVAY2xvdWRlbi5uZXQ+IChDKSBDbG91ZGVuIE95IDIwMjAtMjAyM1xuICpcbiAqIFRoaXMgdG9vbCBjYW4gYmUgdXNlZCB0byBhdXRvZ2VuZXJhdGUgQ2xvdUROUyByZWNvcmRzIGZvciBDbG91ZEZvcm1hdGlvbiByZXNvdXJjZXMgbGlrZVxuICogQ2xvdWRGcm9udCBkaXN0cmlidXRpb25zIGFuZCBBUEkgR2F0ZXdheSBkb21haW5zLlxuICpcbiAqIENsb3VkRm9ybWF0aW9uIGV4cG9ydCBuYW1lIG11c3Qgc3BlY2lmeSB0aGUgcmVzb3VyY2UgdHlwZSBhbmQgcmVjb3JkIGhvc3RuYW1lIGFzIGZvbGxvd3M6XG4gKiBDbG91RE5TOkNOQU1FOm15aG9zdDpleGFtcGxlOm9yZ1xuICpcbiAqIENsb3VkRm9ybWF0aW9uIGV4cG9ydCB2YWx1ZSBtdXN0IHNwZWNpZnkgdGhlIHJlY29yZCB2YWx1ZSBhcy1pcyAoZm9yIGluc3RhbmNlLCBhIGRpc3RyaWJ1dGlvbiBkb21haW4gbmFtZSk6XG4gKiB4eHh4eHh4eHh4eHh4eC5jbG91ZGZyb250Lm5ldFxuICpcbiAqIFRoZSBhYm92ZSBleGFtcGxlIHdpbGwgZ2VuZXJhdGUgdGhlIGZvbGxvd2luZyByZWNvcmQgaW4gdGhlIENsb3VETlMgem9uZSBleGFtcGxlLm9yZzpcbiAqIG15aG9zdC5leGFtcGxlLm9yZyBDTkFNRSB4eHh4eHh4eHh4eHh4eC5jbG91ZGZyb250Lm5ldFxuICpcbiAqIE90aGVyIHJlc291cmNlIHR5cGVzIGFyZSBhbHNvIGFsbG93ZWQgKEEsIEFBQUEsIEFMSUFTLCBldGMpLlxuICpcbiAqIENvbW1hbmQgbGluZSB1c2FnZTogQVdTX1BST0ZJTEU9eHh4IHRzLW5vZGUgY2xvdWRucy1jbG91ZGZvcm1hdGlvbi1zeW5jLnRzIDxjbG91ZG5zLXVzZXJuYW1lPiA8Y2xvdWRucy1wYXNzd29yZC1wYXJhbWV0ZXItbmFtZT4gW3R0bCBbc3RhY2tOYW1lXV1cbiAqXG4gKiBBV1NfUFJPRklMRT14eHggLSBTcGVjaWZ5IHlvdXIgQVdTIHByb2ZpbGUgaW4gfi8uYXdzL2NyZWRlbnRpYWxzIGFzIGFuIGVudmlyb25tZW50IHZhcmlhYmxlXG4gKiA8Y2xvdWRucy11c2VybmFtZT4gLSBDbG91RE5TIEFQSSBzdWItYXV0aC11c2VyXG4gKiA8Y2xvdWRucy1wYXNzd29yZC1wYXJhbWV0ZXItbmFtZT4gLSBTU00gUGFyYW1ldGVyIHdpdGggdGhlIGVuY3J5cHRlZCBDbG91RE5TIEFQSSBwYXNzd29yZFxuICogW3R0bF0gLSBPcHRpb25hbCBUVEwgZm9yIGdlbmVyYXRlZCByZWNvcmRzIChkZWZhdWx0cyB0byAzMDApXG4gKiBbc3RhY2tOYW1lXSAtIE9wdGlvbmFsIENsb3VkRm9ybWF0aW9uIHN0YWNrIG5hbWUgdG8gbGltaXQgdGhlIGV4cG9ydHMgdG8gc2NhbiAoZGVmYXVsdHMgdG8gYWxsIHN0YWNrcylcbiAqL1xuaW1wb3J0IHsgU1NNQ2xpZW50LCBHZXRQYXJhbWV0ZXJDb21tYW5kIH0gZnJvbSAnQGF3cy1zZGsvY2xpZW50LXNzbSdcbmltcG9ydCB7IENsb3VkRm9ybWF0aW9uQ2xpZW50LCBMaXN0RXhwb3J0c0NvbW1hbmQsIExpc3RFeHBvcnRzT3V0cHV0IH0gZnJvbSAnQGF3cy1zZGsvY2xpZW50LWNsb3VkZm9ybWF0aW9uJ1xuaW1wb3J0IGZldGNoIGZyb20gJ25vZGUtZmV0Y2gnXG5pbXBvcnQgKiBhcyBxdWVyeXN0cmluZyBmcm9tICdxdWVyeXN0cmluZydcblxuLy8gTG9hZCB+Ly5hd3MvY29uZmlnXG5wcm9jZXNzLmVudi5BV1NfU0RLX0xPQURfQ09ORklHID0gJzEnXG5cbmFzeW5jIGZ1bmN0aW9uIGNsb3VkbnNSZXN0Q2FsbChjbG91ZG5zVXNlcm5hbWU6IHN0cmluZywgY2xvdWRuc1Bhc3N3b3JkOiBzdHJpbmcsIG1ldGhvZDogc3RyaW5nLCByZWxhdGl2ZVVybDogc3RyaW5nLCBxdWVyeU9wdGlvbnM6IGFueSkge1xuICBsZXQgZnVsbFVybCA9XG4gICAgJ2h0dHBzOi8vYXBpLmNsb3VkbnMubmV0JyArXG4gICAgcmVsYXRpdmVVcmwgK1xuICAgICc/JyArXG4gICAgcXVlcnlzdHJpbmcuc3RyaW5naWZ5KFxuICAgICAgT2JqZWN0LmFzc2lnbihcbiAgICAgICAge1xuICAgICAgICAgICdzdWItYXV0aC11c2VyJzogY2xvdWRuc1VzZXJuYW1lLFxuICAgICAgICAgICdhdXRoLXBhc3N3b3JkJzogY2xvdWRuc1Bhc3N3b3JkLFxuICAgICAgICB9LFxuICAgICAgICBxdWVyeU9wdGlvbnMgfHwge31cbiAgICAgIClcbiAgICApXG5cbiAgLy8gY29uc29sZS5sb2coJ05vdGU6IENhbGxpbmcnLCBmdWxsVXJsKVxuXG4gIGNvbnN0IHJlc3BvbnNlID0gYXdhaXQgZmV0Y2goZnVsbFVybCwge1xuICAgIG1ldGhvZDogbWV0aG9kLFxuICAgIGhlYWRlcnM6IHtcbiAgICAgICdDb250ZW50LVR5cGUnOiAnYXBwbGljYXRpb24vanNvbicsXG4gICAgICBBY2NlcHQ6ICdhcHBsaWNhdGlvbi9qc29uJyxcbiAgICB9LFxuICB9KVxuICBpZiAoIXJlc3BvbnNlLm9rKSB7XG4gICAgY29uc3QgZXJyb3JUZXh0ID0gYXdhaXQgcmVzcG9uc2UudGV4dCgpXG4gICAgY29uc29sZS5lcnJvcignSFRUUCBFcnJvcicsIHJlc3BvbnNlLnN0YXR1cywgcmVzcG9uc2Uuc3RhdHVzVGV4dCwgZXJyb3JUZXh0KVxuICAgIHRocm93IG5ldyBFcnJvcihlcnJvclRleHQpXG4gIH1cbiAgcmV0dXJuIHJlc3BvbnNlLmpzb24oKVxufVxuXG5hc3luYyBmdW5jdGlvbiBhdXRvRGV0ZWN0Q2xvdWRuc0hvc3RBbmRab25lKGNsb3VkbnNVc2VybmFtZTogc3RyaW5nLCBjbG91ZG5zUGFzc3dvcmQ6IHN0cmluZywgbmFtZTogc3RyaW5nLCB6b25lQ2FjaGU6IGFueSkge1xuICBjb25zdCBuYW1lUGFydHMgPSBuYW1lLnNwbGl0KCcuJylcblxuICAvLyBab25lIGFuZCBob3N0IG5hbWUgZm9yIHh4eC50bGRcbiAgY29uc3QgaG9zdE5hbWUxID0gbmFtZVBhcnRzLnNsaWNlKDAsIG5hbWVQYXJ0cy5sZW5ndGggLSAyKS5qb2luKCcuJylcbiAgY29uc3Qgem9uZU5hbWUxID0gbmFtZVBhcnRzLnNsaWNlKG5hbWVQYXJ0cy5sZW5ndGggLSAyKS5qb2luKCcuJylcblxuICAvLyBab25lIGFuZCBob3N0IG5hbWUgZm9yIHh4eC5zdWJ0bGQudGxkXG4gIGNvbnN0IGhvc3ROYW1lMiA9IG5hbWVQYXJ0cy5zbGljZSgwLCBuYW1lUGFydHMubGVuZ3RoIC0gMykuam9pbignLicpXG4gIGNvbnN0IHpvbmVOYW1lMiA9IG5hbWVQYXJ0cy5zbGljZShuYW1lUGFydHMubGVuZ3RoIC0gMykuam9pbignLicpXG5cbiAgLy8gQ2hlY2sgd2hpY2ggem9uZSBleGlzdHNcbiAgY29uc3Qgem9uZVJlc3BvbnNlMSA9XG4gICAgem9uZUNhY2hlW3pvbmVOYW1lMV0gfHxcbiAgICAoYXdhaXQgY2xvdWRuc1Jlc3RDYWxsKGNsb3VkbnNVc2VybmFtZSwgY2xvdWRuc1Bhc3N3b3JkLCAnR0VUJywgJy9kbnMvZ2V0LXpvbmUtaW5mby5qc29uJywge1xuICAgICAgJ2RvbWFpbi1uYW1lJzogem9uZU5hbWUxLFxuICAgIH0pKVxuICB6b25lQ2FjaGVbem9uZU5hbWUxXSA9IHpvbmVSZXNwb25zZTFcbiAgY29uc3Qgem9uZVJlc3BvbnNlMiA9XG4gICAgem9uZUNhY2hlW3pvbmVOYW1lMl0gfHxcbiAgICAoYXdhaXQgY2xvdWRuc1Jlc3RDYWxsKGNsb3VkbnNVc2VybmFtZSwgY2xvdWRuc1Bhc3N3b3JkLCAnR0VUJywgJy9kbnMvZ2V0LXpvbmUtaW5mby5qc29uJywge1xuICAgICAgJ2RvbWFpbi1uYW1lJzogem9uZU5hbWUyLFxuICAgIH0pKVxuICB6b25lQ2FjaGVbem9uZU5hbWUyXSA9IHpvbmVSZXNwb25zZTJcblxuICAvLyBjb25zb2xlLmxvZygnTm90ZTogUmVzcG9uc2UgZm9yIGhvc3QnLCBob3N0TmFtZTEsICdpbiB6b25lJywgem9uZU5hbWUxLCAnOicsIHpvbmVSZXNwb25zZTEpXG4gIC8vIGNvbnNvbGUubG9nKCdOb3RlOiBSZXNwb25zZSBmb3IgaG9zdCcsIGhvc3ROYW1lMiwgJ2luIHpvbmUnLCB6b25lTmFtZTIsICc6Jywgem9uZVJlc3BvbnNlMilcblxuICBjb25zdCB6b25lTmFtZSA9IHpvbmVSZXNwb25zZTEuc3RhdHVzID09PSAnMScgPyB6b25lTmFtZTEgOiB6b25lUmVzcG9uc2UyLnN0YXR1cyA9PT0gJzEnID8gem9uZU5hbWUyIDogJydcbiAgY29uc3QgaG9zdE5hbWUgPSB6b25lUmVzcG9uc2UxLnN0YXR1cyA9PT0gJzEnID8gaG9zdE5hbWUxIDogem9uZVJlc3BvbnNlMi5zdGF0dXMgPT09ICcxJyA/IGhvc3ROYW1lMiA6ICcnXG4gIGlmICghem9uZU5hbWUpIHtcbiAgICAvLyBOZWl0aGVyIHpvbmUgZXhpc3RzXG4gICAgdGhyb3cgbmV3IEVycm9yKCdab25lIE5vdCBGb3VuZDogJyArIG5hbWUpXG4gIH1cbiAgcmV0dXJuIHtcbiAgICBob3N0TmFtZTogaG9zdE5hbWUsXG4gICAgem9uZU5hbWU6IHpvbmVOYW1lLFxuICB9XG59XG5cbmFzeW5jIGZ1bmN0aW9uIGNyZWF0ZU9yVXBkYXRlQ2xvdWRuc1Jlc291cmNlKFxuICBjbG91ZG5zVXNlcm5hbWU6IHN0cmluZyxcbiAgY2xvdWRuc1Bhc3N3b3JkOiBzdHJpbmcsXG4gIG5hbWU6IHN0cmluZyxcbiAgdHlwZTogc3RyaW5nLFxuICB2YWx1ZTogc3RyaW5nLFxuICB0dGxWYWx1ZTogc3RyaW5nLFxuICB6b25lQ2FjaGU6IGFueVxuKSB7XG4gIGNvbnN0IHsgem9uZU5hbWUsIGhvc3ROYW1lIH0gPSBhd2FpdCBhdXRvRGV0ZWN0Q2xvdWRuc0hvc3RBbmRab25lKGNsb3VkbnNVc2VybmFtZSwgY2xvdWRuc1Bhc3N3b3JkLCBuYW1lLCB6b25lQ2FjaGUpXG4gIC8vIERvZXMgdGhlIHJlY29yZCBleGlzdD9cbiAgY29uc3QgcmVjb3Jkc1Jlc3BvbnNlID0gYXdhaXQgY2xvdWRuc1Jlc3RDYWxsKGNsb3VkbnNVc2VybmFtZSwgY2xvdWRuc1Bhc3N3b3JkLCAnR0VUJywgJy9kbnMvcmVjb3Jkcy5qc29uJywge1xuICAgICdkb21haW4tbmFtZSc6IHpvbmVOYW1lLFxuICAgIGhvc3Q6IGhvc3ROYW1lLFxuICAgIHR5cGU6IHR5cGUsXG4gIH0pXG4gIGNvbnN0IGV4aXN0aW5nUmVjb3JkOiBhbnkgPSBPYmplY3QudmFsdWVzKHJlY29yZHNSZXNwb25zZSlbMF1cbiAgaWYgKGV4aXN0aW5nUmVjb3JkPy5ob3N0ID09PSBob3N0TmFtZSAmJiBleGlzdGluZ1JlY29yZD8udHlwZSA9PT0gdHlwZSAmJiBleGlzdGluZ1JlY29yZD8udHRsID09PSB0dGxWYWx1ZSAmJiBleGlzdGluZ1JlY29yZD8ucmVjb3JkID09PSB2YWx1ZSkge1xuICAgIC8vIFJlY29yZCBleGlzdHMgYWxyZWFkeSAtIG5vIGNoYW5nZVxuICAgIGNvbnNvbGUubG9nKCdPSycsIG5hbWUsIHR5cGUsIHR0bFZhbHVlLCB2YWx1ZSwgJ1pPTkUnLCB6b25lTmFtZSwgJ0hPU1QnLCBob3N0TmFtZSlcbiAgfSBlbHNlIGlmIChleGlzdGluZ1JlY29yZD8uaWQpIHtcbiAgICAvLyBVcGRhdGUgcmVjb3JkXG4gICAgY29uc29sZS5sb2coJ1VQREFURScsIG5hbWUsIHR5cGUsIHR0bFZhbHVlLCB2YWx1ZSwgJ1pPTkUnLCB6b25lTmFtZSwgJ0hPU1QnLCBob3N0TmFtZSlcbiAgICBjb25zdCByZXN1bHQgPSBhd2FpdCBjbG91ZG5zUmVzdENhbGwoY2xvdWRuc1VzZXJuYW1lLCBjbG91ZG5zUGFzc3dvcmQsICdQT1NUJywgJy9kbnMvbW9kLXJlY29yZC5qc29uJywge1xuICAgICAgJ2RvbWFpbi1uYW1lJzogem9uZU5hbWUsXG4gICAgICAncmVjb3JkLWlkJzogZXhpc3RpbmdSZWNvcmQ/LmlkLFxuICAgICAgaG9zdDogaG9zdE5hbWUsXG4gICAgICAncmVjb3JkLXR5cGUnOiB0eXBlLFxuICAgICAgcmVjb3JkOiB2YWx1ZSxcbiAgICAgIHR0bDogdHRsVmFsdWUsXG4gICAgfSlcbiAgICBpZiAocmVzdWx0LnN0YXR1cyA9PT0gJ0ZhaWxlZCcpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcignTW9kaWZ5IHJlY29yZCBmYWlsZWQ6ICcgKyAocmVzdWx0LnN0YXR1c01lc3NhZ2UgfHwgcmVzdWx0LnN0YXR1c0Rlc2NyaXB0aW9uKSlcbiAgICB9XG4gIH0gZWxzZSB7XG4gICAgLy8gQ3JlYXRlIHJlY29yZFxuICAgIGNvbnNvbGUubG9nKCdDUkVBVEUnLCBuYW1lLCB0eXBlLCB0dGxWYWx1ZSwgdmFsdWUsICdaT05FJywgem9uZU5hbWUsICdIT1NUJywgaG9zdE5hbWUpXG4gICAgY29uc3QgcmVzdWx0ID0gYXdhaXQgY2xvdWRuc1Jlc3RDYWxsKGNsb3VkbnNVc2VybmFtZSwgY2xvdWRuc1Bhc3N3b3JkLCAnUE9TVCcsICcvZG5zL2FkZC1yZWNvcmQuanNvbicsIHtcbiAgICAgICdkb21haW4tbmFtZSc6IHpvbmVOYW1lLFxuICAgICAgaG9zdDogaG9zdE5hbWUsXG4gICAgICAncmVjb3JkLXR5cGUnOiB0eXBlLFxuICAgICAgcmVjb3JkOiB2YWx1ZSxcbiAgICAgIHR0bDogdHRsVmFsdWUsXG4gICAgfSlcbiAgICBpZiAocmVzdWx0LnN0YXR1cyA9PT0gJ0ZhaWxlZCcpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcignQWRkIHJlY29yZCBmYWlsZWQ6ICcgKyAocmVzdWx0LnN0YXR1c01lc3NhZ2UgfHwgcmVzdWx0LnN0YXR1c0Rlc2NyaXB0aW9uKSlcbiAgICB9XG4gIH1cbn1cblxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIG1haW4oKSB7XG4gIGNvbnNvbGUubG9nKCdDbG91RE5TIENsb3VkRm9ybWF0aW9uIFN5bmMgYnkgS2VubmV0aCBGYWxjayA8a2VubnVAY2xvdWRlbi5uZXQ+IChDKSBDbG91ZGVuIE95IDIwMjAtMjAyMycpXG4gIGNvbnN0IGNsb3VkbnNVc2VybmFtZSA9IHByb2Nlc3MuYXJndlsyXVxuICBjb25zdCBjbG91ZG5zUGFzc3dvcmRQYXJhbWV0ZXIgPSBwcm9jZXNzLmFyZ3ZbM11cbiAgY29uc3QgdHRsVmFsdWUgPSBwcm9jZXNzLmFyZ3ZbNF0gfHwgJzMwMCdcbiAgY29uc3Qgc3RhY2tOYW1lID0gcHJvY2Vzcy5hcmd2WzVdIHx8ICcnXG4gIGlmICghY2xvdWRuc1VzZXJuYW1lKSB7XG4gICAgY29uc29sZS5lcnJvcignVXNhZ2U6IGNsb3VkbnMtY2xvdWRmb3JtYXRpb24tc3luYyA8Y2xvdWRucy11c2VybmFtZT4gPGNsb3VkbnMtcGFzc3dvcmQtcGFyYW1ldGVyLW5hbWU+IFt0dGwgW3N0YWNrTmFtZV1dJylcbiAgICBwcm9jZXNzLmV4aXQoMSlcbiAgfVxuICBpZiAoIWNsb3VkbnNQYXNzd29yZFBhcmFtZXRlcikge1xuICAgIGNvbnNvbGUuZXJyb3IoJ1VzYWdlOiBjbG91ZG5zLWNsb3VkZm9ybWF0aW9uLXN5bmMgPGNsb3VkbnMtdXNlcm5hbWU+IDxjbG91ZG5zLXBhc3N3b3JkLXBhcmFtZXRlci1uYW1lPiBbdHRsIFtzdGFja05hbWVdXScpXG4gICAgcHJvY2Vzcy5leGl0KDEpXG4gIH1cblxuICBjb25zdCBzc20gPSBuZXcgU1NNQ2xpZW50KHt9KVxuICBjb25zdCB6b25lQ2FjaGUgPSB7fVxuXG4gIGNvbnN0IHJlc3BvbnNlID0gYXdhaXQgc3NtLnNlbmQoXG4gICAgbmV3IEdldFBhcmFtZXRlckNvbW1hbmQoe1xuICAgICAgTmFtZTogY2xvdWRuc1Bhc3N3b3JkUGFyYW1ldGVyLFxuICAgICAgV2l0aERlY3J5cHRpb246IHRydWUsXG4gICAgfSlcbiAgKVxuICBjb25zdCBjbG91ZG5zUGFzc3dvcmQgPSByZXNwb25zZS5QYXJhbWV0ZXI/LlZhbHVlIHx8ICcnXG5cbiAgY29uc3QgY2xvdWRGb3JtYXRpb24gPSBuZXcgQ2xvdWRGb3JtYXRpb25DbGllbnQoe30pXG4gIGxldCBuZXh0VG9rZW5cbiAgZG8ge1xuICAgIGNvbnN0IHJlc3BvbnNlOiBMaXN0RXhwb3J0c091dHB1dCA9IGF3YWl0IGNsb3VkRm9ybWF0aW9uLnNlbmQoXG4gICAgICBuZXcgTGlzdEV4cG9ydHNDb21tYW5kKHtcbiAgICAgICAgTmV4dFRva2VuOiBuZXh0VG9rZW4sXG4gICAgICB9KVxuICAgIClcbiAgICBmb3IgKGNvbnN0IGV4cG9ydE9iaiBvZiByZXNwb25zZS5FeHBvcnRzIHx8IFtdKSB7XG4gICAgICBpZiAoc3RhY2tOYW1lICYmIGV4cG9ydE9iai5FeHBvcnRpbmdTdGFja0lkICE9PSBzdGFja05hbWUpIHtcbiAgICAgICAgLy8gQ2hlY2sgaWYgdGhlIG5hbWUgcGFydCBvZiB0aGUgSUQgbWF0Y2hlcyBhcm46YXdzOmNsb3VkZm9ybWF0aW9uOmV1LXdlc3QtMTo8eHh4PjpzdGFjay88bmFtZT4vPHh4eD5cbiAgICAgICAgY29uc3QgbSA9IGV4cG9ydE9iai5FeHBvcnRpbmdTdGFja0lkPy5tYXRjaCgvXmFybjpbXjpdKzpjbG91ZGZvcm1hdGlvbjpbXjpdKzpbXjpdKzpzdGFja1xcLyhbXlxcL10rKVxcLy8pXG4gICAgICAgIGlmICghbSB8fCBtWzFdICE9PSBzdGFja05hbWUpIHtcbiAgICAgICAgICAvLyBTdGFjayBJRCBuYW1lIHBhcnQgZGlkbid0IG1hdGNoIGdpdmVuIHN0YWNrTmFtZSwgc28gc2tpcCBpdFxuICAgICAgICAgIGNvbnRpbnVlXG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIGlmIChleHBvcnRPYmouTmFtZT8ubWF0Y2goL15DbG91RE5TOi8pKSB7XG4gICAgICAgIGNvbnN0IG5hbWVQYXJ0cyA9IGV4cG9ydE9iai5OYW1lLnNwbGl0KCc6JylcbiAgICAgICAgY29uc3QgcmVzb3VyY2VUeXBlID0gbmFtZVBhcnRzWzFdXG4gICAgICAgIGNvbnN0IHJlc291cmNlTmFtZSA9IG5hbWVQYXJ0cy5zbGljZSgyKS5qb2luKCcuJylcbiAgICAgICAgY29uc3QgcmVzb3VyY2VWYWx1ZSA9IGV4cG9ydE9iai5WYWx1ZSFcbiAgICAgICAgYXdhaXQgY3JlYXRlT3JVcGRhdGVDbG91ZG5zUmVzb3VyY2UoY2xvdWRuc1VzZXJuYW1lLCBjbG91ZG5zUGFzc3dvcmQsIHJlc291cmNlTmFtZSwgcmVzb3VyY2VUeXBlLCByZXNvdXJjZVZhbHVlLCB0dGxWYWx1ZSwgem9uZUNhY2hlKVxuICAgICAgfVxuICAgIH1cbiAgICBuZXh0VG9rZW4gPSByZXNwb25zZS5OZXh0VG9rZW5cbiAgfSB3aGlsZSAobmV4dFRva2VuKVxufVxuIl19
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cloudns-cloudformation-sync",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"description": "Copyright (C) Clouden Oy 2023",
|
|
5
5
|
"main": "lib/cloudns-cloudformation-sync.js",
|
|
6
6
|
"bin": {
|
|
@@ -28,13 +28,13 @@
|
|
|
28
28
|
},
|
|
29
29
|
"homepage": "https://github.com/cloudeninc/cloudns-cloudformation-sync#readme",
|
|
30
30
|
"dependencies": {
|
|
31
|
-
"@aws-sdk/client-cloudformation": "^3.
|
|
32
|
-
"@aws-sdk/client-ssm": "^3.
|
|
31
|
+
"@aws-sdk/client-cloudformation": "^3.454.0",
|
|
32
|
+
"@aws-sdk/client-ssm": "^3.454.0",
|
|
33
33
|
"node-fetch": "^2.6.0"
|
|
34
34
|
},
|
|
35
35
|
"devDependencies": {
|
|
36
|
-
"@types/node": "^
|
|
36
|
+
"@types/node": "^20.10.0",
|
|
37
37
|
"@types/node-fetch": "^2.5.7",
|
|
38
|
-
"typescript": "^
|
|
38
|
+
"typescript": "^5.3.2"
|
|
39
39
|
}
|
|
40
40
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Read AWS CloudFormation Exports and autogenerate ClouDNS records based on their names and values.
|
|
3
|
-
* Kenneth Falck <kennu@clouden.net> (C) Clouden Oy 2020
|
|
3
|
+
* Kenneth Falck <kennu@clouden.net> (C) Clouden Oy 2020-2023
|
|
4
4
|
*
|
|
5
5
|
* This tool can be used to autogenerate ClouDNS records for CloudFormation resources like
|
|
6
6
|
* CloudFront distributions and API Gateway domains.
|
|
@@ -16,12 +16,13 @@
|
|
|
16
16
|
*
|
|
17
17
|
* Other resource types are also allowed (A, AAAA, ALIAS, etc).
|
|
18
18
|
*
|
|
19
|
-
* Command line usage: AWS_PROFILE=xxx ts-node cloudns-cloudformation-sync.ts <cloudns-username> <cloudns-password-parameter-name> [ttl]
|
|
19
|
+
* Command line usage: AWS_PROFILE=xxx ts-node cloudns-cloudformation-sync.ts <cloudns-username> <cloudns-password-parameter-name> [ttl [stackName]]
|
|
20
20
|
*
|
|
21
21
|
* AWS_PROFILE=xxx - Specify your AWS profile in ~/.aws/credentials as an environment variable
|
|
22
22
|
* <cloudns-username> - ClouDNS API sub-auth-user
|
|
23
23
|
* <cloudns-password-parameter-name> - SSM Parameter with the encrypted ClouDNS API password
|
|
24
24
|
* [ttl] - Optional TTL for generated records (defaults to 300)
|
|
25
|
+
* [stackName] - Optional CloudFormation stack name to limit the exports to scan (defaults to all stacks)
|
|
25
26
|
*/
|
|
26
27
|
import { SSMClient, GetParameterCommand } from '@aws-sdk/client-ssm'
|
|
27
28
|
import { CloudFormationClient, ListExportsCommand, ListExportsOutput } from '@aws-sdk/client-cloudformation'
|
|
@@ -32,10 +33,19 @@ import * as querystring from 'querystring'
|
|
|
32
33
|
process.env.AWS_SDK_LOAD_CONFIG = '1'
|
|
33
34
|
|
|
34
35
|
async function cloudnsRestCall(cloudnsUsername: string, cloudnsPassword: string, method: string, relativeUrl: string, queryOptions: any) {
|
|
35
|
-
let fullUrl =
|
|
36
|
-
'
|
|
37
|
-
|
|
38
|
-
|
|
36
|
+
let fullUrl =
|
|
37
|
+
'https://api.cloudns.net' +
|
|
38
|
+
relativeUrl +
|
|
39
|
+
'?' +
|
|
40
|
+
querystring.stringify(
|
|
41
|
+
Object.assign(
|
|
42
|
+
{
|
|
43
|
+
'sub-auth-user': cloudnsUsername,
|
|
44
|
+
'auth-password': cloudnsPassword,
|
|
45
|
+
},
|
|
46
|
+
queryOptions || {}
|
|
47
|
+
)
|
|
48
|
+
)
|
|
39
49
|
|
|
40
50
|
// console.log('Note: Calling', fullUrl)
|
|
41
51
|
|
|
@@ -43,8 +53,8 @@ async function cloudnsRestCall(cloudnsUsername: string, cloudnsPassword: string,
|
|
|
43
53
|
method: method,
|
|
44
54
|
headers: {
|
|
45
55
|
'Content-Type': 'application/json',
|
|
46
|
-
|
|
47
|
-
}
|
|
56
|
+
Accept: 'application/json',
|
|
57
|
+
},
|
|
48
58
|
})
|
|
49
59
|
if (!response.ok) {
|
|
50
60
|
const errorText = await response.text()
|
|
@@ -58,28 +68,32 @@ async function autoDetectCloudnsHostAndZone(cloudnsUsername: string, cloudnsPass
|
|
|
58
68
|
const nameParts = name.split('.')
|
|
59
69
|
|
|
60
70
|
// Zone and host name for xxx.tld
|
|
61
|
-
const hostName1 = nameParts.slice(0, nameParts.length-2).join('.')
|
|
62
|
-
const zoneName1 = nameParts.slice(nameParts.length-2).join('.')
|
|
71
|
+
const hostName1 = nameParts.slice(0, nameParts.length - 2).join('.')
|
|
72
|
+
const zoneName1 = nameParts.slice(nameParts.length - 2).join('.')
|
|
63
73
|
|
|
64
74
|
// Zone and host name for xxx.subtld.tld
|
|
65
|
-
const hostName2 = nameParts.slice(0, nameParts.length-3).join('.')
|
|
66
|
-
const zoneName2 = nameParts.slice(nameParts.length-3).join('.')
|
|
75
|
+
const hostName2 = nameParts.slice(0, nameParts.length - 3).join('.')
|
|
76
|
+
const zoneName2 = nameParts.slice(nameParts.length - 3).join('.')
|
|
67
77
|
|
|
68
78
|
// Check which zone exists
|
|
69
|
-
const zoneResponse1 =
|
|
70
|
-
|
|
71
|
-
|
|
79
|
+
const zoneResponse1 =
|
|
80
|
+
zoneCache[zoneName1] ||
|
|
81
|
+
(await cloudnsRestCall(cloudnsUsername, cloudnsPassword, 'GET', '/dns/get-zone-info.json', {
|
|
82
|
+
'domain-name': zoneName1,
|
|
83
|
+
}))
|
|
72
84
|
zoneCache[zoneName1] = zoneResponse1
|
|
73
|
-
const zoneResponse2 =
|
|
74
|
-
|
|
75
|
-
|
|
85
|
+
const zoneResponse2 =
|
|
86
|
+
zoneCache[zoneName2] ||
|
|
87
|
+
(await cloudnsRestCall(cloudnsUsername, cloudnsPassword, 'GET', '/dns/get-zone-info.json', {
|
|
88
|
+
'domain-name': zoneName2,
|
|
89
|
+
}))
|
|
76
90
|
zoneCache[zoneName2] = zoneResponse2
|
|
77
91
|
|
|
78
92
|
// console.log('Note: Response for host', hostName1, 'in zone', zoneName1, ':', zoneResponse1)
|
|
79
93
|
// console.log('Note: Response for host', hostName2, 'in zone', zoneName2, ':', zoneResponse2)
|
|
80
94
|
|
|
81
|
-
const zoneName =
|
|
82
|
-
const hostName =
|
|
95
|
+
const zoneName = zoneResponse1.status === '1' ? zoneName1 : zoneResponse2.status === '1' ? zoneName2 : ''
|
|
96
|
+
const hostName = zoneResponse1.status === '1' ? hostName1 : zoneResponse2.status === '1' ? hostName2 : ''
|
|
83
97
|
if (!zoneName) {
|
|
84
98
|
// Neither zone exists
|
|
85
99
|
throw new Error('Zone Not Found: ' + name)
|
|
@@ -90,13 +104,21 @@ async function autoDetectCloudnsHostAndZone(cloudnsUsername: string, cloudnsPass
|
|
|
90
104
|
}
|
|
91
105
|
}
|
|
92
106
|
|
|
93
|
-
async function createOrUpdateCloudnsResource(
|
|
107
|
+
async function createOrUpdateCloudnsResource(
|
|
108
|
+
cloudnsUsername: string,
|
|
109
|
+
cloudnsPassword: string,
|
|
110
|
+
name: string,
|
|
111
|
+
type: string,
|
|
112
|
+
value: string,
|
|
113
|
+
ttlValue: string,
|
|
114
|
+
zoneCache: any
|
|
115
|
+
) {
|
|
94
116
|
const { zoneName, hostName } = await autoDetectCloudnsHostAndZone(cloudnsUsername, cloudnsPassword, name, zoneCache)
|
|
95
117
|
// Does the record exist?
|
|
96
118
|
const recordsResponse = await cloudnsRestCall(cloudnsUsername, cloudnsPassword, 'GET', '/dns/records.json', {
|
|
97
119
|
'domain-name': zoneName,
|
|
98
|
-
|
|
99
|
-
|
|
120
|
+
host: hostName,
|
|
121
|
+
type: type,
|
|
100
122
|
})
|
|
101
123
|
const existingRecord: any = Object.values(recordsResponse)[0]
|
|
102
124
|
if (existingRecord?.host === hostName && existingRecord?.type === type && existingRecord?.ttl === ttlValue && existingRecord?.record === value) {
|
|
@@ -108,60 +130,73 @@ async function createOrUpdateCloudnsResource(cloudnsUsername: string, cloudnsPas
|
|
|
108
130
|
const result = await cloudnsRestCall(cloudnsUsername, cloudnsPassword, 'POST', '/dns/mod-record.json', {
|
|
109
131
|
'domain-name': zoneName,
|
|
110
132
|
'record-id': existingRecord?.id,
|
|
111
|
-
|
|
133
|
+
host: hostName,
|
|
112
134
|
'record-type': type,
|
|
113
|
-
|
|
114
|
-
|
|
135
|
+
record: value,
|
|
136
|
+
ttl: ttlValue,
|
|
115
137
|
})
|
|
116
138
|
if (result.status === 'Failed') {
|
|
117
|
-
throw new Error(
|
|
139
|
+
throw new Error('Modify record failed: ' + (result.statusMessage || result.statusDescription))
|
|
118
140
|
}
|
|
119
141
|
} else {
|
|
120
142
|
// Create record
|
|
121
143
|
console.log('CREATE', name, type, ttlValue, value, 'ZONE', zoneName, 'HOST', hostName)
|
|
122
144
|
const result = await cloudnsRestCall(cloudnsUsername, cloudnsPassword, 'POST', '/dns/add-record.json', {
|
|
123
145
|
'domain-name': zoneName,
|
|
124
|
-
|
|
146
|
+
host: hostName,
|
|
125
147
|
'record-type': type,
|
|
126
|
-
|
|
127
|
-
|
|
148
|
+
record: value,
|
|
149
|
+
ttl: ttlValue,
|
|
128
150
|
})
|
|
129
151
|
if (result.status === 'Failed') {
|
|
130
|
-
throw new Error(
|
|
152
|
+
throw new Error('Add record failed: ' + (result.statusMessage || result.statusDescription))
|
|
131
153
|
}
|
|
132
154
|
}
|
|
133
155
|
}
|
|
134
156
|
|
|
135
157
|
export async function main() {
|
|
136
|
-
console.log('ClouDNS CloudFormation Sync by Kenneth Falck <kennu@clouden.net> (C) Clouden Oy
|
|
158
|
+
console.log('ClouDNS CloudFormation Sync by Kenneth Falck <kennu@clouden.net> (C) Clouden Oy 2020-2023')
|
|
137
159
|
const cloudnsUsername = process.argv[2]
|
|
138
160
|
const cloudnsPasswordParameter = process.argv[3]
|
|
139
161
|
const ttlValue = process.argv[4] || '300'
|
|
162
|
+
const stackName = process.argv[5] || ''
|
|
140
163
|
if (!cloudnsUsername) {
|
|
141
|
-
console.error('Usage: cloudns-cloudformation-sync <cloudns-username> <cloudns-password-parameter-name> [ttl]')
|
|
164
|
+
console.error('Usage: cloudns-cloudformation-sync <cloudns-username> <cloudns-password-parameter-name> [ttl [stackName]]')
|
|
142
165
|
process.exit(1)
|
|
143
166
|
}
|
|
144
167
|
if (!cloudnsPasswordParameter) {
|
|
145
|
-
console.error('Usage: cloudns-cloudformation-sync <cloudns-username> <cloudns-password-parameter-name> [ttl]')
|
|
168
|
+
console.error('Usage: cloudns-cloudformation-sync <cloudns-username> <cloudns-password-parameter-name> [ttl [stackName]]')
|
|
146
169
|
process.exit(1)
|
|
147
170
|
}
|
|
148
171
|
|
|
149
172
|
const ssm = new SSMClient({})
|
|
150
173
|
const zoneCache = {}
|
|
151
174
|
|
|
152
|
-
const response = await ssm.send(
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
175
|
+
const response = await ssm.send(
|
|
176
|
+
new GetParameterCommand({
|
|
177
|
+
Name: cloudnsPasswordParameter,
|
|
178
|
+
WithDecryption: true,
|
|
179
|
+
})
|
|
180
|
+
)
|
|
156
181
|
const cloudnsPassword = response.Parameter?.Value || ''
|
|
157
182
|
|
|
158
183
|
const cloudFormation = new CloudFormationClient({})
|
|
159
184
|
let nextToken
|
|
160
185
|
do {
|
|
161
|
-
const response: ListExportsOutput = await cloudFormation.send(
|
|
162
|
-
|
|
163
|
-
|
|
186
|
+
const response: ListExportsOutput = await cloudFormation.send(
|
|
187
|
+
new ListExportsCommand({
|
|
188
|
+
NextToken: nextToken,
|
|
189
|
+
})
|
|
190
|
+
)
|
|
164
191
|
for (const exportObj of response.Exports || []) {
|
|
192
|
+
if (stackName && exportObj.ExportingStackId !== stackName) {
|
|
193
|
+
// Check if the name part of the ID matches arn:aws:cloudformation:eu-west-1:<xxx>:stack/<name>/<xxx>
|
|
194
|
+
const m = exportObj.ExportingStackId?.match(/^arn:[^:]+:cloudformation:[^:]+:[^:]+:stack\/([^\/]+)\//)
|
|
195
|
+
if (!m || m[1] !== stackName) {
|
|
196
|
+
// Stack ID name part didn't match given stackName, so skip it
|
|
197
|
+
continue
|
|
198
|
+
}
|
|
199
|
+
}
|
|
165
200
|
if (exportObj.Name?.match(/^ClouDNS:/)) {
|
|
166
201
|
const nameParts = exportObj.Name.split(':')
|
|
167
202
|
const resourceType = nameParts[1]
|
|
@@ -173,4 +208,3 @@ export async function main() {
|
|
|
173
208
|
nextToken = response.NextToken
|
|
174
209
|
} while (nextToken)
|
|
175
210
|
}
|
|
176
|
-
|