sst 2.15.0 → 2.16.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/cdk/deploy-stack.d.ts +2 -1
- package/cdk/deploy-stack.js +15 -4
- package/cdk/{cloudformation-deployments-wrapper.d.ts → deployments-wrapper.d.ts} +1 -1
- package/cdk/{cloudformation-deployments-wrapper.js → deployments-wrapper.js} +25 -8
- package/cdk/{cloudformation-deployments.d.ts → deployments.d.ts} +76 -44
- package/cdk/deployments.js +362 -0
- package/constructs/RDS.d.ts +11 -11
- package/constructs/RDS.js +31 -23
- package/package.json +10 -10
- package/sst.mjs +263 -135
- package/stacks/deploy.js +3 -5
- package/cdk/cloudformation-deployments.js +0 -267
|
@@ -0,0 +1,362 @@
|
|
|
1
|
+
// Copied from https://github.com/aws/aws-cdk/blob/main/packages/aws-cdk/lib/api/cloudformation-deployments.ts
|
|
2
|
+
import * as cxapi from "@aws-cdk/cx-api";
|
|
3
|
+
import * as cdk_assets from "cdk-assets";
|
|
4
|
+
import { AssetManifest } from "cdk-assets";
|
|
5
|
+
import { debug, warning } from "sst-aws-cdk/lib/logging.js";
|
|
6
|
+
import { buildAssets, publishAssets, PublishingAws, EVENT_TO_LOGGER, } from "sst-aws-cdk/lib/util/asset-publishing.js";
|
|
7
|
+
import { Mode } from "sst-aws-cdk/lib/api/aws-auth/credentials.js";
|
|
8
|
+
import { deployStack, destroyStack, makeBodyParameterAndUpload, } from "./deploy-stack.js";
|
|
9
|
+
import { loadCurrentTemplateWithNestedStacks, loadCurrentTemplate, } from "sst-aws-cdk/lib/api/nested-stack-helpers.js";
|
|
10
|
+
import { ToolkitInfo } from "sst-aws-cdk/lib/api/toolkit-info.js";
|
|
11
|
+
import { CloudFormationStack, } from "sst-aws-cdk/lib/api/util/cloudformation.js";
|
|
12
|
+
import { replaceEnvPlaceholders } from "sst-aws-cdk/lib/api/util/placeholders.js";
|
|
13
|
+
import { callWithRetry } from "./util.js";
|
|
14
|
+
/**
|
|
15
|
+
* Scope for a single set of deployments from a set of Cloud Assembly Artifacts
|
|
16
|
+
*
|
|
17
|
+
* Manages lookup of SDKs, Bootstrap stacks, etc.
|
|
18
|
+
*/
|
|
19
|
+
export class Deployments {
|
|
20
|
+
props;
|
|
21
|
+
sdkProvider;
|
|
22
|
+
toolkitInfoCache = new Map();
|
|
23
|
+
sdkCache = new Map();
|
|
24
|
+
publisherCache = new Map();
|
|
25
|
+
constructor(props) {
|
|
26
|
+
this.props = props;
|
|
27
|
+
this.sdkProvider = props.sdkProvider;
|
|
28
|
+
}
|
|
29
|
+
async readCurrentTemplateWithNestedStacks(rootStackArtifact, retrieveProcessedTemplate = false) {
|
|
30
|
+
const sdk = (await this.prepareSdkWithLookupOrDeployRole(rootStackArtifact))
|
|
31
|
+
.stackSdk;
|
|
32
|
+
return (await loadCurrentTemplateWithNestedStacks(rootStackArtifact, sdk, retrieveProcessedTemplate)).deployedTemplate;
|
|
33
|
+
}
|
|
34
|
+
async readCurrentTemplate(stackArtifact) {
|
|
35
|
+
debug(`Reading existing template for stack ${stackArtifact.displayName}.`);
|
|
36
|
+
const sdk = (await this.prepareSdkWithLookupOrDeployRole(stackArtifact))
|
|
37
|
+
.stackSdk;
|
|
38
|
+
return loadCurrentTemplate(stackArtifact, sdk);
|
|
39
|
+
}
|
|
40
|
+
async resourceIdentifierSummaries(stackArtifact, toolkitStackName) {
|
|
41
|
+
debug(`Retrieving template summary for stack ${stackArtifact.displayName}.`);
|
|
42
|
+
// Currently, needs to use `deploy-role` since it may need to read templates in the staging
|
|
43
|
+
// bucket which have been encrypted with a KMS key (and lookup-role may not read encrypted things)
|
|
44
|
+
const { stackSdk, resolvedEnvironment } = await this.prepareSdkFor(stackArtifact, undefined, Mode.ForReading);
|
|
45
|
+
const cfn = stackSdk.cloudFormation();
|
|
46
|
+
const toolkitInfo = await this.lookupToolkit(resolvedEnvironment, stackSdk, toolkitStackName);
|
|
47
|
+
// Upload the template, if necessary, before passing it to CFN
|
|
48
|
+
const cfnParam = await makeBodyParameterAndUpload(stackArtifact, resolvedEnvironment, toolkitInfo, this.sdkProvider, stackSdk);
|
|
49
|
+
const response = await cfn.getTemplateSummary(cfnParam).promise();
|
|
50
|
+
if (!response.ResourceIdentifierSummaries) {
|
|
51
|
+
debug('GetTemplateSummary API call did not return "ResourceIdentifierSummaries"');
|
|
52
|
+
}
|
|
53
|
+
return response.ResourceIdentifierSummaries ?? [];
|
|
54
|
+
}
|
|
55
|
+
async deployStack(options) {
|
|
56
|
+
let deploymentMethod = options.deploymentMethod;
|
|
57
|
+
if (options.changeSetName || options.execute !== undefined) {
|
|
58
|
+
if (deploymentMethod) {
|
|
59
|
+
throw new Error("You cannot supply both 'deploymentMethod' and 'changeSetName/execute'. Supply one or the other.");
|
|
60
|
+
}
|
|
61
|
+
deploymentMethod = {
|
|
62
|
+
method: "change-set",
|
|
63
|
+
changeSetName: options.changeSetName,
|
|
64
|
+
execute: options.execute,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
const { stackSdk, resolvedEnvironment, cloudFormationRoleArn } = await this.prepareSdkFor(options.stack, options.roleArn, Mode.ForWriting);
|
|
68
|
+
const toolkitInfo = await callWithRetry(() => this.lookupToolkit(resolvedEnvironment, stackSdk, options.toolkitStackName));
|
|
69
|
+
// Do a verification of the bootstrap stack version
|
|
70
|
+
await this.validateBootstrapStackVersion(options.stack.stackName, options.stack.requiresBootstrapStackVersion, options.stack.bootstrapStackVersionSsmParameter, toolkitInfo);
|
|
71
|
+
// Deploy assets
|
|
72
|
+
const assetArtifacts = options.stack.dependencies.filter(cxapi.AssetManifestArtifact.isAssetManifestArtifact);
|
|
73
|
+
for (const asset of assetArtifacts) {
|
|
74
|
+
const manifest = AssetManifest.fromFile(asset.file);
|
|
75
|
+
//await buildAssets(manifest, sdkProvider, resolvedEnvironment, {
|
|
76
|
+
//});
|
|
77
|
+
await publishAssets(manifest, this.sdkProvider, resolvedEnvironment, {
|
|
78
|
+
buildAssets: true,
|
|
79
|
+
quiet: options.quiet,
|
|
80
|
+
parallel: options.assetParallelism,
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
return deployStack({
|
|
84
|
+
stack: options.stack,
|
|
85
|
+
noMonitor: true,
|
|
86
|
+
resolvedEnvironment,
|
|
87
|
+
deployName: options.deployName,
|
|
88
|
+
notificationArns: options.notificationArns,
|
|
89
|
+
quiet: options.quiet,
|
|
90
|
+
sdk: stackSdk,
|
|
91
|
+
sdkProvider: this.sdkProvider,
|
|
92
|
+
roleArn: cloudFormationRoleArn,
|
|
93
|
+
reuseAssets: options.reuseAssets,
|
|
94
|
+
toolkitInfo,
|
|
95
|
+
tags: options.tags,
|
|
96
|
+
deploymentMethod,
|
|
97
|
+
force: options.force,
|
|
98
|
+
parameters: options.parameters,
|
|
99
|
+
usePreviousParameters: options.usePreviousParameters,
|
|
100
|
+
progress: options.progress,
|
|
101
|
+
ci: options.ci,
|
|
102
|
+
rollback: options.rollback,
|
|
103
|
+
hotswap: options.hotswap,
|
|
104
|
+
extraUserAgent: options.extraUserAgent,
|
|
105
|
+
resourcesToImport: options.resourcesToImport,
|
|
106
|
+
overrideTemplate: options.overrideTemplate,
|
|
107
|
+
assetParallelism: options.assetParallelism,
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
async destroyStack(options) {
|
|
111
|
+
const { stackSdk, cloudFormationRoleArn: roleArn } = await this.prepareSdkFor(options.stack, options.roleArn, Mode.ForWriting);
|
|
112
|
+
return destroyStack({
|
|
113
|
+
sdk: stackSdk,
|
|
114
|
+
roleArn,
|
|
115
|
+
stack: options.stack,
|
|
116
|
+
deployName: options.deployName,
|
|
117
|
+
quiet: options.quiet,
|
|
118
|
+
ci: options.ci,
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
async stackExists(options) {
|
|
122
|
+
const { stackSdk } = await this.prepareSdkFor(options.stack, undefined, Mode.ForReading);
|
|
123
|
+
const stack = await CloudFormationStack.lookup(stackSdk.cloudFormation(), options.deployName ?? options.stack.stackName);
|
|
124
|
+
return stack.exists;
|
|
125
|
+
}
|
|
126
|
+
async prepareSdkWithLookupOrDeployRole(stackArtifact) {
|
|
127
|
+
// try to assume the lookup role
|
|
128
|
+
try {
|
|
129
|
+
const result = await this.prepareSdkWithLookupRoleFor(stackArtifact);
|
|
130
|
+
if (result.didAssumeRole) {
|
|
131
|
+
return {
|
|
132
|
+
resolvedEnvironment: result.resolvedEnvironment,
|
|
133
|
+
stackSdk: result.sdk,
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
catch { }
|
|
138
|
+
// fall back to the deploy role
|
|
139
|
+
return this.prepareSdkFor(stackArtifact, undefined, Mode.ForReading);
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Get the environment necessary for touching the given stack
|
|
143
|
+
*
|
|
144
|
+
* Returns the following:
|
|
145
|
+
*
|
|
146
|
+
* - The resolved environment for the stack (no more 'unknown-account/unknown-region')
|
|
147
|
+
* - SDK loaded with the right credentials for calling `CreateChangeSet`.
|
|
148
|
+
* - The Execution Role that should be passed to CloudFormation.
|
|
149
|
+
*/
|
|
150
|
+
async prepareSdkFor(stack, roleArn, mode) {
|
|
151
|
+
if (!stack.environment) {
|
|
152
|
+
throw new Error(`The stack ${stack.displayName} does not have an environment`);
|
|
153
|
+
}
|
|
154
|
+
const resolvedEnvironment = await this.sdkProvider.resolveEnvironment(stack.environment);
|
|
155
|
+
// Substitute any placeholders with information about the current environment
|
|
156
|
+
const arns = await replaceEnvPlaceholders({
|
|
157
|
+
assumeRoleArn: stack.assumeRoleArn,
|
|
158
|
+
// Use the override if given, otherwise use the field from the stack
|
|
159
|
+
cloudFormationRoleArn: roleArn ?? stack.cloudFormationExecutionRoleArn,
|
|
160
|
+
}, resolvedEnvironment, this.sdkProvider);
|
|
161
|
+
const stackSdk = await this.cachedSdkForEnvironment(resolvedEnvironment, mode, {
|
|
162
|
+
assumeRoleArn: arns.assumeRoleArn,
|
|
163
|
+
assumeRoleExternalId: stack.assumeRoleExternalId,
|
|
164
|
+
});
|
|
165
|
+
return {
|
|
166
|
+
stackSdk: stackSdk.sdk,
|
|
167
|
+
resolvedEnvironment,
|
|
168
|
+
cloudFormationRoleArn: arns.cloudFormationRoleArn,
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Try to use the bootstrap lookupRole. There are two scenarios that are handled here
|
|
173
|
+
* 1. The lookup role may not exist (it was added in bootstrap stack version 7)
|
|
174
|
+
* 2. The lookup role may not have the correct permissions (ReadOnlyAccess was added in
|
|
175
|
+
* bootstrap stack version 8)
|
|
176
|
+
*
|
|
177
|
+
* In the case of 1 (lookup role doesn't exist) `forEnvironment` will either:
|
|
178
|
+
* 1. Return the default credentials if the default credentials are for the stack account
|
|
179
|
+
* 2. Throw an error if the default credentials are not for the stack account.
|
|
180
|
+
*
|
|
181
|
+
* If we successfully assume the lookup role we then proceed to 2 and check whether the bootstrap
|
|
182
|
+
* stack version is valid. If it is not we throw an error which should be handled in the calling
|
|
183
|
+
* function (and fallback to use a different role, etc)
|
|
184
|
+
*
|
|
185
|
+
* If we do not successfully assume the lookup role, but do get back the default credentials
|
|
186
|
+
* then return those and note that we are returning the default credentials. The calling
|
|
187
|
+
* function can then decide to use them or fallback to another role.
|
|
188
|
+
*/
|
|
189
|
+
async prepareSdkWithLookupRoleFor(stack) {
|
|
190
|
+
const resolvedEnvironment = await this.sdkProvider.resolveEnvironment(stack.environment);
|
|
191
|
+
// Substitute any placeholders with information about the current environment
|
|
192
|
+
const arns = await replaceEnvPlaceholders({
|
|
193
|
+
lookupRoleArn: stack.lookupRole?.arn,
|
|
194
|
+
}, resolvedEnvironment, this.sdkProvider);
|
|
195
|
+
// try to assume the lookup role
|
|
196
|
+
const warningMessage = `Could not assume ${arns.lookupRoleArn}, proceeding anyway.`;
|
|
197
|
+
const upgradeMessage = `(To get rid of this warning, please upgrade to bootstrap version >= ${stack.lookupRole?.requiresBootstrapStackVersion})`;
|
|
198
|
+
try {
|
|
199
|
+
const stackSdk = await this.cachedSdkForEnvironment(resolvedEnvironment, Mode.ForReading, {
|
|
200
|
+
assumeRoleArn: arns.lookupRoleArn,
|
|
201
|
+
assumeRoleExternalId: stack.lookupRole?.assumeRoleExternalId,
|
|
202
|
+
});
|
|
203
|
+
// if we succeed in assuming the lookup role, make sure we have the correct bootstrap stack version
|
|
204
|
+
if (stackSdk.didAssumeRole &&
|
|
205
|
+
stack.lookupRole?.bootstrapStackVersionSsmParameter &&
|
|
206
|
+
stack.lookupRole.requiresBootstrapStackVersion) {
|
|
207
|
+
const version = await ToolkitInfo.versionFromSsmParameter(stackSdk.sdk, stack.lookupRole.bootstrapStackVersionSsmParameter);
|
|
208
|
+
if (version < stack.lookupRole.requiresBootstrapStackVersion) {
|
|
209
|
+
throw new Error(`Bootstrap stack version '${stack.lookupRole.requiresBootstrapStackVersion}' is required, found version '${version}'.`);
|
|
210
|
+
}
|
|
211
|
+
// we may not have assumed the lookup role because one was not provided
|
|
212
|
+
// if that is the case then don't print the upgrade warning
|
|
213
|
+
}
|
|
214
|
+
else if (!stackSdk.didAssumeRole &&
|
|
215
|
+
stack.lookupRole?.requiresBootstrapStackVersion) {
|
|
216
|
+
warning(upgradeMessage);
|
|
217
|
+
}
|
|
218
|
+
return { ...stackSdk, resolvedEnvironment };
|
|
219
|
+
}
|
|
220
|
+
catch (e) {
|
|
221
|
+
debug(e);
|
|
222
|
+
// only print out the warnings if the lookupRole exists AND there is a required
|
|
223
|
+
// bootstrap version, otherwise the warnings will print `undefined`
|
|
224
|
+
if (stack.lookupRole && stack.lookupRole.requiresBootstrapStackVersion) {
|
|
225
|
+
warning(warningMessage);
|
|
226
|
+
warning(upgradeMessage);
|
|
227
|
+
}
|
|
228
|
+
throw e;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* Look up the toolkit for a given environment, using a given SDK
|
|
233
|
+
*/
|
|
234
|
+
async lookupToolkit(resolvedEnvironment, sdk, toolkitStackName) {
|
|
235
|
+
const key = `${resolvedEnvironment.account}:${resolvedEnvironment.region}:${toolkitStackName}`;
|
|
236
|
+
const existing = this.toolkitInfoCache.get(key);
|
|
237
|
+
if (existing) {
|
|
238
|
+
return existing;
|
|
239
|
+
}
|
|
240
|
+
const ret = await ToolkitInfo.lookup(resolvedEnvironment, sdk, toolkitStackName);
|
|
241
|
+
this.toolkitInfoCache.set(key, ret);
|
|
242
|
+
return ret;
|
|
243
|
+
}
|
|
244
|
+
async prepareAndValidateAssets(asset, options) {
|
|
245
|
+
const { stackSdk, resolvedEnvironment } = await this.prepareSdkFor(options.stack, options.roleArn, Mode.ForWriting);
|
|
246
|
+
const toolkitInfo = await this.lookupToolkit(resolvedEnvironment, stackSdk, options.toolkitStackName);
|
|
247
|
+
const stackEnv = await this.sdkProvider.resolveEnvironment(options.stack.environment);
|
|
248
|
+
await this.validateBootstrapStackVersion(options.stack.stackName, asset.requiresBootstrapStackVersion, asset.bootstrapStackVersionSsmParameter, toolkitInfo);
|
|
249
|
+
const manifest = AssetManifest.fromFile(asset.file);
|
|
250
|
+
return { manifest, stackEnv };
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* Build all assets in a manifest
|
|
254
|
+
*
|
|
255
|
+
* @deprecated Use `buildSingleAsset` instead
|
|
256
|
+
*/
|
|
257
|
+
async buildAssets(asset, options) {
|
|
258
|
+
const { manifest, stackEnv } = await this.prepareAndValidateAssets(asset, options);
|
|
259
|
+
await buildAssets(manifest, this.sdkProvider, stackEnv, options.buildOptions);
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* Publish all assets in a manifest
|
|
263
|
+
*
|
|
264
|
+
* @deprecated Use `publishSingleAsset` instead
|
|
265
|
+
*/
|
|
266
|
+
async publishAssets(asset, options) {
|
|
267
|
+
const { manifest, stackEnv } = await this.prepareAndValidateAssets(asset, options);
|
|
268
|
+
await publishAssets(manifest, this.sdkProvider, stackEnv, options.publishOptions);
|
|
269
|
+
}
|
|
270
|
+
/**
|
|
271
|
+
* Build a single asset from an asset manifest
|
|
272
|
+
*/
|
|
273
|
+
// eslint-disable-next-line max-len
|
|
274
|
+
async buildSingleAsset(assetArtifact, assetManifest, asset, options) {
|
|
275
|
+
const { stackSdk, resolvedEnvironment: stackEnv } = await this.prepareSdkFor(options.stack, options.roleArn, Mode.ForWriting);
|
|
276
|
+
const toolkitInfo = await this.lookupToolkit(stackEnv, stackSdk, options.toolkitStackName);
|
|
277
|
+
await this.validateBootstrapStackVersion(options.stack.stackName, assetArtifact.requiresBootstrapStackVersion, assetArtifact.bootstrapStackVersionSsmParameter, toolkitInfo);
|
|
278
|
+
const publisher = this.cachedPublisher(assetManifest, stackEnv, options.stackName);
|
|
279
|
+
await publisher.buildEntry(asset);
|
|
280
|
+
}
|
|
281
|
+
/**
|
|
282
|
+
* Publish a single asset from an asset manifest
|
|
283
|
+
*/
|
|
284
|
+
// eslint-disable-next-line max-len
|
|
285
|
+
async publishSingleAsset(assetManifest, asset, options) {
|
|
286
|
+
const { resolvedEnvironment: stackEnv } = await this.prepareSdkFor(options.stack, options.roleArn, Mode.ForWriting);
|
|
287
|
+
// No need to validate anymore, we already did that during build
|
|
288
|
+
const publisher = this.cachedPublisher(assetManifest, stackEnv, options.stackName);
|
|
289
|
+
await publisher.publishEntry(asset);
|
|
290
|
+
}
|
|
291
|
+
/**
|
|
292
|
+
* Return whether a single asset has been published already
|
|
293
|
+
*/
|
|
294
|
+
async isSingleAssetPublished(assetManifest, asset, options) {
|
|
295
|
+
const { resolvedEnvironment: stackEnv } = await this.prepareSdkFor(options.stack, options.roleArn, Mode.ForWriting);
|
|
296
|
+
const publisher = this.cachedPublisher(assetManifest, stackEnv, options.stackName);
|
|
297
|
+
return publisher.isEntryPublished(asset);
|
|
298
|
+
}
|
|
299
|
+
/**
|
|
300
|
+
* Validate that the bootstrap stack has the right version for this stack
|
|
301
|
+
*/
|
|
302
|
+
async validateBootstrapStackVersion(stackName, requiresBootstrapStackVersion, bootstrapStackVersionSsmParameter, toolkitInfo) {
|
|
303
|
+
if (requiresBootstrapStackVersion === undefined) {
|
|
304
|
+
return;
|
|
305
|
+
}
|
|
306
|
+
try {
|
|
307
|
+
await toolkitInfo.validateVersion(requiresBootstrapStackVersion, bootstrapStackVersionSsmParameter);
|
|
308
|
+
}
|
|
309
|
+
catch (e) {
|
|
310
|
+
throw new Error(`${stackName}: ${e.message}`);
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
async cachedSdkForEnvironment(environment, mode, options) {
|
|
314
|
+
const cacheKey = [
|
|
315
|
+
environment.account,
|
|
316
|
+
environment.region,
|
|
317
|
+
`${mode}`,
|
|
318
|
+
options?.assumeRoleArn ?? "",
|
|
319
|
+
options?.assumeRoleExternalId ?? "",
|
|
320
|
+
].join(":");
|
|
321
|
+
const existing = this.sdkCache.get(cacheKey);
|
|
322
|
+
if (existing) {
|
|
323
|
+
return existing;
|
|
324
|
+
}
|
|
325
|
+
const ret = await this.sdkProvider.forEnvironment(environment, mode, options);
|
|
326
|
+
this.sdkCache.set(cacheKey, ret);
|
|
327
|
+
return ret;
|
|
328
|
+
}
|
|
329
|
+
cachedPublisher(assetManifest, env, stackName) {
|
|
330
|
+
const existing = this.publisherCache.get(assetManifest);
|
|
331
|
+
if (existing) {
|
|
332
|
+
return existing;
|
|
333
|
+
}
|
|
334
|
+
const prefix = stackName ? `${stackName}: ` : "";
|
|
335
|
+
const publisher = new cdk_assets.AssetPublishing(assetManifest, {
|
|
336
|
+
aws: new PublishingAws(this.sdkProvider, env),
|
|
337
|
+
progressListener: new ParallelSafeAssetProgress(prefix, this.props.quiet ?? false),
|
|
338
|
+
});
|
|
339
|
+
this.publisherCache.set(assetManifest, publisher);
|
|
340
|
+
return publisher;
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
/**
|
|
344
|
+
* Asset progress that doesn't do anything with percentages (currently)
|
|
345
|
+
*/
|
|
346
|
+
class ParallelSafeAssetProgress {
|
|
347
|
+
prefix;
|
|
348
|
+
quiet;
|
|
349
|
+
constructor(prefix, quiet) {
|
|
350
|
+
this.prefix = prefix;
|
|
351
|
+
this.quiet = quiet;
|
|
352
|
+
}
|
|
353
|
+
onPublishEvent(type, event) {
|
|
354
|
+
const handler = this.quiet && type !== "fail" ? debug : EVENT_TO_LOGGER[type];
|
|
355
|
+
handler(`${this.prefix} ${type}: ${event.message}`);
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
/**
|
|
359
|
+
* @deprecated Use 'Deployments' instead
|
|
360
|
+
*/
|
|
361
|
+
export class CloudFormationDeployments extends Deployments {
|
|
362
|
+
}
|
package/constructs/RDS.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Construct } from "constructs";
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
import
|
|
2
|
+
import { IVpc } from "aws-cdk-lib/aws-ec2";
|
|
3
|
+
import { AuroraCapacityUnit, Endpoint, IServerlessCluster, ServerlessCluster, ServerlessClusterProps } from "aws-cdk-lib/aws-rds";
|
|
4
|
+
import { ISecret } from "aws-cdk-lib/aws-secretsmanager";
|
|
5
5
|
import { SSTConstruct } from "./Construct.js";
|
|
6
6
|
import { Function as Fn } from "./Function.js";
|
|
7
7
|
import { FunctionBindingProps } from "./util/functionBinding.js";
|
|
@@ -44,13 +44,13 @@ export interface RDSProps {
|
|
|
44
44
|
*
|
|
45
45
|
* @default "ACU_2"
|
|
46
46
|
*/
|
|
47
|
-
minCapacity?: keyof typeof
|
|
47
|
+
minCapacity?: keyof typeof AuroraCapacityUnit;
|
|
48
48
|
/**
|
|
49
49
|
* The maximum capacity for the cluster.
|
|
50
50
|
*
|
|
51
51
|
* @default "ACU_16"
|
|
52
52
|
*/
|
|
53
|
-
maxCapacity?: keyof typeof
|
|
53
|
+
maxCapacity?: keyof typeof AuroraCapacityUnit;
|
|
54
54
|
};
|
|
55
55
|
/**
|
|
56
56
|
* Path to the directory that contains the migration scripts. The `RDS` construct uses [Kysely](https://kysely-org.github.io/kysely/) to run and manage schema migrations. The `migrations` prop should point to the folder where your migration files are.
|
|
@@ -128,16 +128,16 @@ export interface RDSProps {
|
|
|
128
128
|
* });
|
|
129
129
|
* ```
|
|
130
130
|
*/
|
|
131
|
-
cluster?:
|
|
131
|
+
cluster?: IServerlessCluster | RDSCdkServerlessClusterProps;
|
|
132
132
|
/**
|
|
133
133
|
* Required when importing existing RDS Serverless v1 Cluster.
|
|
134
134
|
*/
|
|
135
|
-
secret?:
|
|
135
|
+
secret?: ISecret;
|
|
136
136
|
};
|
|
137
137
|
}
|
|
138
138
|
export type RDSEngineType = RDSProps["engine"];
|
|
139
|
-
export interface RDSCdkServerlessClusterProps extends Omit<
|
|
140
|
-
vpc?:
|
|
139
|
+
export interface RDSCdkServerlessClusterProps extends Omit<ServerlessClusterProps, "vpc" | "engine" | "defaultDatabaseName" | "scaling"> {
|
|
140
|
+
vpc?: IVpc;
|
|
141
141
|
}
|
|
142
142
|
/**
|
|
143
143
|
* The `RDS` construct is a higher level CDK construct that makes it easy to create an [RDS Serverless Cluster](https://aws.amazon.com/rds/).
|
|
@@ -159,7 +159,7 @@ export declare class RDS extends Construct implements SSTConstruct {
|
|
|
159
159
|
/**
|
|
160
160
|
* The ARN of the internally created CDK ServerlessCluster instance.
|
|
161
161
|
*/
|
|
162
|
-
cluster:
|
|
162
|
+
cluster: ServerlessCluster;
|
|
163
163
|
};
|
|
164
164
|
/**
|
|
165
165
|
* The ARN of the internally created CDK ServerlessCluster instance.
|
|
@@ -179,7 +179,7 @@ export declare class RDS extends Construct implements SSTConstruct {
|
|
|
179
179
|
/**
|
|
180
180
|
* The ARN of the internally created RDS Serverless Cluster.
|
|
181
181
|
*/
|
|
182
|
-
get clusterEndpoint():
|
|
182
|
+
get clusterEndpoint(): Endpoint;
|
|
183
183
|
/**
|
|
184
184
|
* The default database name of the RDS Serverless Cluster.
|
|
185
185
|
*/
|
package/constructs/RDS.js
CHANGED
|
@@ -5,9 +5,11 @@ import url from "url";
|
|
|
5
5
|
import * as crypto from "crypto";
|
|
6
6
|
import { Construct } from "constructs";
|
|
7
7
|
import { Duration as CDKDuration, CustomResource } from "aws-cdk-lib/core";
|
|
8
|
-
import
|
|
9
|
-
import
|
|
10
|
-
import
|
|
8
|
+
import { SubnetType, Vpc } from "aws-cdk-lib/aws-ec2";
|
|
9
|
+
import { AuroraCapacityUnit, AuroraEngineVersion, AuroraMysqlEngineVersion, AuroraPostgresEngineVersion, DatabaseClusterEngine, ServerlessCluster, } from "aws-cdk-lib/aws-rds";
|
|
10
|
+
import { Code, Function, Runtime } from "aws-cdk-lib/aws-lambda";
|
|
11
|
+
import { PolicyStatement } from "aws-cdk-lib/aws-iam";
|
|
12
|
+
import { Stack } from "./Stack.js";
|
|
11
13
|
import { getFunctionRef, isCDKConstruct } from "./Construct.js";
|
|
12
14
|
import { Function as Fn } from "./Function.js";
|
|
13
15
|
const __dirname = url.fileURLToPath(new URL(".", import.meta.url));
|
|
@@ -185,33 +187,33 @@ export class RDS extends Construct {
|
|
|
185
187
|
}
|
|
186
188
|
getEngine(engine) {
|
|
187
189
|
if (engine === "mysql5.6") {
|
|
188
|
-
return
|
|
189
|
-
version:
|
|
190
|
+
return DatabaseClusterEngine.aurora({
|
|
191
|
+
version: AuroraEngineVersion.VER_10A,
|
|
190
192
|
});
|
|
191
193
|
}
|
|
192
194
|
else if (engine === "mysql5.7") {
|
|
193
|
-
return
|
|
194
|
-
version:
|
|
195
|
+
return DatabaseClusterEngine.auroraMysql({
|
|
196
|
+
version: AuroraMysqlEngineVersion.VER_2_07_1,
|
|
195
197
|
});
|
|
196
198
|
}
|
|
197
199
|
else if (engine === "postgresql10.14") {
|
|
198
|
-
return
|
|
199
|
-
version:
|
|
200
|
+
return DatabaseClusterEngine.auroraPostgres({
|
|
201
|
+
version: AuroraPostgresEngineVersion.VER_10_14,
|
|
200
202
|
});
|
|
201
203
|
}
|
|
202
204
|
else if (engine === "postgresql11.13") {
|
|
203
|
-
return
|
|
204
|
-
version:
|
|
205
|
+
return DatabaseClusterEngine.auroraPostgres({
|
|
206
|
+
version: AuroraPostgresEngineVersion.VER_11_13,
|
|
205
207
|
});
|
|
206
208
|
}
|
|
207
209
|
else if (engine === "postgresql11.16") {
|
|
208
|
-
return
|
|
209
|
-
version:
|
|
210
|
+
return DatabaseClusterEngine.auroraPostgres({
|
|
211
|
+
version: AuroraPostgresEngineVersion.VER_11_16,
|
|
210
212
|
});
|
|
211
213
|
}
|
|
212
214
|
else if (engine === "postgresql13.9") {
|
|
213
|
-
return
|
|
214
|
-
version:
|
|
215
|
+
return DatabaseClusterEngine.auroraPostgres({
|
|
216
|
+
version: AuroraPostgresEngineVersion.VER_13_9,
|
|
215
217
|
});
|
|
216
218
|
}
|
|
217
219
|
throw new Error(`The specified "engine" is not supported for sst.RDS. Only mysql5.6, mysql5.7, postgresql11.13, postgresql11.16, and postgres13.9 engines are currently supported.`);
|
|
@@ -223,15 +225,15 @@ export class RDS extends Construct {
|
|
|
223
225
|
: scaling?.autoPause === true || scaling?.autoPause === undefined
|
|
224
226
|
? CDKDuration.minutes(5)
|
|
225
227
|
: CDKDuration.minutes(scaling?.autoPause),
|
|
226
|
-
minCapacity:
|
|
227
|
-
maxCapacity:
|
|
228
|
+
minCapacity: AuroraCapacityUnit[scaling?.minCapacity || "ACU_2"],
|
|
229
|
+
maxCapacity: AuroraCapacityUnit[scaling?.maxCapacity || "ACU_16"],
|
|
228
230
|
};
|
|
229
231
|
}
|
|
230
232
|
getVpc(props) {
|
|
231
233
|
if (props.vpc) {
|
|
232
234
|
return props.vpc;
|
|
233
235
|
}
|
|
234
|
-
return new
|
|
236
|
+
return new Vpc(this, "vpc", {
|
|
235
237
|
natGateways: 0,
|
|
236
238
|
});
|
|
237
239
|
}
|
|
@@ -240,14 +242,14 @@ export class RDS extends Construct {
|
|
|
240
242
|
return props.vpcSubnets;
|
|
241
243
|
}
|
|
242
244
|
return {
|
|
243
|
-
subnetType:
|
|
245
|
+
subnetType: SubnetType.PRIVATE_ISOLATED,
|
|
244
246
|
};
|
|
245
247
|
}
|
|
246
248
|
createCluster() {
|
|
247
249
|
const { engine, defaultDatabaseName, scaling, cdk } = this.props;
|
|
248
250
|
const app = this.node.root;
|
|
249
251
|
const clusterProps = (cdk?.cluster || {});
|
|
250
|
-
return new
|
|
252
|
+
return new ServerlessCluster(this, "Cluster", {
|
|
251
253
|
clusterIdentifier: app.logicalPrefixedName(this.node.id),
|
|
252
254
|
...clusterProps,
|
|
253
255
|
defaultDatabaseName: defaultDatabaseName,
|
|
@@ -307,12 +309,18 @@ export class RDS extends Construct {
|
|
|
307
309
|
createMigrationCustomResource(migrations) {
|
|
308
310
|
const app = this.node.root;
|
|
309
311
|
// Create custom resource handler
|
|
310
|
-
const handler = new
|
|
311
|
-
code:
|
|
312
|
-
runtime:
|
|
312
|
+
const handler = new Function(this, "MigrationHandler", {
|
|
313
|
+
code: Code.fromAsset(path.join(__dirname, "../support/script-function")),
|
|
314
|
+
runtime: Runtime.NODEJS_16_X,
|
|
313
315
|
handler: "index.handler",
|
|
314
316
|
timeout: CDKDuration.minutes(15),
|
|
315
317
|
memorySize: 1024,
|
|
318
|
+
initialPolicy: [
|
|
319
|
+
new PolicyStatement({
|
|
320
|
+
actions: ["cloudformation:DescribeStacks"],
|
|
321
|
+
resources: [Stack.of(this).stackId],
|
|
322
|
+
}),
|
|
323
|
+
],
|
|
316
324
|
});
|
|
317
325
|
this.migratorFunction?.grantInvoke(handler);
|
|
318
326
|
// Note: "MigrationsHash" is generated to ensure the Custom Resource function
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"sideEffects": false,
|
|
3
3
|
"name": "sst",
|
|
4
|
-
"version": "2.
|
|
4
|
+
"version": "2.16.0",
|
|
5
5
|
"bin": {
|
|
6
6
|
"sst": "cli/sst.js"
|
|
7
7
|
},
|
|
@@ -25,12 +25,12 @@
|
|
|
25
25
|
},
|
|
26
26
|
"homepage": "https://sst.dev",
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@aws-cdk/aws-apigatewayv2-alpha": "^2.
|
|
29
|
-
"@aws-cdk/aws-apigatewayv2-authorizers-alpha": "^2.
|
|
30
|
-
"@aws-cdk/aws-apigatewayv2-integrations-alpha": "^2.
|
|
31
|
-
"@aws-cdk/cloud-assembly-schema": "2.
|
|
32
|
-
"@aws-cdk/cloudformation-diff": "2.
|
|
33
|
-
"@aws-cdk/cx-api": "2.
|
|
28
|
+
"@aws-cdk/aws-apigatewayv2-alpha": "^2.84.0-alpha.0",
|
|
29
|
+
"@aws-cdk/aws-apigatewayv2-authorizers-alpha": "^2.84.0-alpha.0",
|
|
30
|
+
"@aws-cdk/aws-apigatewayv2-integrations-alpha": "^2.84.0-alpha.0",
|
|
31
|
+
"@aws-cdk/cloud-assembly-schema": "2.84.0",
|
|
32
|
+
"@aws-cdk/cloudformation-diff": "2.84.0",
|
|
33
|
+
"@aws-cdk/cx-api": "2.84.0",
|
|
34
34
|
"@aws-sdk/client-cloudformation": "^3.279.0",
|
|
35
35
|
"@aws-sdk/client-eventbridge": "^3.342.0",
|
|
36
36
|
"@aws-sdk/client-iam": "^3.279.0",
|
|
@@ -52,11 +52,11 @@
|
|
|
52
52
|
"@babel/plugin-syntax-typescript": "^7.21.4",
|
|
53
53
|
"@trpc/server": "9.16.0",
|
|
54
54
|
"adm-zip": "^0.5.10",
|
|
55
|
-
"aws-cdk-lib": "2.
|
|
55
|
+
"aws-cdk-lib": "2.84.0",
|
|
56
56
|
"aws-iot-device-sdk": "^2.2.12",
|
|
57
57
|
"aws-sdk": "^2.1326.0",
|
|
58
58
|
"builtin-modules": "3.2.0",
|
|
59
|
-
"cdk-assets": "2.
|
|
59
|
+
"cdk-assets": "2.84.0",
|
|
60
60
|
"chalk": "^5.2.0",
|
|
61
61
|
"chokidar": "^3.5.3",
|
|
62
62
|
"ci-info": "^3.7.0",
|
|
@@ -84,7 +84,7 @@
|
|
|
84
84
|
"ora": "^6.1.2",
|
|
85
85
|
"react": "18.2.0",
|
|
86
86
|
"remeda": "^1.3.0",
|
|
87
|
-
"sst-aws-cdk": "2.
|
|
87
|
+
"sst-aws-cdk": "2.84.0",
|
|
88
88
|
"tree-kill": "^1.2.2",
|
|
89
89
|
"undici": "^5.12.0",
|
|
90
90
|
"uuid": "^9.0.0",
|