@fishawack/lab-env 5.6.1-beta.1 → 5.7.0-beta.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/CHANGELOG.md +25 -0
- package/cli.js +1 -0
- package/commands/create/cmds/deprovision.js +30 -6
- package/commands/create/cmds/provision.js +116 -1
- package/commands/create/cmds/sync-secrets.js +197 -0
- package/commands/create/libs/vars.js +171 -256
- package/commands/create/services/aws/ec2.js +150 -1
- package/commands/create/services/aws/elasticbeanstalk.js +54 -0
- package/commands/create/services/aws/iam.js +14 -0
- package/commands/create/services/aws/index.js +399 -42
- package/commands/create/services/aws/rds.js +246 -0
- package/commands/create/services/aws/secretsmanager.js +73 -0
- package/commands/create/templates/elasticbeanstalk/.ebextensions/laravel/rds-ssl.config +5 -0
- package/commands/create/templates/elasticbeanstalk/.ebextensions/misc/setvars.config +16 -1
- package/package.json +3 -1
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
const {
|
|
2
|
+
RDSClient,
|
|
3
|
+
CreateDBInstanceCommand,
|
|
4
|
+
DeleteDBInstanceCommand,
|
|
5
|
+
DescribeDBInstancesCommand,
|
|
6
|
+
DescribeDBSnapshotsCommand,
|
|
7
|
+
RestoreDBInstanceFromDBSnapshotCommand,
|
|
8
|
+
ModifyDBInstanceCommand,
|
|
9
|
+
DescribeDBParameterGroupsCommand,
|
|
10
|
+
CreateDBParameterGroupCommand,
|
|
11
|
+
ModifyDBParameterGroupCommand,
|
|
12
|
+
} = require("@aws-sdk/client-rds");
|
|
13
|
+
const { Spinner, poll } = require("../../libs/utilities");
|
|
14
|
+
|
|
15
|
+
module.exports.createRDSInstance = async (
|
|
16
|
+
name,
|
|
17
|
+
tags = [],
|
|
18
|
+
password,
|
|
19
|
+
securityGroupId,
|
|
20
|
+
) => {
|
|
21
|
+
const client = new RDSClient({});
|
|
22
|
+
|
|
23
|
+
let res = await Spinner.prototype.simple(
|
|
24
|
+
`Creating RDS instance ${name}`,
|
|
25
|
+
() => {
|
|
26
|
+
return client.send(
|
|
27
|
+
new CreateDBInstanceCommand({
|
|
28
|
+
DBInstanceIdentifier: name,
|
|
29
|
+
DBInstanceClass: "db.t3.small",
|
|
30
|
+
Engine: "mysql",
|
|
31
|
+
EngineVersion: "8.0.43",
|
|
32
|
+
MasterUsername: "ebroot",
|
|
33
|
+
MasterUserPassword: password,
|
|
34
|
+
AllocatedStorage: 5,
|
|
35
|
+
StorageEncrypted: true,
|
|
36
|
+
PubliclyAccessible: false,
|
|
37
|
+
BackupRetentionPeriod: 7,
|
|
38
|
+
DBName: "ebdb",
|
|
39
|
+
DBParameterGroupName: "lab-env-mysql80-ssl-required",
|
|
40
|
+
VpcSecurityGroupIds: [securityGroupId],
|
|
41
|
+
Tags: [
|
|
42
|
+
{ Key: "client", Value: process.env.AWS_PROFILE },
|
|
43
|
+
].concat(tags),
|
|
44
|
+
}),
|
|
45
|
+
);
|
|
46
|
+
},
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
return res;
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
module.exports.waitForRDSInstance = async (name) => {
|
|
53
|
+
const client = new RDSClient({});
|
|
54
|
+
|
|
55
|
+
const res = await Spinner.prototype.simple(
|
|
56
|
+
`Waiting for RDS instance to become available`,
|
|
57
|
+
() => {
|
|
58
|
+
return poll(
|
|
59
|
+
async () =>
|
|
60
|
+
(
|
|
61
|
+
await client.send(
|
|
62
|
+
new DescribeDBInstancesCommand({
|
|
63
|
+
DBInstanceIdentifier: name,
|
|
64
|
+
}),
|
|
65
|
+
)
|
|
66
|
+
).DBInstances[0],
|
|
67
|
+
({ DBInstanceStatus }) => DBInstanceStatus !== "available",
|
|
68
|
+
);
|
|
69
|
+
},
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
return res;
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
module.exports.describeRDSInstance = async (name) => {
|
|
76
|
+
const client = new RDSClient({});
|
|
77
|
+
|
|
78
|
+
let res = await Spinner.prototype.simple(
|
|
79
|
+
`Describing RDS instance ${name}`,
|
|
80
|
+
() => {
|
|
81
|
+
return client.send(
|
|
82
|
+
new DescribeDBInstancesCommand({
|
|
83
|
+
DBInstanceIdentifier: name,
|
|
84
|
+
}),
|
|
85
|
+
);
|
|
86
|
+
},
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
return res.DBInstances[0];
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
module.exports.deleteRDSInstance = async (name) => {
|
|
93
|
+
const client = new RDSClient({});
|
|
94
|
+
|
|
95
|
+
await Spinner.prototype.simple(`Deleting RDS instance ${name}`, () => {
|
|
96
|
+
return client.send(
|
|
97
|
+
new DeleteDBInstanceCommand({
|
|
98
|
+
DBInstanceIdentifier: name,
|
|
99
|
+
SkipFinalSnapshot: true,
|
|
100
|
+
}),
|
|
101
|
+
);
|
|
102
|
+
});
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
module.exports.waitForRDSDeletion = async (name) => {
|
|
106
|
+
const client = new RDSClient({});
|
|
107
|
+
|
|
108
|
+
await Spinner.prototype.simple(
|
|
109
|
+
`Waiting for RDS instance to be deleted`,
|
|
110
|
+
() => {
|
|
111
|
+
return poll(
|
|
112
|
+
async () => {
|
|
113
|
+
try {
|
|
114
|
+
const res = await client.send(
|
|
115
|
+
new DescribeDBInstancesCommand({
|
|
116
|
+
DBInstanceIdentifier: name,
|
|
117
|
+
}),
|
|
118
|
+
);
|
|
119
|
+
return res.DBInstances[0];
|
|
120
|
+
} catch (e) {
|
|
121
|
+
if (e.name === "DBInstanceNotFoundFault") {
|
|
122
|
+
return { DBInstanceStatus: "deleted" };
|
|
123
|
+
}
|
|
124
|
+
throw e;
|
|
125
|
+
}
|
|
126
|
+
},
|
|
127
|
+
({ DBInstanceStatus }) => DBInstanceStatus !== "deleted",
|
|
128
|
+
);
|
|
129
|
+
},
|
|
130
|
+
);
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
module.exports.listManualSnapshots = async () => {
|
|
134
|
+
const client = new RDSClient({});
|
|
135
|
+
|
|
136
|
+
const res = await Spinner.prototype.simple(
|
|
137
|
+
`Fetching manual RDS snapshots`,
|
|
138
|
+
() => {
|
|
139
|
+
return client.send(
|
|
140
|
+
new DescribeDBSnapshotsCommand({
|
|
141
|
+
SnapshotType: "manual",
|
|
142
|
+
}),
|
|
143
|
+
);
|
|
144
|
+
},
|
|
145
|
+
);
|
|
146
|
+
|
|
147
|
+
return (res.DBSnapshots || [])
|
|
148
|
+
.filter((s) => s.Status === "available")
|
|
149
|
+
.sort(
|
|
150
|
+
(a, b) =>
|
|
151
|
+
new Date(b.SnapshotCreateTime) - new Date(a.SnapshotCreateTime),
|
|
152
|
+
);
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
module.exports.restoreRDSInstanceFromSnapshot = async (
|
|
156
|
+
name,
|
|
157
|
+
snapshotId,
|
|
158
|
+
securityGroupId,
|
|
159
|
+
tags = [],
|
|
160
|
+
) => {
|
|
161
|
+
const client = new RDSClient({});
|
|
162
|
+
|
|
163
|
+
let res = await Spinner.prototype.simple(
|
|
164
|
+
`Restoring RDS instance ${name} from snapshot ${snapshotId}`,
|
|
165
|
+
() => {
|
|
166
|
+
return client.send(
|
|
167
|
+
new RestoreDBInstanceFromDBSnapshotCommand({
|
|
168
|
+
DBInstanceIdentifier: name,
|
|
169
|
+
DBSnapshotIdentifier: snapshotId,
|
|
170
|
+
DBInstanceClass: "db.t3.small",
|
|
171
|
+
PubliclyAccessible: false,
|
|
172
|
+
DBParameterGroupName: "lab-env-mysql80-ssl-required",
|
|
173
|
+
VpcSecurityGroupIds: [securityGroupId],
|
|
174
|
+
Tags: [
|
|
175
|
+
{ Key: "client", Value: process.env.AWS_PROFILE },
|
|
176
|
+
].concat(tags),
|
|
177
|
+
}),
|
|
178
|
+
);
|
|
179
|
+
},
|
|
180
|
+
);
|
|
181
|
+
|
|
182
|
+
return res;
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
module.exports.modifyRDSPassword = async (name, password) => {
|
|
186
|
+
const client = new RDSClient({});
|
|
187
|
+
|
|
188
|
+
await Spinner.prototype.simple(
|
|
189
|
+
`Updating master password for RDS instance ${name}`,
|
|
190
|
+
() => {
|
|
191
|
+
return client.send(
|
|
192
|
+
new ModifyDBInstanceCommand({
|
|
193
|
+
DBInstanceIdentifier: name,
|
|
194
|
+
MasterUserPassword: password,
|
|
195
|
+
ApplyImmediately: true,
|
|
196
|
+
}),
|
|
197
|
+
);
|
|
198
|
+
},
|
|
199
|
+
);
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
const SSL_PARAMETER_GROUP = "lab-env-mysql80-ssl-required";
|
|
203
|
+
|
|
204
|
+
module.exports.ensureSSLParameterGroup = async () => {
|
|
205
|
+
const client = new RDSClient({});
|
|
206
|
+
|
|
207
|
+
try {
|
|
208
|
+
await client.send(
|
|
209
|
+
new DescribeDBParameterGroupsCommand({
|
|
210
|
+
DBParameterGroupName: SSL_PARAMETER_GROUP,
|
|
211
|
+
}),
|
|
212
|
+
);
|
|
213
|
+
} catch {
|
|
214
|
+
await Spinner.prototype.simple(
|
|
215
|
+
`Creating parameter group ${SSL_PARAMETER_GROUP}`,
|
|
216
|
+
() => {
|
|
217
|
+
return client.send(
|
|
218
|
+
new CreateDBParameterGroupCommand({
|
|
219
|
+
DBParameterGroupName: SSL_PARAMETER_GROUP,
|
|
220
|
+
DBParameterGroupFamily: "mysql8.0",
|
|
221
|
+
Description:
|
|
222
|
+
"lab-env managed parameter group requiring SSL connections",
|
|
223
|
+
}),
|
|
224
|
+
);
|
|
225
|
+
},
|
|
226
|
+
);
|
|
227
|
+
|
|
228
|
+
await Spinner.prototype.simple(
|
|
229
|
+
`Enabling require_secure_transport on ${SSL_PARAMETER_GROUP}`,
|
|
230
|
+
() => {
|
|
231
|
+
return client.send(
|
|
232
|
+
new ModifyDBParameterGroupCommand({
|
|
233
|
+
DBParameterGroupName: SSL_PARAMETER_GROUP,
|
|
234
|
+
Parameters: [
|
|
235
|
+
{
|
|
236
|
+
ParameterName: "require_secure_transport",
|
|
237
|
+
ParameterValue: "1",
|
|
238
|
+
ApplyMethod: "immediate",
|
|
239
|
+
},
|
|
240
|
+
],
|
|
241
|
+
}),
|
|
242
|
+
);
|
|
243
|
+
},
|
|
244
|
+
);
|
|
245
|
+
}
|
|
246
|
+
};
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
const {
|
|
2
|
+
SecretsManagerClient,
|
|
3
|
+
CreateSecretCommand,
|
|
4
|
+
DescribeSecretCommand,
|
|
5
|
+
GetSecretValueCommand,
|
|
6
|
+
DeleteSecretCommand,
|
|
7
|
+
} = require("@aws-sdk/client-secrets-manager");
|
|
8
|
+
const { Spinner } = require("../../libs/utilities");
|
|
9
|
+
|
|
10
|
+
module.exports.createSecret = async (name, secretObject, tags = []) => {
|
|
11
|
+
const client = new SecretsManagerClient({});
|
|
12
|
+
|
|
13
|
+
let res = await Spinner.prototype.simple(`Creating secret ${name}`, () => {
|
|
14
|
+
return client.send(
|
|
15
|
+
new CreateSecretCommand({
|
|
16
|
+
Name: name,
|
|
17
|
+
SecretString: JSON.stringify(secretObject),
|
|
18
|
+
Tags: tags.map((t) => ({ Key: t.Key, Value: String(t.Value) })),
|
|
19
|
+
}),
|
|
20
|
+
);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
return res;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
module.exports.describeSecret = async (name) => {
|
|
27
|
+
const client = new SecretsManagerClient({});
|
|
28
|
+
|
|
29
|
+
let res = await Spinner.prototype.simple(
|
|
30
|
+
`Checking for existing secret ${name}`,
|
|
31
|
+
() => {
|
|
32
|
+
return client.send(
|
|
33
|
+
new DescribeSecretCommand({
|
|
34
|
+
SecretId: name,
|
|
35
|
+
}),
|
|
36
|
+
);
|
|
37
|
+
},
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
return res;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
module.exports.getSecretValue = async (name) => {
|
|
44
|
+
const client = new SecretsManagerClient({});
|
|
45
|
+
|
|
46
|
+
let res = await Spinner.prototype.simple(
|
|
47
|
+
`Retrieving secret ${name}`,
|
|
48
|
+
() => {
|
|
49
|
+
return client.send(
|
|
50
|
+
new GetSecretValueCommand({
|
|
51
|
+
SecretId: name,
|
|
52
|
+
}),
|
|
53
|
+
);
|
|
54
|
+
},
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
return JSON.parse(res.SecretString);
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
module.exports.deleteSecret = async (name) => {
|
|
61
|
+
const client = new SecretsManagerClient({});
|
|
62
|
+
|
|
63
|
+
let res = await Spinner.prototype.simple(`Deleting secret ${name}`, () => {
|
|
64
|
+
return client.send(
|
|
65
|
+
new DeleteSecretCommand({
|
|
66
|
+
SecretId: name,
|
|
67
|
+
ForceDeleteWithoutRecovery: true,
|
|
68
|
+
}),
|
|
69
|
+
);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
return res;
|
|
73
|
+
};
|
|
@@ -5,7 +5,22 @@ option_settings:
|
|
|
5
5
|
commands:
|
|
6
6
|
setvars:
|
|
7
7
|
command: |
|
|
8
|
-
/opt/elasticbeanstalk/bin/get-config
|
|
8
|
+
ENV_NAME=$(/opt/elasticbeanstalk/bin/get-config container -k environment_name)
|
|
9
|
+
REGION=$(TOKEN=$(curl -s -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 21600") && curl -s -H "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/meta-data/placement/region)
|
|
10
|
+
|
|
11
|
+
> /etc/profile.d/sh.local
|
|
12
|
+
> /etc/sysconfig/env
|
|
13
|
+
|
|
14
|
+
for GROUP in app storage database; do
|
|
15
|
+
SECRET_JSON=$(aws secretsmanager get-secret-value \
|
|
16
|
+
--secret-id "${ENV_NAME}-${GROUP}" \
|
|
17
|
+
--region "$REGION" \
|
|
18
|
+
--query SecretString \
|
|
19
|
+
--output text 2>/dev/null) || continue
|
|
20
|
+
|
|
21
|
+
echo "$SECRET_JSON" | jq -r 'to_entries | .[] | "export \(.key)=\u0027\(.value)\u0027"' >> /etc/profile.d/sh.local
|
|
22
|
+
echo "$SECRET_JSON" | jq -r 'to_entries | .[] | "\(.key)=\"\(.value)\""' >> /etc/sysconfig/env
|
|
23
|
+
done
|
|
9
24
|
|
|
10
25
|
packages:
|
|
11
26
|
yum:
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fishawack/lab-env",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.7.0-beta.1",
|
|
4
4
|
"description": "Docker manager for FW",
|
|
5
5
|
"main": "cli.js",
|
|
6
6
|
"scripts": {
|
|
@@ -25,7 +25,9 @@
|
|
|
25
25
|
"@aws-sdk/client-ec2": "^3.399.0",
|
|
26
26
|
"@aws-sdk/client-elastic-beanstalk": "^3.395.0",
|
|
27
27
|
"@aws-sdk/client-iam": "^3.150.0",
|
|
28
|
+
"@aws-sdk/client-rds": "^3.395.0",
|
|
28
29
|
"@aws-sdk/client-s3": "^3.141.0",
|
|
30
|
+
"@aws-sdk/client-secrets-manager": "^3.395.0",
|
|
29
31
|
"@aws-sdk/credential-providers": "^3.1011.0",
|
|
30
32
|
"apache-md5": "^1.1.8",
|
|
31
33
|
"axios": "^0.21.4",
|