@brunwig/mup-aws-beanstalk 0.8.1
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/.babelrc +6 -0
- package/.eslintignore +2 -0
- package/.eslintrc.yml +10 -0
- package/.github/FUNDING.yml +12 -0
- package/changelog.md +56 -0
- package/docs/getting-started.md +83 -0
- package/docs/index.md +333 -0
- package/index.js +1 -0
- package/lib/assets/env.sh +30 -0
- package/lib/assets/env.yaml +19 -0
- package/lib/assets/graceful_shutdown.sh +14 -0
- package/lib/assets/graceful_shutdown.yaml +7 -0
- package/lib/assets/health-check.js +54 -0
- package/lib/assets/health-check.js.map +1 -0
- package/lib/assets/nginx-server.conf +59 -0
- package/lib/assets/nginx.conf +85 -0
- package/lib/assets/nginx.yaml +13 -0
- package/lib/assets/node.sh +25 -0
- package/lib/assets/node.yaml +8 -0
- package/lib/assets/npmrc +1 -0
- package/lib/assets/package.json +7 -0
- package/lib/assets/packages.yaml +5 -0
- package/lib/assets/start.sh +21 -0
- package/lib/aws.js +98 -0
- package/lib/aws.js.map +1 -0
- package/lib/certificates.js +64 -0
- package/lib/certificates.js.map +1 -0
- package/lib/command-handlers.js +774 -0
- package/lib/command-handlers.js.map +1 -0
- package/lib/commands.js +145 -0
- package/lib/commands.js.map +1 -0
- package/lib/download.js +27 -0
- package/lib/download.js.map +1 -0
- package/lib/eb-config.js +269 -0
- package/lib/eb-config.js.map +1 -0
- package/lib/env-ready.js +121 -0
- package/lib/env-ready.js.map +1 -0
- package/lib/env-settings.js +22 -0
- package/lib/env-settings.js.map +1 -0
- package/lib/index.js +111 -0
- package/lib/index.js.map +1 -0
- package/lib/policies.js +144 -0
- package/lib/policies.js.map +1 -0
- package/lib/prepare-bundle.js +245 -0
- package/lib/prepare-bundle.js.map +1 -0
- package/lib/recheck.js +27 -0
- package/lib/recheck.js.map +1 -0
- package/lib/upload.js +75 -0
- package/lib/upload.js.map +1 -0
- package/lib/utils.js +678 -0
- package/lib/utils.js.map +1 -0
- package/lib/validate.js +67 -0
- package/lib/validate.js.map +1 -0
- package/lib/versions.js +116 -0
- package/lib/versions.js.map +1 -0
- package/package.json +65 -0
- package/readme.md +18 -0
package/lib/utils.js
ADDED
|
@@ -0,0 +1,678 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.logStep = logStep;
|
|
7
|
+
exports.shouldRebuild = shouldRebuild;
|
|
8
|
+
exports.tmpBuildPath = tmpBuildPath;
|
|
9
|
+
exports.names = names;
|
|
10
|
+
exports.createUniqueName = createUniqueName;
|
|
11
|
+
exports.getLogs = getLogs;
|
|
12
|
+
exports.getNodeVersion = getNodeVersion;
|
|
13
|
+
exports.selectPlatformArn = selectPlatformArn;
|
|
14
|
+
exports.attachPolicies = attachPolicies;
|
|
15
|
+
exports.getAccountId = getAccountId;
|
|
16
|
+
exports.ensureRoleExists = ensureRoleExists;
|
|
17
|
+
exports.ensureInstanceProfileExists = ensureInstanceProfileExists;
|
|
18
|
+
exports.ensureRoleAdded = ensureRoleAdded;
|
|
19
|
+
exports.ensurePoliciesAttached = ensurePoliciesAttached;
|
|
20
|
+
exports.ensureInlinePolicyAttached = ensureInlinePolicyAttached;
|
|
21
|
+
exports.ensureBucketExists = ensureBucketExists;
|
|
22
|
+
exports.findBucketWithPrefix = findBucketWithPrefix;
|
|
23
|
+
exports.ensureBucketPolicyAttached = ensureBucketPolicyAttached;
|
|
24
|
+
exports.ensureCloudWatchRule = ensureCloudWatchRule;
|
|
25
|
+
exports.ensureRuleTargetExists = ensureRuleTargetExists;
|
|
26
|
+
exports.coloredStatusText = coloredStatusText;
|
|
27
|
+
exports.createVersionDescription = createVersionDescription;
|
|
28
|
+
exports.ensureSsmDocument = ensureSsmDocument;
|
|
29
|
+
exports.pickInstance = pickInstance;
|
|
30
|
+
exports.connectToInstance = connectToInstance;
|
|
31
|
+
exports.executeSSHCommand = executeSSHCommand;
|
|
32
|
+
|
|
33
|
+
var _axios = _interopRequireDefault(require("axios"));
|
|
34
|
+
|
|
35
|
+
var _chalk = _interopRequireDefault(require("chalk"));
|
|
36
|
+
|
|
37
|
+
var _fs = _interopRequireDefault(require("fs"));
|
|
38
|
+
|
|
39
|
+
var _lodash = require("lodash");
|
|
40
|
+
|
|
41
|
+
var _os = _interopRequireDefault(require("os"));
|
|
42
|
+
|
|
43
|
+
var _randomSeed = _interopRequireDefault(require("random-seed"));
|
|
44
|
+
|
|
45
|
+
var _uuid = _interopRequireDefault(require("uuid"));
|
|
46
|
+
|
|
47
|
+
var _child_process = require("child_process");
|
|
48
|
+
|
|
49
|
+
var _aws = require("./aws");
|
|
50
|
+
|
|
51
|
+
var _recheck = require("./recheck");
|
|
52
|
+
|
|
53
|
+
var _envReady = require("./env-ready");
|
|
54
|
+
|
|
55
|
+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
56
|
+
|
|
57
|
+
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) { symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); } keys.push.apply(keys, symbols); } return keys; }
|
|
58
|
+
|
|
59
|
+
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; }
|
|
60
|
+
|
|
61
|
+
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
|
62
|
+
|
|
63
|
+
function logStep(message) {
|
|
64
|
+
console.log(_chalk.default.blue(message));
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function shouldRebuild(bundlePath, useCachedBuild) {
|
|
68
|
+
if (_fs.default.existsSync(bundlePath) && useCachedBuild) {
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return true;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function tmpBuildPath(appPath, api) {
|
|
76
|
+
const rand = _randomSeed.default.create(appPath);
|
|
77
|
+
|
|
78
|
+
const uuidNumbers = [];
|
|
79
|
+
|
|
80
|
+
for (let i = 0; i < 16; i++) {
|
|
81
|
+
uuidNumbers.push(rand(255));
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return api.resolvePath(_os.default.tmpdir(), `mup-meteor-${_uuid.default.v4({
|
|
85
|
+
random: uuidNumbers
|
|
86
|
+
})}`);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function names(config) {
|
|
90
|
+
const name = config.app.name.toLowerCase();
|
|
91
|
+
return {
|
|
92
|
+
bucket: `mup-${name}`,
|
|
93
|
+
environment: config.app.envName || `mup-env-${name}`,
|
|
94
|
+
app: `mup-${name}`,
|
|
95
|
+
bundlePrefix: `mup/bundles/${name}/`,
|
|
96
|
+
instanceProfile: 'aws-elasticbeanstalk-ec2-role',
|
|
97
|
+
serviceRole: 'aws-elasticbeanstalk-service-role',
|
|
98
|
+
trailBucketPrefix: 'mup-graceful-shutdown-trail',
|
|
99
|
+
trailName: 'mup-graceful-shutdown-trail',
|
|
100
|
+
deregisterRuleName: 'mup-target-deregister',
|
|
101
|
+
eventTargetRole: `mup-envoke-run-command-${name}`,
|
|
102
|
+
eventTargetPolicyName: 'Invoke_Run_Command',
|
|
103
|
+
eventTargetPassRoleName: 'Pass_Role',
|
|
104
|
+
automationDocument: 'mup-graceful-shutdown'
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function createUniqueName(prefix = '') {
|
|
109
|
+
const randomNumbers = Math.floor(Math.random() * 10000);
|
|
110
|
+
return `${prefix}-${Date.now()}-${randomNumbers}`;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
async function retrieveEnvironmentInfo(api, count) {
|
|
114
|
+
const config = api.getConfig();
|
|
115
|
+
const {
|
|
116
|
+
environment
|
|
117
|
+
} = names(config);
|
|
118
|
+
const {
|
|
119
|
+
EnvironmentInfo
|
|
120
|
+
} = await _aws.beanstalk.retrieveEnvironmentInfo({
|
|
121
|
+
EnvironmentName: environment,
|
|
122
|
+
InfoType: 'tail'
|
|
123
|
+
}).promise();
|
|
124
|
+
|
|
125
|
+
if (EnvironmentInfo.length > 0) {
|
|
126
|
+
return EnvironmentInfo;
|
|
127
|
+
} else if (count > 5) {
|
|
128
|
+
throw new Error('No logs');
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
return new Promise((resolve, reject) => {
|
|
132
|
+
setTimeout(() => {
|
|
133
|
+
// The logs aren't always available, so retry until they are
|
|
134
|
+
// Another option is to look for the event that says it is ready
|
|
135
|
+
retrieveEnvironmentInfo(api, count + 1).then(resolve).catch(reject);
|
|
136
|
+
}, (0, _recheck.getRecheckInterval)());
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
async function getLogs(api, logNames) {
|
|
141
|
+
const config = api.getConfig();
|
|
142
|
+
const {
|
|
143
|
+
environment
|
|
144
|
+
} = names(config);
|
|
145
|
+
await (0, _envReady.waitForEnvReady)(config, false);
|
|
146
|
+
logStep('=> Requesting Logs');
|
|
147
|
+
await _aws.beanstalk.requestEnvironmentInfo({
|
|
148
|
+
EnvironmentName: environment,
|
|
149
|
+
InfoType: 'tail'
|
|
150
|
+
}).promise();
|
|
151
|
+
const EnvironmentInfo = await retrieveEnvironmentInfo(api, 0);
|
|
152
|
+
logStep('=> Downloading Logs');
|
|
153
|
+
const logsForServer = EnvironmentInfo.reduce((result, info) => {
|
|
154
|
+
result[info.Ec2InstanceId] = info.Message;
|
|
155
|
+
return result;
|
|
156
|
+
}, {});
|
|
157
|
+
return Promise.all(Object.keys(logsForServer).map(key => new Promise((resolve, reject) => {
|
|
158
|
+
_axios.default.get(logsForServer[key]).then(({
|
|
159
|
+
data
|
|
160
|
+
}) => {
|
|
161
|
+
// The separator changed with Amazon Linux 2
|
|
162
|
+
let parts = data.split('----------------------------------------\n/var/log/');
|
|
163
|
+
|
|
164
|
+
if (parts.length === 1) {
|
|
165
|
+
parts = data.split('-------------------------------------\n/var/log/');
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
data = logNames.map(name => parts.find(part => part.trim().startsWith(name)));
|
|
169
|
+
resolve({
|
|
170
|
+
data,
|
|
171
|
+
instance: key
|
|
172
|
+
});
|
|
173
|
+
}).catch(reject);
|
|
174
|
+
})));
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function getNodeVersion(api, bundlePath) {
|
|
178
|
+
let star = _fs.default.readFileSync(api.resolvePath(bundlePath, 'bundle/star.json')).toString();
|
|
179
|
+
|
|
180
|
+
const nodeVersionTxt = _fs.default.readFileSync(api.resolvePath(bundlePath, 'bundle/.node_version.txt')).toString();
|
|
181
|
+
|
|
182
|
+
star = JSON.parse(star);
|
|
183
|
+
|
|
184
|
+
if (star.npmVersion) {
|
|
185
|
+
return {
|
|
186
|
+
nodeVersion: star.nodeVersion,
|
|
187
|
+
npmVersion: star.npmVersion
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
const nodeVersion = nodeVersionTxt.substr(1);
|
|
192
|
+
|
|
193
|
+
if (nodeVersion.startsWith('4')) {
|
|
194
|
+
return {
|
|
195
|
+
nodeVersion,
|
|
196
|
+
npmVersion: '4.6.1'
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
return {
|
|
201
|
+
nodeVersion,
|
|
202
|
+
npmVersion: '3.10.5'
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
async function selectPlatformArn() {
|
|
207
|
+
const {
|
|
208
|
+
PlatformBranchSummaryList
|
|
209
|
+
} = await _aws.beanstalk.listPlatformBranches({
|
|
210
|
+
Filters: [{
|
|
211
|
+
Attribute: 'LifecycleState',
|
|
212
|
+
Operator: '=',
|
|
213
|
+
Values: ['supported']
|
|
214
|
+
}, {
|
|
215
|
+
Attribute: 'PlatformName',
|
|
216
|
+
Operator: '=',
|
|
217
|
+
Values: ['Node.js']
|
|
218
|
+
}, {
|
|
219
|
+
Attribute: 'TierType',
|
|
220
|
+
Operator: '=',
|
|
221
|
+
Values: ['WebServer/Standard']
|
|
222
|
+
}]
|
|
223
|
+
}).promise();
|
|
224
|
+
|
|
225
|
+
if (PlatformBranchSummaryList.length === 0) {
|
|
226
|
+
throw new Error('Unable to find supported Node.js platform');
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
const branchName = PlatformBranchSummaryList[0].BranchName;
|
|
230
|
+
const {
|
|
231
|
+
PlatformSummaryList
|
|
232
|
+
} = await _aws.beanstalk.listPlatformVersions({
|
|
233
|
+
Filters: [{
|
|
234
|
+
Type: 'PlatformBranchName',
|
|
235
|
+
Operator: '=',
|
|
236
|
+
Values: [branchName]
|
|
237
|
+
}, {
|
|
238
|
+
Type: 'PlatformStatus',
|
|
239
|
+
Operator: '=',
|
|
240
|
+
Values: ['Ready']
|
|
241
|
+
}]
|
|
242
|
+
}).promise();
|
|
243
|
+
const arn = PlatformSummaryList[0].PlatformArn;
|
|
244
|
+
return arn;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
async function attachPolicies(config, roleName, policies) {
|
|
248
|
+
const promises = [];
|
|
249
|
+
policies.forEach(policy => {
|
|
250
|
+
const promise = _aws.iam.attachRolePolicy({
|
|
251
|
+
RoleName: roleName,
|
|
252
|
+
PolicyArn: policy
|
|
253
|
+
}).promise();
|
|
254
|
+
|
|
255
|
+
promises.push(promise);
|
|
256
|
+
});
|
|
257
|
+
await Promise.all(promises);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
function getAccountId() {
|
|
261
|
+
return _aws.sts.getCallerIdentity().promise().then(({
|
|
262
|
+
Account
|
|
263
|
+
}) => Account);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
async function ensureRoleExists(name, assumeRolePolicyDocument, ensureAssumeRolePolicy) {
|
|
267
|
+
let exists = true;
|
|
268
|
+
let updateAssumeRolePolicy = false;
|
|
269
|
+
|
|
270
|
+
try {
|
|
271
|
+
const {
|
|
272
|
+
Role
|
|
273
|
+
} = await _aws.iam.getRole({
|
|
274
|
+
RoleName: name
|
|
275
|
+
}).promise();
|
|
276
|
+
const currentAssumeRolePolicy = decodeURIComponent(Role.AssumeRolePolicyDocument); // Make the whitespace consistent with the current document
|
|
277
|
+
|
|
278
|
+
assumeRolePolicyDocument = JSON.stringify(JSON.parse(assumeRolePolicyDocument));
|
|
279
|
+
|
|
280
|
+
if (currentAssumeRolePolicy !== assumeRolePolicyDocument && ensureAssumeRolePolicy) {
|
|
281
|
+
updateAssumeRolePolicy = true;
|
|
282
|
+
}
|
|
283
|
+
} catch (e) {
|
|
284
|
+
exists = false;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
if (!exists) {
|
|
288
|
+
await _aws.iam.createRole({
|
|
289
|
+
RoleName: name,
|
|
290
|
+
AssumeRolePolicyDocument: assumeRolePolicyDocument
|
|
291
|
+
}).promise();
|
|
292
|
+
} else if (updateAssumeRolePolicy) {
|
|
293
|
+
await _aws.iam.updateAssumeRolePolicy({
|
|
294
|
+
RoleName: name,
|
|
295
|
+
PolicyDocument: assumeRolePolicyDocument
|
|
296
|
+
}).promise();
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
async function ensureInstanceProfileExists(config, name) {
|
|
301
|
+
let exists = true;
|
|
302
|
+
|
|
303
|
+
try {
|
|
304
|
+
await _aws.iam.getInstanceProfile({
|
|
305
|
+
InstanceProfileName: name
|
|
306
|
+
}).promise();
|
|
307
|
+
} catch (e) {
|
|
308
|
+
exists = false;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
if (!exists) {
|
|
312
|
+
await _aws.iam.createInstanceProfile({
|
|
313
|
+
InstanceProfileName: name
|
|
314
|
+
}).promise();
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
async function ensureRoleAdded(config, instanceProfile, role) {
|
|
319
|
+
let added = true;
|
|
320
|
+
const {
|
|
321
|
+
InstanceProfile
|
|
322
|
+
} = await _aws.iam.getInstanceProfile({
|
|
323
|
+
InstanceProfileName: instanceProfile
|
|
324
|
+
}).promise();
|
|
325
|
+
|
|
326
|
+
if (InstanceProfile.Roles.length === 0 || InstanceProfile.Roles[0].RoleName !== role) {
|
|
327
|
+
added = false;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
if (!added) {
|
|
331
|
+
await _aws.iam.addRoleToInstanceProfile({
|
|
332
|
+
InstanceProfileName: instanceProfile,
|
|
333
|
+
RoleName: role
|
|
334
|
+
}).promise();
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
async function ensurePoliciesAttached(config, role, policies) {
|
|
339
|
+
let {
|
|
340
|
+
AttachedPolicies
|
|
341
|
+
} = await _aws.iam.listAttachedRolePolicies({
|
|
342
|
+
RoleName: role
|
|
343
|
+
}).promise();
|
|
344
|
+
AttachedPolicies = AttachedPolicies.map(policy => policy.PolicyArn);
|
|
345
|
+
const unattachedPolicies = policies.reduce((result, policy) => {
|
|
346
|
+
if (AttachedPolicies.indexOf(policy) === -1) {
|
|
347
|
+
result.push(policy);
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
return result;
|
|
351
|
+
}, []);
|
|
352
|
+
|
|
353
|
+
if (unattachedPolicies.length > 0) {
|
|
354
|
+
await attachPolicies(config, role, unattachedPolicies);
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
async function ensureInlinePolicyAttached(role, policyName, policyDocument) {
|
|
359
|
+
let exists = true;
|
|
360
|
+
let needsUpdating = false;
|
|
361
|
+
|
|
362
|
+
try {
|
|
363
|
+
const result = await _aws.iam.getRolePolicy({
|
|
364
|
+
RoleName: role,
|
|
365
|
+
PolicyName: policyName
|
|
366
|
+
}).promise();
|
|
367
|
+
const currentPolicyDocument = decodeURIComponent(result.PolicyDocument);
|
|
368
|
+
|
|
369
|
+
if (currentPolicyDocument !== policyDocument) {
|
|
370
|
+
needsUpdating = true;
|
|
371
|
+
}
|
|
372
|
+
} catch (e) {
|
|
373
|
+
exists = false;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
if (!exists || needsUpdating) {
|
|
377
|
+
await _aws.iam.putRolePolicy({
|
|
378
|
+
RoleName: role,
|
|
379
|
+
PolicyName: policyName,
|
|
380
|
+
PolicyDocument: policyDocument
|
|
381
|
+
}).promise();
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
async function ensureBucketExists(buckets, bucketName, region) {
|
|
386
|
+
if (!buckets.find(bucket => bucket.Name === bucketName)) {
|
|
387
|
+
await _aws.s3.createBucket(_objectSpread({
|
|
388
|
+
Bucket: bucketName
|
|
389
|
+
}, region ? {
|
|
390
|
+
CreateBucketConfiguration: {
|
|
391
|
+
LocationConstraint: region
|
|
392
|
+
}
|
|
393
|
+
} : {})).promise();
|
|
394
|
+
return true;
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
function findBucketWithPrefix(buckets, prefix) {
|
|
399
|
+
return buckets.find(bucket => bucket.Name.indexOf(prefix) === 0);
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
async function ensureBucketPolicyAttached(bucketName, policy) {
|
|
403
|
+
let error = false;
|
|
404
|
+
let currentPolicy;
|
|
405
|
+
|
|
406
|
+
try {
|
|
407
|
+
const {
|
|
408
|
+
Policy
|
|
409
|
+
} = await _aws.s3.getBucketPolicy({
|
|
410
|
+
Bucket: bucketName
|
|
411
|
+
}).promise();
|
|
412
|
+
currentPolicy = Policy;
|
|
413
|
+
} catch (e) {
|
|
414
|
+
error = true;
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
if (error || currentPolicy !== policy) {
|
|
418
|
+
const params = {
|
|
419
|
+
Bucket: bucketName,
|
|
420
|
+
Policy: policy
|
|
421
|
+
};
|
|
422
|
+
await _aws.s3.putBucketPolicy(params).promise();
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
async function ensureCloudWatchRule(name, description, eventPattern) {
|
|
427
|
+
let error = false;
|
|
428
|
+
|
|
429
|
+
try {
|
|
430
|
+
await _aws.cloudWatchEvents.describeRule({
|
|
431
|
+
Name: name
|
|
432
|
+
}).promise();
|
|
433
|
+
} catch (e) {
|
|
434
|
+
error = true;
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
if (error) {
|
|
438
|
+
await _aws.cloudWatchEvents.putRule({
|
|
439
|
+
Name: name,
|
|
440
|
+
Description: description,
|
|
441
|
+
EventPattern: eventPattern
|
|
442
|
+
}).promise();
|
|
443
|
+
return true;
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
return false;
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
async function ensureRuleTargetExists(ruleName, target) {
|
|
450
|
+
const {
|
|
451
|
+
Targets
|
|
452
|
+
} = await _aws.cloudWatchEvents.listTargetsByRule({
|
|
453
|
+
Rule: ruleName
|
|
454
|
+
}).promise();
|
|
455
|
+
|
|
456
|
+
if (!Targets.find(_target => (0, _lodash.isEqual)(_target, target))) {
|
|
457
|
+
const params = {
|
|
458
|
+
Rule: ruleName,
|
|
459
|
+
Targets: [target]
|
|
460
|
+
};
|
|
461
|
+
await _aws.cloudWatchEvents.putTargets(params).promise();
|
|
462
|
+
return true;
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
function coloredStatusText(envColor, text) {
|
|
467
|
+
if (envColor === 'Green') {
|
|
468
|
+
return _chalk.default.green(text);
|
|
469
|
+
} else if (envColor === 'Yellow') {
|
|
470
|
+
return _chalk.default.yellow(text);
|
|
471
|
+
} else if (envColor === 'Red') {
|
|
472
|
+
return _chalk.default.red(text);
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
return text;
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
function createVersionDescription(api, appConfig) {
|
|
479
|
+
const appPath = api.resolvePath(api.getBasePath(), appConfig.path);
|
|
480
|
+
let description = '';
|
|
481
|
+
|
|
482
|
+
try {
|
|
483
|
+
description = (0, _child_process.execSync)('git log -1 --pretty=%B', {
|
|
484
|
+
cwd: appPath,
|
|
485
|
+
stdio: 'pipe'
|
|
486
|
+
}).toString();
|
|
487
|
+
} catch (e) {
|
|
488
|
+
description = `Deployed by Mup on ${new Date().toUTCString()}`;
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
return description.split('\n')[0].slice(0, 195);
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
async function ensureSsmDocument(name, content) {
|
|
495
|
+
let exists = true;
|
|
496
|
+
let needsUpdating = false;
|
|
497
|
+
|
|
498
|
+
try {
|
|
499
|
+
const result = await _aws.ssm.getDocument({
|
|
500
|
+
Name: name,
|
|
501
|
+
DocumentVersion: '$DEFAULT'
|
|
502
|
+
}).promise(); // If the document was created or edited on the AWS console, there is extra new
|
|
503
|
+
// line characters and whitespace
|
|
504
|
+
|
|
505
|
+
const currentContent = JSON.stringify(JSON.parse(result.Content.replace(/\r?\n|\r/g, '')));
|
|
506
|
+
|
|
507
|
+
if (currentContent !== content) {
|
|
508
|
+
needsUpdating = true;
|
|
509
|
+
}
|
|
510
|
+
} catch (e) {
|
|
511
|
+
exists = false;
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
if (!exists) {
|
|
515
|
+
await _aws.ssm.createDocument({
|
|
516
|
+
Content: content,
|
|
517
|
+
Name: name,
|
|
518
|
+
DocumentType: 'Automation'
|
|
519
|
+
}).promise();
|
|
520
|
+
return true;
|
|
521
|
+
} else if (needsUpdating) {
|
|
522
|
+
try {
|
|
523
|
+
await _aws.ssm.updateDocument({
|
|
524
|
+
Content: content,
|
|
525
|
+
Name: name,
|
|
526
|
+
DocumentVersion: '$LATEST'
|
|
527
|
+
}).promise();
|
|
528
|
+
} catch (e) {
|
|
529
|
+
// If the latest document version has the correct content
|
|
530
|
+
// then it must not be the default version. Ignore the error
|
|
531
|
+
// so we can fix the default version
|
|
532
|
+
if (e.code !== 'DuplicateDocumentContent') {
|
|
533
|
+
throw e;
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
const result = await _aws.ssm.getDocument({
|
|
538
|
+
Name: name,
|
|
539
|
+
DocumentVersion: '$LATEST'
|
|
540
|
+
}).promise();
|
|
541
|
+
await _aws.ssm.updateDocumentDefaultVersion({
|
|
542
|
+
DocumentVersion: result.DocumentVersion,
|
|
543
|
+
Name: name
|
|
544
|
+
}).promise();
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
async function pickInstance(config, instance) {
|
|
549
|
+
const {
|
|
550
|
+
environment
|
|
551
|
+
} = names(config);
|
|
552
|
+
const {
|
|
553
|
+
EnvironmentResources
|
|
554
|
+
} = await _aws.beanstalk.describeEnvironmentResources({
|
|
555
|
+
EnvironmentName: environment
|
|
556
|
+
}).promise();
|
|
557
|
+
const instanceIds = EnvironmentResources.Instances.map(({
|
|
558
|
+
Id
|
|
559
|
+
}) => Id);
|
|
560
|
+
const description = ['Available instances', ...instanceIds.map(id => ` - ${id}`)].join('\n');
|
|
561
|
+
return {
|
|
562
|
+
selected: instanceIds.includes(instance) ? instance : null,
|
|
563
|
+
description
|
|
564
|
+
};
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
async function connectToInstance(api, instanceId, commandLabel) {
|
|
568
|
+
const {
|
|
569
|
+
sshKey
|
|
570
|
+
} = api.getConfig().app;
|
|
571
|
+
|
|
572
|
+
if (!sshKey) {
|
|
573
|
+
const error = new Error('missing sshKey config');
|
|
574
|
+
error.solution = 'Learn how to configure sshKey at https://github.com/zodern/mup-aws-beanstalk/blob/master/docs/index.md#meteor-shell-and-debug';
|
|
575
|
+
throw error;
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
const {
|
|
579
|
+
Reservations
|
|
580
|
+
} = await _aws.ec2.describeInstances({
|
|
581
|
+
InstanceIds: [instanceId]
|
|
582
|
+
}).promise();
|
|
583
|
+
const instance = Reservations[0].Instances[0];
|
|
584
|
+
const availabilityZone = instance.Placement.AvailabilityZone;
|
|
585
|
+
const securityGroups = instance.SecurityGroups.map(g => g.GroupId);
|
|
586
|
+
let {
|
|
587
|
+
data: ipAddress
|
|
588
|
+
} = await _axios.default.get('https://ipv4.icanhazip.com');
|
|
589
|
+
ipAddress = ipAddress.trim();
|
|
590
|
+
|
|
591
|
+
if (securityGroups.length > 1) {
|
|
592
|
+
console.warn('Instance has more than one security group. Please open a GitHub issue for mup-aws-beanstalk');
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
let ruleIds = [];
|
|
596
|
+
|
|
597
|
+
try {
|
|
598
|
+
const {
|
|
599
|
+
SecurityGroupRules
|
|
600
|
+
} = await _aws.ec2.authorizeSecurityGroupIngress({
|
|
601
|
+
GroupId: securityGroups[0],
|
|
602
|
+
IpPermissions: [{
|
|
603
|
+
FromPort: 22,
|
|
604
|
+
IpProtocol: 'tcp',
|
|
605
|
+
IpRanges: [{
|
|
606
|
+
CidrIp: `${ipAddress}/32`,
|
|
607
|
+
Description: `Temporary SSH access for ${commandLabel}`
|
|
608
|
+
}],
|
|
609
|
+
ToPort: 22
|
|
610
|
+
}]
|
|
611
|
+
}).promise();
|
|
612
|
+
ruleIds = SecurityGroupRules.map(rule => rule.SecurityGroupRuleId);
|
|
613
|
+
} catch (e) {
|
|
614
|
+
if (e.code === 'InvalidPermission.Duplicate') {// This rule already exists
|
|
615
|
+
// TODO: should we find the rule id so we can remove it, or leave it in
|
|
616
|
+
// case the user had manually added this rule?
|
|
617
|
+
} else {
|
|
618
|
+
throw e;
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
await _aws.ec2InstanceConnect.sendSSHPublicKey({
|
|
623
|
+
InstanceId: instanceId,
|
|
624
|
+
AvailabilityZone: availabilityZone,
|
|
625
|
+
InstanceOSUser: 'ec2-user',
|
|
626
|
+
SSHPublicKey: _fs.default.readFileSync(api.resolvePath(sshKey.publicKey), 'utf-8')
|
|
627
|
+
}).promise();
|
|
628
|
+
const sshOptions = {
|
|
629
|
+
host: instance.PublicDnsName,
|
|
630
|
+
port: 22,
|
|
631
|
+
username: 'ec2-user',
|
|
632
|
+
privateKey: _fs.default.readFileSync(api.resolvePath(sshKey.privateKey), 'utf-8')
|
|
633
|
+
};
|
|
634
|
+
return {
|
|
635
|
+
sshOptions,
|
|
636
|
+
|
|
637
|
+
removeSSHAccess() {
|
|
638
|
+
if (ruleIds.length === 0) {
|
|
639
|
+
return;
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
console.log('Removing temporary security group rule for SSH');
|
|
643
|
+
return _aws.ec2.revokeSecurityGroupIngress({
|
|
644
|
+
GroupId: securityGroups[0],
|
|
645
|
+
SecurityGroupRuleIds: ruleIds
|
|
646
|
+
}).promise();
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
};
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
async function executeSSHCommand(conn, command) {
|
|
653
|
+
return new Promise((resolve, reject) => {
|
|
654
|
+
conn.exec(command, (err, outputStream) => {
|
|
655
|
+
if (err) {
|
|
656
|
+
conn.end();
|
|
657
|
+
reject(err);
|
|
658
|
+
return;
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
let output = '';
|
|
662
|
+
outputStream.on('data', data => {
|
|
663
|
+
output += data;
|
|
664
|
+
});
|
|
665
|
+
outputStream.stderr.on('data', data => {
|
|
666
|
+
output += data;
|
|
667
|
+
});
|
|
668
|
+
outputStream.once('close', code => {
|
|
669
|
+
conn.end();
|
|
670
|
+
resolve({
|
|
671
|
+
code,
|
|
672
|
+
output
|
|
673
|
+
});
|
|
674
|
+
});
|
|
675
|
+
});
|
|
676
|
+
});
|
|
677
|
+
}
|
|
678
|
+
//# sourceMappingURL=utils.js.map
|