@fishawack/lab-env 5.7.0-beta.2 → 5.7.0-beta.3

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.
Files changed (36) hide show
  1. package/CHANGELOG.md +42 -0
  2. package/_Test/provision.js +1 -8
  3. package/_Test/prune.js +8 -5
  4. package/bitbucket-pipelines.yml +2 -2
  5. package/cli.js +1 -0
  6. package/commands/create/cmds/dekey.js +13 -9
  7. package/commands/create/cmds/deprovision.js +29 -0
  8. package/commands/create/cmds/key.js +60 -41
  9. package/commands/create/cmds/provision.js +425 -55
  10. package/commands/create/cmds/rekey.js +218 -0
  11. package/commands/create/libs/output-credentials.js +59 -0
  12. package/commands/create/libs/parallel-runner.js +204 -0
  13. package/commands/create/libs/resolve-operator.js +59 -0
  14. package/commands/create/libs/utilities.js +71 -8
  15. package/commands/create/libs/vars.js +70 -86
  16. package/commands/create/services/aws/acm.js +4 -2
  17. package/commands/create/services/aws/cloudfront.js +51 -45
  18. package/commands/create/services/aws/ec2.js +132 -53
  19. package/commands/create/services/aws/elasticache.js +159 -0
  20. package/commands/create/services/aws/elasticbeanstalk.js +141 -60
  21. package/commands/create/services/aws/iam.js +159 -93
  22. package/commands/create/services/aws/index.js +1209 -466
  23. package/commands/create/services/aws/opensearch.js +25 -17
  24. package/commands/create/services/aws/rds.js +22 -34
  25. package/commands/create/services/aws/s3.js +14 -27
  26. package/commands/create/services/aws/secretsmanager.js +8 -15
  27. package/commands/create/services/aws/ses.js +195 -0
  28. package/commands/create/templates/elasticbeanstalk/.ebextensions/laravel/cron.config +45 -0
  29. package/commands/create/templates/elasticbeanstalk/.ebextensions/laravel/queue-worker.config +29 -0
  30. package/commands/create/templates/elasticbeanstalk/.ebextensions/laravel/software.config +12 -0
  31. package/commands/create/templates/elasticbeanstalk/.ebextensions/misc/setvars.config +2 -16
  32. package/commands/create/templates/elasticbeanstalk/.platform/confighooks/postdeploy/rewrite-flush.sh +1 -1
  33. package/commands/create/templates/elasticbeanstalk/.platform/confighooks/postdeploy/setvars.sh +19 -0
  34. package/commands/create/templates/elasticbeanstalk/.platform/hooks/postdeploy/restart_queue.sh +4 -0
  35. package/commands/create/templates/elasticbeanstalk/.platform/hooks/prebuild/setvars.sh +19 -0
  36. package/package.json +5 -2
@@ -1,5 +1,7 @@
1
1
  const fs = require("fs-extra");
2
+ const crypto = require("crypto");
2
3
  const glob = require("glob");
