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

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.
@@ -0,0 +1,147 @@
1
+ const {
2
+ OpenSearchClient,
3
+ CreateDomainCommand,
4
+ DeleteDomainCommand,
5
+ DescribeDomainCommand,
6
+ } = require("@aws-sdk/client-opensearch");
7
+ const { Spinner, poll } = require("../../libs/utilities");
8
+
9
+ module.exports.createOpenSearchDomain = async (
10
+ name,
11
+ securityGroupId,
12
+ privateSubnetIds,
13
+ instanceProfileRole,
14
+ ) => {
15
+ const client = new OpenSearchClient({});
16
+
17
+ const accountId = instanceProfileRole.split(":")[4] || "*";
18
+ const region = process.env.AWS_REGION;
19
+
20
+ const res = await Spinner.prototype.simple(
21
+ `Creating OpenSearch domain ${name}`,
22
+ () => {
23
+ return client.send(
24
+ new CreateDomainCommand({
25
+ DomainName: name,
26
+ EngineVersion: "OpenSearch_2.19",
27
+ ClusterConfig: {
28
+ InstanceType: "t3.small.search",
29
+ InstanceCount: 2,
30
+ ZoneAwarenessEnabled: true,
31
+ ZoneAwarenessConfig: {
32
+ AvailabilityZoneCount: 2,
33
+ },
34
+ },
35
+ EBSOptions: {
36
+ EBSEnabled: true,
37
+ VolumeType: "gp3",
38
+ VolumeSize: 20,
39
+ },
40
+ VPCOptions: {
41
+ SubnetIds: privateSubnetIds,
42
+ SecurityGroupIds: [securityGroupId],
43
+ },
44
+ EncryptionAtRestOptions: {
45
+ Enabled: true,
46
+ },
47
+ NodeToNodeEncryptionOptions: {
48
+ Enabled: true,
49
+ },
50
+ DomainEndpointOptions: {
51
+ EnforceHTTPS: true,
52
+ TLSSecurityPolicy: "Policy-Min-TLS-1-2-2019-07",
53
+ },
54
+ AdvancedSecurityOptions: {
55
+ Enabled: false,
56
+ InternalUserDatabaseEnabled: false,
57
+ },
58
+ AccessPolicies: JSON.stringify({
59
+ Version: "2012-10-17",
60
+ Statement: [
61
+ {
62
+ Effect: "Allow",
63
+ Principal: {
64
+ AWS: instanceProfileRole,
65
+ },
66
+ Action: "es:ESHttp*",
67
+ Resource: `arn:aws:es:${region}:${accountId}:domain/${name}/*`,
68
+ },
69
+ ],
70
+ }),
71
+ }),
72
+ );
73
+ },
74
+ );
75
+
76
+ return res;
77
+ };
78
+
79
+ module.exports.waitForOpenSearchDomain = async (name) => {
80
+ const client = new OpenSearchClient({});
81
+
82
+ const res = await Spinner.prototype.simple(
83
+ `Waiting for OpenSearch domain to become available (this may take 15-20 minutes)`,
84
+ () => {
85
+ return poll(
86
+ async () => {
87
+ const { DomainStatus } = await client.send(
88
+ new DescribeDomainCommand({ DomainName: name }),
89
+ );
90
+ return DomainStatus;
91
+ },
92
+ (status) =>
93
+ status.Processing ||
94
+ (!status.Endpoint && !status.Endpoints),
95
+ );
96
+ },
97
+ );
98
+
99
+ return res;
100
+ };
101
+
102
+ module.exports.describeOpenSearchDomain = async (name) => {
103
+ const client = new OpenSearchClient({});
104
+
105
+ const res = await Spinner.prototype.simple(
106
+ `Describing OpenSearch domain ${name}`,
107
+ () => {
108
+ return client.send(new DescribeDomainCommand({ DomainName: name }));
109
+ },
110
+ );
111
+
112
+ return res.DomainStatus;
113
+ };
114
+
115
+ module.exports.deleteOpenSearchDomain = async (name) => {
116
+ const client = new OpenSearchClient({});
117
+
118
+ await Spinner.prototype.simple(`Deleting OpenSearch domain ${name}`, () => {
119
+ return client.send(new DeleteDomainCommand({ DomainName: name }));
120
+ });
121
+ };
122
+
123
+ module.exports.waitForOpenSearchDeletion = async (name) => {
124
+ const client = new OpenSearchClient({});
125
+
126
+ await Spinner.prototype.simple(
127
+ `Waiting for OpenSearch domain to be deleted`,
128
+ () => {
129
+ return poll(
130
+ async () => {
131
+ try {
132
+ const { DomainStatus } = await client.send(
133
+ new DescribeDomainCommand({ DomainName: name }),
134
+ );
135
+ return DomainStatus;
136
+ } catch (e) {
137
+ if (e.name === "ResourceNotFoundException") {
138
+ return { deleted: true };
139
+ }
140
+ throw e;
141
+ }
142
+ },
143
+ (status) => !status.deleted,
144
+ );
145
+ },
146
+ );
147
+ };
@@ -1,9 +1,11 @@
1
1
  const {
2
2
  RDSClient,
3
3
  CreateDBInstanceCommand,
4
+ CreateDBSubnetGroupCommand,
4
5
  DeleteDBInstanceCommand,
5
6
  DescribeDBInstancesCommand,
6
7
  DescribeDBSnapshotsCommand,
8
+ DescribeDBSubnetGroupsCommand,
7
9
  RestoreDBInstanceFromDBSnapshotCommand,
8
10
  ModifyDBInstanceCommand,
9
11
  DescribeDBParameterGroupsCommand,
@@ -12,11 +14,44 @@ const {
12
14
  } = require("@aws-sdk/client-rds");
13
15
  const { Spinner, poll } = require("../../libs/utilities");
14
16
 
17
+ module.exports.ensureDBSubnetGroup = async (privateSubnetIds) => {
18
+ const client = new RDSClient({});
19
+ const name = "fw-auto-private-db-subnet-group";
20
+
21
+ try {
22
+ await client.send(
23
+ new DescribeDBSubnetGroupsCommand({
24
+ DBSubnetGroupName: name,
25
+ }),
26
+ );
27
+
28
+ return name;
29
+ } catch (e) {
30
+ if (e.name !== "DBSubnetGroupNotFoundFault") {
31
+ throw e;
32
+ }
33
+ }
34
+
35
+ await Spinner.prototype.simple(`Creating DB subnet group ${name}`, () => {
36
+ return client.send(
37
+ new CreateDBSubnetGroupCommand({
38
+ DBSubnetGroupName: name,
39
+ DBSubnetGroupDescription:
40
+ "Private subnets for lab-env RDS instances",
41
+ SubnetIds: privateSubnetIds,
42
+ }),
43
+ );
44
+ });
45
+
46
+ return name;
47
+ };
48
+
15
49
  module.exports.createRDSInstance = async (
16
50
  name,
17
51
  tags = [],
18
52
  password,
19
53
  securityGroupId,
54
+ subnetGroupName,
20
55
  ) => {
21
56
  const client = new RDSClient({});
22
57
 
@@ -37,6 +72,7 @@ module.exports.createRDSInstance = async (
37
72
  BackupRetentionPeriod: 7,
38
73
  DBName: "ebdb",
39
74
  DBParameterGroupName: "lab-env-mysql80-ssl-required",
75
+ DBSubnetGroupName: subnetGroupName,
40
76
  VpcSecurityGroupIds: [securityGroupId],
41
77
  Tags: [
42
78
  { Key: "client", Value: process.env.AWS_PROFILE },
@@ -157,6 +193,7 @@ module.exports.restoreRDSInstanceFromSnapshot = async (
157
193
  snapshotId,
158
194
  securityGroupId,
159
195
  tags = [],
196
+ subnetGroupName,
160
197
  ) => {
161
198
  const client = new RDSClient({});
162
199
 
@@ -170,6 +207,7 @@ module.exports.restoreRDSInstanceFromSnapshot = async (
170
207
  DBInstanceClass: "db.t3.small",
171
208
  PubliclyAccessible: false,
172
209
  DBParameterGroupName: "lab-env-mysql80-ssl-required",
210
+ DBSubnetGroupName: subnetGroupName,
173
211
  VpcSecurityGroupIds: [securityGroupId],
174
212
  Tags: [
175
213
  { Key: "client", Value: process.env.AWS_PROFILE },
@@ -30,18 +30,23 @@ module.exports.createS3Bucket = async (
30
30
  },
31
31
  );
32
32
 
33
+ const publicAccessConfig =
34
+ typeof blockAccess === "object"
35
+ ? blockAccess
36
+ : {
37
+ BlockPublicAcls: blockAccess,
38
+ BlockPublicPolicy: blockAccess,
39
+ IgnorePublicAcls: blockAccess,
40
+ RestrictPublicBuckets: blockAccess,
41
+ };
42
+
33
43
  await Spinner.prototype.simple(
34
- `${blockAccess ? "Blocking" : "Allowing"} all public access to s3 bucket`,
44
+ `Configuring public access settings for s3 bucket`,
35
45
  () => {
36
46
  return client.send(
37
47
  new PutPublicAccessBlockCommand({
38
48
  Bucket: bucket,
39
- PublicAccessBlockConfiguration: {
40
- BlockPublicAcls: blockAccess,
41
- BlockPublicPolicy: blockAccess,
42
- IgnorePublicAcls: blockAccess,
43
- RestrictPublicBuckets: blockAccess,
44
- },
49
+ PublicAccessBlockConfiguration: publicAccessConfig,
45
50
  }),
46
51
  );
47
52
  },
@@ -116,6 +121,35 @@ module.exports.setS3BucketPolicy = async (bucket, OAI) => {
116
121
  return res;
117
122
  };
118
123
 
124
+ module.exports.setPublicReadPolicy = async (bucket, prefix) => {
125
+ const client = new S3Client({});
126
+
127
+ let res = await Spinner.prototype.simple(
128
+ `Setting public read policy for ${prefix}`,
129
+ () => {
130
+ return client.send(
131
+ new PutBucketPolicyCommand({
132
+ Bucket: bucket,
133
+ Policy: JSON.stringify({
134
+ Version: "2012-10-17",
135
+ Statement: [
136
+ {
137
+ Sid: "PublicReadPrefix",
138
+ Effect: "Allow",
139
+ Principal: "*",
140
+ Action: "s3:GetObject",
141
+ Resource: `arn:aws:s3:::${bucket}/${prefix}`,
142
+ },
143
+ ],
144
+ }),
145
+ }),
146
+ );
147
+ },
148
+ );
149
+
150
+ return res;
151
+ };
152
+
119
153
  module.exports.addFileToS3Bucket = async (bucket, filepath, file) => {
120
154
  const client = new S3Client({});
121
155
 
@@ -3,6 +3,7 @@ const {
3
3
  CreateSecretCommand,
4
4
  DescribeSecretCommand,
5
5
  GetSecretValueCommand,
6
+ PutSecretValueCommand,
6
7
  DeleteSecretCommand,
7
8
  } = require("@aws-sdk/client-secrets-manager");
8
9
  const { Spinner } = require("../../libs/utilities");
@@ -57,6 +58,21 @@ module.exports.getSecretValue = async (name) => {
57
58
  return JSON.parse(res.SecretString);
58
59
  };
59
60
 
61
+ module.exports.updateSecretValue = async (name, secretObject) => {
62
+ const client = new SecretsManagerClient({});
63
+
64
+ let res = await Spinner.prototype.simple(`Updating secret ${name}`, () => {
65
+ return client.send(
66
+ new PutSecretValueCommand({
67
+ SecretId: name,
68
+ SecretString: JSON.stringify(secretObject),
69
+ }),
70
+ );
71
+ });
72
+
73
+ return res;
74
+ };
75
+
60
76
  module.exports.deleteSecret = async (name) => {
61
77
  const client = new SecretsManagerClient({});
62
78
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fishawack/lab-env",
3
- "version": "5.7.0-beta.1",
3
+ "version": "5.7.0-beta.2",
4
4
  "description": "Docker manager for FW",
5
5
  "main": "cli.js",
6
6
  "scripts": {
@@ -21,10 +21,12 @@
21
21
  "fw": "cli.js"
22
22
  },
23
23
  "dependencies": {
24
+ "@aws-sdk/client-acm": "^3.1067.0",
24
25
  "@aws-sdk/client-cloudfront": "^3.141.0",
25
26
  "@aws-sdk/client-ec2": "^3.399.0",
26
27
  "@aws-sdk/client-elastic-beanstalk": "^3.395.0",
27
28
  "@aws-sdk/client-iam": "^3.150.0",
29
+ "@aws-sdk/client-opensearch": "^3.1065.0",
28
30
  "@aws-sdk/client-rds": "^3.395.0",
29
31
  "@aws-sdk/client-s3": "^3.141.0",
30
32
  "@aws-sdk/client-secrets-manager": "^3.395.0",