4
+ const inquirer = require("inquirer");
3
5
  const {
4
6
  ElasticBeanstalkClient,
5
7
  CreateApplicationCommand,
@@ -14,19 +16,26 @@ const {
14
16
  UpdateEnvironmentCommand,
15
17
  CreateApplicationVersionCommand,
16
18
  ListAvailableSolutionStacksCommand,
19
+ ListTagsForResourceCommand,
17
20
  RestartAppServerCommand,
18
21
  } = require("@aws-sdk/client-elastic-beanstalk");
19
22
  const { Spinner, poll } = require("../../libs/utilities");
20
- const { eb } = require("../../libs/vars");
23
+ const { eb, secretGroups, awsClientConfig } = require("../../libs/vars");
24
+ const { template } = require("lodash");
21
25
 
22
- module.exports.createElasticBeanstalkApplication = async (name) => {
23
- const client = new ElasticBeanstalkClient({});
26
+ const getClient = () => new ElasticBeanstalkClient(awsClientConfig);
24
27
 
28
+ module.exports.createElasticBeanstalkApplication = async (name, tags = []) => {
25
29
  let res = await Spinner.prototype.simple(
26
30
  `Creating elasticbeanstalk application ${name}`,
27
31
  () => {
28
- return client.send(
29
- new CreateApplicationCommand({ ApplicationName: name }),
32
+ return getClient().send(
33
+ new CreateApplicationCommand({
34
+ ApplicationName: name,
35
+ Tags: [
36
+ { Key: "client", Value: process.env.AWS_PROFILE },
37
+ ].concat(tags),
38
+ }),
30
39
  );
31
40
  },
32
41
  );
@@ -35,15 +44,36 @@ module.exports.createElasticBeanstalkApplication = async (name) => {
35
44
  };
36
45
 
37
46
  module.exports.describeEnvironment = async (name) => {
38
- const client = new ElasticBeanstalkClient({});
39
-
40
- return client.send(
47
+ return getClient().send(
41
48
  new DescribeEnvironmentsCommand({
42
49
  EnvironmentNames: [name],
43
50
  }),
44
51
  );
45
52
  };
46
53
 
54
+ module.exports.listEnvironments = async (applicationName) => {
55
+ const res = await Spinner.prototype.simple(
56
+ `Retrieving environments for ${applicationName}`,
57
+ () => {
58
+ return getClient().send(
59
+ new DescribeEnvironmentsCommand({
60
+ ApplicationName: applicationName,
61
+ IncludeDeleted: false,
62
+ }),
63
+ );
64
+ },
65
+ );
66
+
67
+ return (res.Environments || []).filter((e) => e.Status !== "Terminated");
68
+ };
69
+
70
+ module.exports.getEnvironmentTags = async (environmentArn) => {
71
+ const res = await getClient().send(
72
+ new ListTagsForResourceCommand({ ResourceArn: environmentArn }),
73
+ );
74
+ return res.ResourceTags || [];
75
+ };
76
+
47
77
  module.exports.createElasticBeanstalkEnvironment = async (
48
78
  name,
49
79
  { language },
@@ -51,13 +81,12 @@ module.exports.createElasticBeanstalkEnvironment = async (
51
81
  OptionSettings,
52
82
  CNAMEPrefix,
53
83
  tags = [],
84
+ versionLabel,
54
85
  ) => {
55
- const client = new ElasticBeanstalkClient({});
56
-
57
86
  const solutions = await Spinner.prototype.simple(
58
87
  `Retrieving available solution stacks`,
59
88
  () => {
60
- return client.send(new ListAvailableSolutionStacksCommand());
89
+ return getClient().send(new ListAvailableSolutionStacksCommand());
61
90
  },
62
91
  );
63
92
 
@@ -75,21 +104,23 @@ module.exports.createElasticBeanstalkEnvironment = async (
75
104
  d.includes(stackName),
76
105
  )[0];
77
106
 
107
+ const createParams = {
108
+ ApplicationName,
109
+ EnvironmentName: name,
110
+ SolutionStackName,
111
+ OptionSettings,
112
+ CNAMEPrefix,
113
+ Tags: [{ Key: "client", Value: process.env.AWS_PROFILE }].concat(tags),
114
+ };
115
+
116
+ if (versionLabel) {
117
+ createParams.VersionLabel = versionLabel;
118
+ }
119
+
78
120
  await Spinner.prototype.simple(
79
121
  `Creating elasticbeanstalk environment ${name}`,
80
122
  () => {
81
- return client.send(
82
- new CreateEnvironmentCommand({
83
- ApplicationName,
84
- EnvironmentName: name,
85
- SolutionStackName,
86
- OptionSettings,
87
- CNAMEPrefix,
88
- Tags: [
89
- { Key: "client", Value: process.env.AWS_PROFILE },
90
- ].concat(tags),
91
- }),
92
- );
123
+ return getClient().send(new CreateEnvironmentCommand(createParams));
93
124
  },
94
125
  );
95
126
 
@@ -112,12 +143,10 @@ module.exports.waitForElasticBeanstalkEnvironment = async (
112
143
  waitFor = "Ready",
113
144
  failIf,
114
145
  ) => {
115
- const client = new ElasticBeanstalkClient({});
116
-
117
146
  const res = await poll(
118
147
  async () =>
119
148
  (
120
- await client.send(
149
+ await getClient().send(
121
150
  new DescribeEnvironmentsCommand({
122
151
  EnvironmentNames: [name],
123
152
  }),
@@ -131,12 +160,10 @@ module.exports.waitForElasticBeanstalkEnvironment = async (
131
160
  };
132
161
 
133
162
  module.exports.removeElasticBeanstalkApplication = async (name) => {
134
- const client = new ElasticBeanstalkClient({});
135
-
136
163
  await Spinner.prototype.simple(
137
164
  `Removing elasticbeanstalk application ${name}`,
138
165
  () => {
139
- return client.send(
166
+ return getClient().send(
140
167
  new DeleteApplicationCommand({ ApplicationName: name }),
141
168
  );
142
169
  },
@@ -144,12 +171,10 @@ module.exports.removeElasticBeanstalkApplication = async (name) => {
144
171
  };
145
172
 
146
173
  module.exports.removeElasticBeanstalkEnvironment = async (name) => {
147
- const client = new ElasticBeanstalkClient({});
148
-
149
174
  await Spinner.prototype.simple(
150
175
  `Removing elasticbeanstalk environment ${name}`,
151
176
  () => {
152
- return client.send(
177
+ return getClient().send(
153
178
  new TerminateEnvironmentCommand({ EnvironmentName: name }),
154
179
  );
155
180
  },
@@ -166,25 +191,93 @@ module.exports.removeElasticBeanstalkEnvironment = async (name) => {
166
191
  );
167
192
  };
168
193
 
169
- module.exports.copyElasticBeanstalkConfigs = async (configurations) => {
194
+ module.exports.copyElasticBeanstalkConfigs = async (configurations, branch) => {
170
195
  const templates = `${__dirname}/../../templates/elasticbeanstalk`;
196
+ const templateData = {
197
+ ...process.env,
198
+ SECRET_GROUPS: secretGroups.join(" "),
199
+ };
200
+
201
+ const checksumPath = `_IaC/${branch}/.lab-env-checksums.json`;
202
+ const checksums = fs.existsSync(checksumPath)
203
+ ? JSON.parse(fs.readFileSync(checksumPath, "utf8"))
204
+ : {};
205
+
206
+ for (const globbed of configurations) {
207
+ const files = glob.sync(globbed, { cwd: `${templates}` });
208
+
209
+ for (const config of files) {
210
+ const content = fs.readFileSync(`${templates}/${config}`, "utf8");
211
+ const output = content.includes("<%=")
212
+ ? template(content, { interpolate: /<%=([\s\S]+?)%>/g })(
213
+ templateData,
214
+ )
215
+ : content;
216
+
217
+ const dest = `_IaC/${branch}/${config}`;
218
+ const newHash = crypto
219
+ .createHash("md5")
220
+ .update(output)
221
+ .digest("hex");
222
+
223
+ if (fs.existsSync(dest)) {
224
+ const existing = fs.readFileSync(dest, "utf8");
225
+
226
+ if (existing === output) {
227
+ checksums[config] = newHash;
228
+ continue;
229
+ }
230
+
231
+ const existingHash = crypto
232
+ .createHash("md5")
233
+ .update(existing)
234
+ .digest("hex");
235
+
236
+ let shouldOverwrite;
237
+
238
+ if (checksums[config] && existingHash === checksums[config]) {
239
+ const { overwrite } = await inquirer.prompt([
240
+ {
241
+ type: "confirm",
242
+ name: "overwrite",
243
+ message: `${dest} has an updated template, overwrite?`,
244
+ default: true,
245
+ },
246
+ ]);
247
+ shouldOverwrite = overwrite;
248
+ } else {
249
+ const { overwrite } = await inquirer.prompt([
250
+ {
251
+ type: "confirm",
252
+ name: "overwrite",
253
+ message: `${dest} has been modified, overwrite?`,
254
+ default: false,
255
+ },
256
+ ]);
257
+ shouldOverwrite = overwrite;
258
+ }
259
+
260
+ if (!shouldOverwrite) continue;
261
+ }
262
+
263
+ fs.outputFileSync(dest, output);
264
+ checksums[config] = newHash;
265
+ }
266
+ }
171
267
 
172
- configurations.forEach((globbed) => {
173
- glob.sync(globbed, { cwd: `${templates}` }).forEach((config) =>
174
- fs.copySync(`${templates}/${config}`, config),
175
- );
176
- });
268
+ fs.outputFileSync(checksumPath, JSON.stringify(checksums, null, 2));
177
269
 
178
- fs.outputFileSync(`.elasticbeanstalk/config.yml`, eb.config());
270
+ fs.outputFileSync(
271
+ `_IaC/${branch}/.elasticbeanstalk/config.yml`,
272
+ eb.config(),
273
+ );
179
274
  };
180
275
 
181
276
  module.exports.listApplications = async () => {
182
- const client = new ElasticBeanstalkClient({});
183
-
184
277
  const res = await Spinner.prototype.simple(
185
278
  `Retrieving elasticbeanstalk applications`,
186
279
  () => {
187
- return client.send(new DescribeApplicationsCommand({}));
280
+ return getClient().send(new DescribeApplicationsCommand({}));
188
281
  },
189
282
  );
190
283
 
@@ -192,13 +285,11 @@ module.exports.listApplications = async () => {
192
285
  };
193
286
 
194
287
  module.exports.listApplicationVersions = async (applicationName) => {
195
- const client = new ElasticBeanstalkClient({});
196
-
197
288
  let versions = [];
198
289
  let nextToken;
199
290
 
200
291
  do {
201
- const res = await client.send(
292
+ const res = await getClient().send(
202
293
  new DescribeApplicationVersionsCommand({
203
294
  ApplicationName: applicationName,
204
295
  ...(nextToken ? { NextToken: nextToken } : {}),
@@ -216,9 +307,7 @@ module.exports.createApplicationVersion = async (
216
307
  applicationName,
217
308
  versionLabel,
218
309
  ) => {
219
- const client = new ElasticBeanstalkClient({});
220
-
221
- const res = await client.send(
310
+ const res = await getClient().send(
222
311
  new CreateApplicationVersionCommand({
223
312
  ApplicationName: applicationName,
224
313
  VersionLabel: versionLabel,
@@ -233,9 +322,7 @@ module.exports.deleteApplicationVersion = async (
233
322
  applicationName,
234
323
  versionLabel,
235
324
  ) => {
236
- const client = new ElasticBeanstalkClient({});
237
-
238
- await client.send(
325
+ await getClient().send(
239
326
  new DeleteApplicationVersionCommand({
240
327
  ApplicationName: applicationName,
241
328
  VersionLabel: versionLabel,
@@ -248,12 +335,10 @@ module.exports.describeConfigurationSettings = async (
248
335
  applicationName,
249
336
  environmentName,
250
337
  ) => {
251
- const client = new ElasticBeanstalkClient({});
252
-
253
338
  const res = await Spinner.prototype.simple(
254
339
  `Retrieving configuration for ${environmentName}`,
255
340
  () => {
256
- return client.send(
341
+ return getClient().send(
257
342
  new DescribeConfigurationSettingsCommand({
258
343
  ApplicationName: applicationName,
259
344
  EnvironmentName: environmentName,
@@ -270,12 +355,10 @@ module.exports.updateEnvironmentSettings = async (
270
355
  optionSettings = [],
271
356
  optionsToRemove = [],
272
357
  ) => {
273
- const client = new ElasticBeanstalkClient({});
274
-
275
358
  await Spinner.prototype.simple(
276
359
  `Updating environment ${environmentName}`,
277
360
  () => {
278
- return client.send(
361
+ return getClient().send(
279
362
  new UpdateEnvironmentCommand({
280
363
  EnvironmentName: environmentName,
281
364
  OptionSettings: optionSettings,
@@ -287,12 +370,10 @@ module.exports.updateEnvironmentSettings = async (
287
370
  };
288
371
 
289
372
  module.exports.restartAppServer = async (environmentName) => {
290
- const client = new ElasticBeanstalkClient({});
291
-
292
373
  await Spinner.prototype.simple(
293
374
  `Restarting app server for ${environmentName}`,
294
375
  () => {
295
- return client.send(
376
+ return getClient().send(
296
377
  new RestartAppServerCommand({
297
378
  EnvironmentName: environmentName,
298
379
  }),