sst 2.28.6 → 2.29.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.
- package/cdk/deploy-stack.d.ts +4 -4
- package/cdk/deploy-stack.js +6 -5
- package/cdk/deployments-wrapper.js +7 -9
- package/cdk/deployments.d.ts +15 -8
- package/cdk/deployments.js +30 -42
- package/cli/commands/bind.js +0 -3
- package/cli/sst.js +2 -4
- package/package.json +3 -3
- package/runtime/handlers.js +41 -7
- package/stacks/build.js +7 -1
- package/stacks/synth.js +67 -54
package/cdk/deploy-stack.d.ts
CHANGED
|
@@ -2,8 +2,8 @@ import * as cxapi from "@aws-cdk/cx-api";
|
|
|
2
2
|
import { Tag } from "sst-aws-cdk/lib/cdk-toolkit.js";
|
|
3
3
|
import { AssetManifestBuilder } from "sst-aws-cdk/lib/util/asset-manifest-builder.js";
|
|
4
4
|
import { ISDK, SdkProvider } from "sst-aws-cdk/lib/api/aws-auth/index.js";
|
|
5
|
+
import { EnvironmentResources } from "sst-aws-cdk/lib/api/environment-resources.js";
|
|
5
6
|
import { HotswapMode } from "sst-aws-cdk/lib/api/hotswap/common.js";
|
|
6
|
-
import { ToolkitInfo } from "sst-aws-cdk/lib/api/toolkit-info.js";
|
|
7
7
|
import { ResourcesToImport } from "sst-aws-cdk/lib/api/util/cloudformation.js";
|
|
8
8
|
import { StackActivityProgress } from "sst-aws-cdk/lib/api/util/cloudformation/stack-activity-monitor.js";
|
|
9
9
|
type TemplateBodyParameter = {
|
|
@@ -56,7 +56,7 @@ export interface DeployStackOptions {
|
|
|
56
56
|
/**
|
|
57
57
|
* Information about the bootstrap stack found in the target environment
|
|
58
58
|
*/
|
|
59
|
-
readonly
|
|
59
|
+
readonly envResources: EnvironmentResources;
|
|
60
60
|
/**
|
|
61
61
|
* Role to pass to CloudFormation to execute the change set
|
|
62
62
|
*
|
|
@@ -200,14 +200,14 @@ export declare function deployStack(options: DeployStackOptions): Promise<Deploy
|
|
|
200
200
|
* @param stack the synthesized stack that provides the CloudFormation template
|
|
201
201
|
* @param toolkitInfo information about the toolkit stack
|
|
202
202
|
*/
|
|
203
|
-
export declare function makeBodyParameter(stack: cxapi.CloudFormationStackArtifact, resolvedEnvironment: cxapi.Environment, assetManifest: AssetManifestBuilder,
|
|
203
|
+
export declare function makeBodyParameter(stack: cxapi.CloudFormationStackArtifact, resolvedEnvironment: cxapi.Environment, assetManifest: AssetManifestBuilder, resources: EnvironmentResources, sdk: ISDK, overrideTemplate?: any): Promise<TemplateBodyParameter>;
|
|
204
204
|
/**
|
|
205
205
|
* Prepare a body parameter for CFN, performing the upload
|
|
206
206
|
*
|
|
207
207
|
* Return it as-is if it is small enough to pass in the API call,
|
|
208
208
|
* upload to S3 and return the coordinates if it is not.
|
|
209
209
|
*/
|
|
210
|
-
export declare function makeBodyParameterAndUpload(stack: cxapi.CloudFormationStackArtifact, resolvedEnvironment: cxapi.Environment,
|
|
210
|
+
export declare function makeBodyParameterAndUpload(stack: cxapi.CloudFormationStackArtifact, resolvedEnvironment: cxapi.Environment, resources: EnvironmentResources, sdkProvider: SdkProvider, sdk: ISDK, overrideTemplate?: any): Promise<TemplateBodyParameter>;
|
|
211
211
|
export interface DestroyStackOptions {
|
|
212
212
|
/**
|
|
213
213
|
* The stack to be destroyed
|
package/cdk/deploy-stack.js
CHANGED
|
@@ -37,7 +37,7 @@ export async function deployStack(options) {
|
|
|
37
37
|
// an ad-hoc asset manifest, while passing their locations via template
|
|
38
38
|
// parameters.
|
|
39
39
|
const legacyAssets = new AssetManifestBuilder();
|
|
40
|
-
const assetParams = await addMetadataAssetsToManifest(stackArtifact, legacyAssets, options.
|
|
40
|
+
const assetParams = await addMetadataAssetsToManifest(stackArtifact, legacyAssets, options.envResources, options.reuseAssets);
|
|
41
41
|
const finalParameterValues = { ...options.parameters, ...assetParams };
|
|
42
42
|
const templateParams = TemplateParameters.fromTemplate(stackArtifact.template);
|
|
43
43
|
const stackParams = options.usePreviousParameters
|
|
@@ -58,7 +58,7 @@ export async function deployStack(options) {
|
|
|
58
58
|
else {
|
|
59
59
|
debug(`${deployName}: deploying...`);
|
|
60
60
|
}
|
|
61
|
-
const bodyParameter = await makeBodyParameter(stackArtifact, options.resolvedEnvironment, legacyAssets, options.
|
|
61
|
+
const bodyParameter = await makeBodyParameter(stackArtifact, options.resolvedEnvironment, legacyAssets, options.envResources, options.sdk, options.overrideTemplate);
|
|
62
62
|
await publishAssets(legacyAssets.toManifest(stackArtifact.assembly.directory), options.sdkProvider, stackEnv, {
|
|
63
63
|
parallel: options.assetParallelism,
|
|
64
64
|
});
|
|
@@ -352,7 +352,7 @@ class FullCloudFormationDeployment {
|
|
|
352
352
|
* @param stack the synthesized stack that provides the CloudFormation template
|
|
353
353
|
* @param toolkitInfo information about the toolkit stack
|
|
354
354
|
*/
|
|
355
|
-
export async function makeBodyParameter(stack, resolvedEnvironment, assetManifest,
|
|
355
|
+
export async function makeBodyParameter(stack, resolvedEnvironment, assetManifest, resources, sdk, overrideTemplate) {
|
|
356
356
|
// If the template has already been uploaded to S3, just use it from there.
|
|
357
357
|
if (stack.stackTemplateAssetObjectUrl && !overrideTemplate) {
|
|
358
358
|
return {
|
|
@@ -364,6 +364,7 @@ export async function makeBodyParameter(stack, resolvedEnvironment, assetManifes
|
|
|
364
364
|
if (templateJson.length <= LARGE_TEMPLATE_SIZE_KB * 1024) {
|
|
365
365
|
return { TemplateBody: templateJson };
|
|
366
366
|
}
|
|
367
|
+
const toolkitInfo = await resources.lookupToolkit();
|
|
367
368
|
if (!toolkitInfo.found) {
|
|
368
369
|
error(`The template for stack "${stack.displayName}" is ${Math.round(templateJson.length / 1024)}KiB. ` +
|
|
369
370
|
`Templates larger than ${LARGE_TEMPLATE_SIZE_KB}KiB must be uploaded to S3.\n` +
|
|
@@ -394,14 +395,14 @@ export async function makeBodyParameter(stack, resolvedEnvironment, assetManifes
|
|
|
394
395
|
* Return it as-is if it is small enough to pass in the API call,
|
|
395
396
|
* upload to S3 and return the coordinates if it is not.
|
|
396
397
|
*/
|
|
397
|
-
export async function makeBodyParameterAndUpload(stack, resolvedEnvironment,
|
|
398
|
+
export async function makeBodyParameterAndUpload(stack, resolvedEnvironment, resources, sdkProvider, sdk, overrideTemplate) {
|
|
398
399
|
// We don't have access to the actual asset manifest here, so pretend that the
|
|
399
400
|
// stack doesn't have a pre-published URL.
|
|
400
401
|
const forceUploadStack = Object.create(stack, {
|
|
401
402
|
stackTemplateAssetObjectUrl: { value: undefined },
|
|
402
403
|
});
|
|
403
404
|
const builder = new AssetManifestBuilder();
|
|
404
|
-
const bodyparam = await makeBodyParameter(forceUploadStack, resolvedEnvironment, builder,
|
|
405
|
+
const bodyparam = await makeBodyParameter(forceUploadStack, resolvedEnvironment, builder, resources, sdk, overrideTemplate);
|
|
405
406
|
const manifest = builder.toManifest(stack.assembly.directory);
|
|
406
407
|
await publishAssets(manifest, sdkProvider, resolvedEnvironment, {
|
|
407
408
|
quiet: true,
|
|
@@ -3,7 +3,6 @@ import { AssetManifest } from "cdk-assets";
|
|
|
3
3
|
import { debug } from "sst-aws-cdk/lib/logging.js";
|
|
4
4
|
import { CloudFormationStack, TemplateParameters, waitForStackDelete, } from "sst-aws-cdk/lib/api/util/cloudformation.js";
|
|
5
5
|
import { Mode } from "sst-aws-cdk/lib/api/aws-auth/credentials.js";
|
|
6
|
-
import { ToolkitInfo } from "sst-aws-cdk/lib/api/toolkit-info.js";
|
|
7
6
|
import { addMetadataAssetsToManifest } from "sst-aws-cdk/lib/assets.js";
|
|
8
7
|
import { publishAssets } from "sst-aws-cdk/lib/util/asset-publishing.js";
|
|
9
8
|
import { AssetManifestBuilder } from "sst-aws-cdk/lib/util/asset-manifest-builder.js";
|
|
@@ -11,7 +10,7 @@ import { Deployments, } from "./deployments.js";
|
|
|
11
10
|
import { makeBodyParameter } from "./deploy-stack.js";
|
|
12
11
|
import { lazy } from "../util/lazy.js";
|
|
13
12
|
export async function publishDeployAssets(sdkProvider, options) {
|
|
14
|
-
const { deployment,
|
|
13
|
+
const { deployment, envResources, stackSdk, resolvedEnvironment, cloudFormationRoleArn, } = await useDeployment().get(sdkProvider, options);
|
|
15
14
|
// TODO
|
|
16
15
|
// old
|
|
17
16
|
//await deployment.publishStackAssets(options.stack, toolkitInfo, {
|
|
@@ -44,7 +43,7 @@ export async function publishDeployAssets(sdkProvider, options) {
|
|
|
44
43
|
sdkProvider,
|
|
45
44
|
roleArn: cloudFormationRoleArn,
|
|
46
45
|
reuseAssets: options.reuseAssets,
|
|
47
|
-
|
|
46
|
+
envResources,
|
|
48
47
|
tags: options.tags,
|
|
49
48
|
deploymentMethod: options.deploymentMethod,
|
|
50
49
|
force: options.force,
|
|
@@ -67,13 +66,12 @@ const useDeployment = lazy(() => {
|
|
|
67
66
|
const region = options.stack.environment.region;
|
|
68
67
|
if (!state.has(region)) {
|
|
69
68
|
const deployment = new Deployments({ sdkProvider });
|
|
70
|
-
const { stackSdk, resolvedEnvironment, cloudFormationRoleArn } = await deployment.prepareSdkFor(options.stack, options.roleArn, Mode.ForWriting);
|
|
71
|
-
const toolkitInfo = await ToolkitInfo.lookup(resolvedEnvironment, stackSdk, options.toolkitStackName);
|
|
69
|
+
const { stackSdk, resolvedEnvironment, cloudFormationRoleArn, envResources, } = await deployment.prepareSdkFor(options.stack, options.roleArn, Mode.ForWriting);
|
|
72
70
|
// Do a verification of the bootstrap stack version
|
|
73
|
-
await deployment.validateBootstrapStackVersion(options.stack.stackName, options.stack.requiresBootstrapStackVersion, options.stack.bootstrapStackVersionSsmParameter,
|
|
71
|
+
await deployment.validateBootstrapStackVersion(options.stack.stackName, options.stack.requiresBootstrapStackVersion, options.stack.bootstrapStackVersionSsmParameter, envResources);
|
|
74
72
|
state.set(region, {
|
|
75
73
|
deployment,
|
|
76
|
-
|
|
74
|
+
envResources,
|
|
77
75
|
stackSdk,
|
|
78
76
|
resolvedEnvironment,
|
|
79
77
|
cloudFormationRoleArn,
|
|
@@ -106,13 +104,13 @@ async function deployStack(options) {
|
|
|
106
104
|
// an ad-hoc asset manifest, while passing their locations via template
|
|
107
105
|
// parameters.
|
|
108
106
|
const legacyAssets = new AssetManifestBuilder();
|
|
109
|
-
const assetParams = await addMetadataAssetsToManifest(stackArtifact, legacyAssets, options.
|
|
107
|
+
const assetParams = await addMetadataAssetsToManifest(stackArtifact, legacyAssets, options.envResources, options.reuseAssets);
|
|
110
108
|
const finalParameterValues = { ...options.parameters, ...assetParams };
|
|
111
109
|
const templateParams = TemplateParameters.fromTemplate(stackArtifact.template);
|
|
112
110
|
const stackParams = options.usePreviousParameters
|
|
113
111
|
? templateParams.updateExisting(finalParameterValues, cloudFormationStack.parameters)
|
|
114
112
|
: templateParams.supplyAll(finalParameterValues);
|
|
115
|
-
const bodyParameter = await makeBodyParameter(stackArtifact, options.resolvedEnvironment, legacyAssets, options.
|
|
113
|
+
const bodyParameter = await makeBodyParameter(stackArtifact, options.resolvedEnvironment, legacyAssets, options.envResources, options.sdk, options.overrideTemplate);
|
|
116
114
|
await publishAssets(legacyAssets.toManifest(stackArtifact.assembly.directory), options.sdkProvider, stackEnv, {
|
|
117
115
|
parallel: options.assetParallelism,
|
|
118
116
|
});
|
package/cdk/deployments.d.ts
CHANGED
|
@@ -6,7 +6,7 @@ import { Mode } from "sst-aws-cdk/lib/api/aws-auth/credentials.js";
|
|
|
6
6
|
import { ISDK } from "sst-aws-cdk/lib/api/aws-auth/sdk.js";
|
|
7
7
|
import { SdkProvider } from "sst-aws-cdk/lib/api/aws-auth/sdk-provider.js";
|
|
8
8
|
import { DeployStackResult, DeploymentMethod } from "./deploy-stack.js";
|
|
9
|
-
import {
|
|
9
|
+
import { EnvironmentResources } from "sst-aws-cdk/lib/api/environment-resources.js";
|
|
10
10
|
import { Template, ResourcesToImport, ResourceIdentifierSummaries } from "sst-aws-cdk/lib/api/util/cloudformation.js";
|
|
11
11
|
import { StackActivityProgress } from "sst-aws-cdk/lib/api/util/cloudformation/stack-activity-monitor.js";
|
|
12
12
|
import { HotswapMode } from "sst-aws-cdk/lib/api/hotswap/common.js";
|
|
@@ -31,6 +31,10 @@ export interface PreparedSdkWithLookupRoleForEnvironment {
|
|
|
31
31
|
* the default credentials (not the assume role credentials)
|
|
32
32
|
*/
|
|
33
33
|
readonly didAssumeRole: boolean;
|
|
34
|
+
/**
|
|
35
|
+
* An object for accessing the bootstrap resources in this environment
|
|
36
|
+
*/
|
|
37
|
+
readonly envResources: EnvironmentResources;
|
|
34
38
|
}
|
|
35
39
|
export interface DeployStackOptions {
|
|
36
40
|
/**
|
|
@@ -212,6 +216,7 @@ export interface StackExistsOptions {
|
|
|
212
216
|
}
|
|
213
217
|
export interface DeploymentsProps {
|
|
214
218
|
sdkProvider: SdkProvider;
|
|
219
|
+
readonly toolkitStackName?: string;
|
|
215
220
|
readonly quiet?: boolean;
|
|
216
221
|
}
|
|
217
222
|
/**
|
|
@@ -234,6 +239,10 @@ export interface PreparedSdkForEnvironment {
|
|
|
234
239
|
* @default - no execution role is used
|
|
235
240
|
*/
|
|
236
241
|
readonly cloudFormationRoleArn?: string;
|
|
242
|
+
/**
|
|
243
|
+
* Access class for environmental resources to help the deployment
|
|
244
|
+
*/
|
|
245
|
+
readonly envResources: EnvironmentResources;
|
|
237
246
|
}
|
|
238
247
|
/**
|
|
239
248
|
* Scope for a single set of deployments from a set of Cloud Assembly Artifacts
|
|
@@ -243,13 +252,13 @@ export interface PreparedSdkForEnvironment {
|
|
|
243
252
|
export declare class Deployments {
|
|
244
253
|
private readonly props;
|
|
245
254
|
private readonly sdkProvider;
|
|
246
|
-
private readonly toolkitInfoCache;
|
|
247
255
|
private readonly sdkCache;
|
|
248
256
|
private readonly publisherCache;
|
|
257
|
+
private readonly environmentResources;
|
|
249
258
|
constructor(props: DeploymentsProps);
|
|
250
259
|
readCurrentTemplateWithNestedStacks(rootStackArtifact: cxapi.CloudFormationStackArtifact, retrieveProcessedTemplate?: boolean): Promise<Template>;
|
|
251
260
|
readCurrentTemplate(stackArtifact: cxapi.CloudFormationStackArtifact): Promise<Template>;
|
|
252
|
-
resourceIdentifierSummaries(stackArtifact: cxapi.CloudFormationStackArtifact
|
|
261
|
+
resourceIdentifierSummaries(stackArtifact: cxapi.CloudFormationStackArtifact): Promise<ResourceIdentifierSummaries>;
|
|
253
262
|
deployStack(options: DeployStackOptions): Promise<DeployStackResult | undefined>;
|
|
254
263
|
destroyStack(options: DestroyStackOptions): Promise<void>;
|
|
255
264
|
stackExists(options: StackExistsOptions): Promise<boolean>;
|
|
@@ -283,10 +292,6 @@ export declare class Deployments {
|
|
|
283
292
|
* function can then decide to use them or fallback to another role.
|
|
284
293
|
*/
|
|
285
294
|
prepareSdkWithLookupRoleFor(stack: cxapi.CloudFormationStackArtifact): Promise<PreparedSdkWithLookupRoleForEnvironment>;
|
|
286
|
-
/**
|
|
287
|
-
* Look up the toolkit for a given environment, using a given SDK
|
|
288
|
-
*/
|
|
289
|
-
lookupToolkit(resolvedEnvironment: cxapi.Environment, sdk: ISDK, toolkitStackName?: string): Promise<ToolkitInfo>;
|
|
290
295
|
private prepareAndValidateAssets;
|
|
291
296
|
/**
|
|
292
297
|
* Build all assets in a manifest
|
|
@@ -314,8 +319,10 @@ export declare class Deployments {
|
|
|
314
319
|
isSingleAssetPublished(assetManifest: AssetManifest, asset: IManifestEntry, options: PublishStackAssetsOptions): Promise<boolean>;
|
|
315
320
|
/**
|
|
316
321
|
* Validate that the bootstrap stack has the right version for this stack
|
|
322
|
+
*
|
|
323
|
+
* Call into envResources.validateVersion, but prepend the stack name in case of failure.
|
|
317
324
|
*/
|
|
318
|
-
validateBootstrapStackVersion(stackName: string, requiresBootstrapStackVersion: number | undefined, bootstrapStackVersionSsmParameter: string | undefined,
|
|
325
|
+
validateBootstrapStackVersion(stackName: string, requiresBootstrapStackVersion: number | undefined, bootstrapStackVersionSsmParameter: string | undefined, envResources: EnvironmentResources): Promise<void>;
|
|
319
326
|
private cachedSdkForEnvironment;
|
|
320
327
|
private cachedPublisher;
|
|
321
328
|
}
|
package/cdk/deployments.js
CHANGED
|
@@ -6,11 +6,10 @@ import { debug, warning } from "sst-aws-cdk/lib/logging.js";
|
|
|
6
6
|
import { buildAssets, publishAssets, PublishingAws, EVENT_TO_LOGGER, } from "sst-aws-cdk/lib/util/asset-publishing.js";
|
|
7
7
|
import { Mode } from "sst-aws-cdk/lib/api/aws-auth/credentials.js";
|
|
8
8
|
import { deployStack, destroyStack, makeBodyParameterAndUpload, } from "./deploy-stack.js";
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
9
|
+
import { EnvironmentResourcesRegistry, } from "sst-aws-cdk/lib/api/environment-resources.js";
|
|
10
|
+
import { loadCurrentTemplateWithNestedStacks, loadCurrentTemplate, flattenNestedStackNames, } from "sst-aws-cdk/lib/api/nested-stack-helpers.js";
|
|
11
11
|
import { CloudFormationStack, } from "sst-aws-cdk/lib/api/util/cloudformation.js";
|
|
12
12
|
import { replaceEnvPlaceholders } from "sst-aws-cdk/lib/api/util/placeholders.js";
|
|
13
|
-
import { callWithRetry } from "./util.js";
|
|
14
13
|
/**
|
|
15
14
|
* Scope for a single set of deployments from a set of Cloud Assembly Artifacts
|
|
16
15
|
*
|
|
@@ -19,17 +18,22 @@ import { callWithRetry } from "./util.js";
|
|
|
19
18
|
export class Deployments {
|
|
20
19
|
props;
|
|
21
20
|
sdkProvider;
|
|
22
|
-
toolkitInfoCache = new Map();
|
|
23
21
|
sdkCache = new Map();
|
|
24
22
|
publisherCache = new Map();
|
|
23
|
+
environmentResources;
|
|
25
24
|
constructor(props) {
|
|
26
25
|
this.props = props;
|
|
27
26
|
this.sdkProvider = props.sdkProvider;
|
|
27
|
+
this.environmentResources = new EnvironmentResourcesRegistry(props.toolkitStackName);
|
|
28
28
|
}
|
|
29
29
|
async readCurrentTemplateWithNestedStacks(rootStackArtifact, retrieveProcessedTemplate = false) {
|
|
30
30
|
const sdk = (await this.prepareSdkWithLookupOrDeployRole(rootStackArtifact))
|
|
31
31
|
.stackSdk;
|
|
32
|
-
|
|
32
|
+
const templateWithNestedStacks = await loadCurrentTemplateWithNestedStacks(rootStackArtifact, sdk, retrieveProcessedTemplate);
|
|
33
|
+
return {
|
|
34
|
+
deployedTemplate: templateWithNestedStacks.deployedTemplate,
|
|
35
|
+
nestedStackCount: flattenNestedStackNames(templateWithNestedStacks.nestedStackNames).length,
|
|
36
|
+
};
|
|
33
37
|
}
|
|
34
38
|
async readCurrentTemplate(stackArtifact) {
|
|
35
39
|
debug(`Reading existing template for stack ${stackArtifact.displayName}.`);
|
|
@@ -37,15 +41,14 @@ export class Deployments {
|
|
|
37
41
|
.stackSdk;
|
|
38
42
|
return loadCurrentTemplate(stackArtifact, sdk);
|
|
39
43
|
}
|
|
40
|
-
async resourceIdentifierSummaries(stackArtifact
|
|
44
|
+
async resourceIdentifierSummaries(stackArtifact) {
|
|
41
45
|
debug(`Retrieving template summary for stack ${stackArtifact.displayName}.`);
|
|
42
46
|
// Currently, needs to use `deploy-role` since it may need to read templates in the staging
|
|
43
47
|
// 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);
|
|
48
|
+
const { stackSdk, resolvedEnvironment, envResources } = await this.prepareSdkFor(stackArtifact, undefined, Mode.ForReading);
|
|
45
49
|
const cfn = stackSdk.cloudFormation();
|
|
46
|
-
const toolkitInfo = await this.lookupToolkit(resolvedEnvironment, stackSdk, toolkitStackName);
|
|
47
50
|
// Upload the template, if necessary, before passing it to CFN
|
|
48
|
-
const cfnParam = await makeBodyParameterAndUpload(stackArtifact, resolvedEnvironment,
|
|
51
|
+
const cfnParam = await makeBodyParameterAndUpload(stackArtifact, resolvedEnvironment, envResources, this.sdkProvider, stackSdk);
|
|
49
52
|
const response = await cfn.getTemplateSummary(cfnParam).promise();
|
|
50
53
|
if (!response.ResourceIdentifierSummaries) {
|
|
51
54
|
debug('GetTemplateSummary API call did not return "ResourceIdentifierSummaries"');
|
|
@@ -64,10 +67,9 @@ export class Deployments {
|
|
|
64
67
|
execute: options.execute,
|
|
65
68
|
};
|
|
66
69
|
}
|
|
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));
|
|
70
|
+
const { stackSdk, resolvedEnvironment, cloudFormationRoleArn, envResources, } = await this.prepareSdkFor(options.stack, options.roleArn, Mode.ForWriting);
|
|
69
71
|
// Do a verification of the bootstrap stack version
|
|
70
|
-
await this.validateBootstrapStackVersion(options.stack.stackName, options.stack.requiresBootstrapStackVersion, options.stack.bootstrapStackVersionSsmParameter,
|
|
72
|
+
await this.validateBootstrapStackVersion(options.stack.stackName, options.stack.requiresBootstrapStackVersion, options.stack.bootstrapStackVersionSsmParameter, envResources);
|
|
71
73
|
// Deploy assets
|
|
72
74
|
const assetArtifacts = options.stack.dependencies.filter(cxapi.AssetManifestArtifact.isAssetManifestArtifact);
|
|
73
75
|
for (const asset of assetArtifacts) {
|
|
@@ -89,7 +91,7 @@ export class Deployments {
|
|
|
89
91
|
sdkProvider: this.sdkProvider,
|
|
90
92
|
roleArn: cloudFormationRoleArn,
|
|
91
93
|
reuseAssets: options.reuseAssets,
|
|
92
|
-
|
|
94
|
+
envResources,
|
|
93
95
|
tags: options.tags,
|
|
94
96
|
deploymentMethod,
|
|
95
97
|
force: options.force,
|
|
@@ -129,6 +131,7 @@ export class Deployments {
|
|
|
129
131
|
return {
|
|
130
132
|
resolvedEnvironment: result.resolvedEnvironment,
|
|
131
133
|
stackSdk: result.sdk,
|
|
134
|
+
envResources: result.envResources,
|
|
132
135
|
};
|
|
133
136
|
}
|
|
134
137
|
}
|
|
@@ -164,6 +167,7 @@ export class Deployments {
|
|
|
164
167
|
stackSdk: stackSdk.sdk,
|
|
165
168
|
resolvedEnvironment,
|
|
166
169
|
cloudFormationRoleArn: arns.cloudFormationRoleArn,
|
|
170
|
+
envResources: this.environmentResources.for(resolvedEnvironment, stackSdk.sdk),
|
|
167
171
|
};
|
|
168
172
|
}
|
|
169
173
|
/**
|
|
@@ -198,11 +202,12 @@ export class Deployments {
|
|
|
198
202
|
assumeRoleArn: arns.lookupRoleArn,
|
|
199
203
|
assumeRoleExternalId: stack.lookupRole?.assumeRoleExternalId,
|
|
200
204
|
});
|
|
205
|
+
const envResources = this.environmentResources.for(resolvedEnvironment, stackSdk.sdk);
|
|
201
206
|
// if we succeed in assuming the lookup role, make sure we have the correct bootstrap stack version
|
|
202
207
|
if (stackSdk.didAssumeRole &&
|
|
203
208
|
stack.lookupRole?.bootstrapStackVersionSsmParameter &&
|
|
204
209
|
stack.lookupRole.requiresBootstrapStackVersion) {
|
|
205
|
-
const version = await
|
|
210
|
+
const version = await envResources.versionFromSsmParameter(stack.lookupRole.bootstrapStackVersionSsmParameter);
|
|
206
211
|
if (version < stack.lookupRole.requiresBootstrapStackVersion) {
|
|
207
212
|
throw new Error(`Bootstrap stack version '${stack.lookupRole.requiresBootstrapStackVersion}' is required, found version '${version}'.`);
|
|
208
213
|
}
|
|
@@ -213,7 +218,7 @@ export class Deployments {
|
|
|
213
218
|
stack.lookupRole?.requiresBootstrapStackVersion) {
|
|
214
219
|
warning(upgradeMessage);
|
|
215
220
|
}
|
|
216
|
-
return { ...stackSdk, resolvedEnvironment };
|
|
221
|
+
return { ...stackSdk, resolvedEnvironment, envResources };
|
|
217
222
|
}
|
|
218
223
|
catch (e) {
|
|
219
224
|
debug(e);
|
|
@@ -226,26 +231,11 @@ export class Deployments {
|
|
|
226
231
|
throw e;
|
|
227
232
|
}
|
|
228
233
|
}
|
|
229
|
-
/**
|
|
230
|
-
* Look up the toolkit for a given environment, using a given SDK
|
|
231
|
-
*/
|
|
232
|
-
async lookupToolkit(resolvedEnvironment, sdk, toolkitStackName) {
|
|
233
|
-
const key = `${resolvedEnvironment.account}:${resolvedEnvironment.region}:${toolkitStackName}`;
|
|
234
|
-
const existing = this.toolkitInfoCache.get(key);
|
|
235
|
-
if (existing) {
|
|
236
|
-
return existing;
|
|
237
|
-
}
|
|
238
|
-
const ret = await ToolkitInfo.lookup(resolvedEnvironment, sdk, toolkitStackName);
|
|
239
|
-
this.toolkitInfoCache.set(key, ret);
|
|
240
|
-
return ret;
|
|
241
|
-
}
|
|
242
234
|
async prepareAndValidateAssets(asset, options) {
|
|
243
|
-
const {
|
|
244
|
-
|
|
245
|
-
const stackEnv = await this.sdkProvider.resolveEnvironment(options.stack.environment);
|
|
246
|
-
await this.validateBootstrapStackVersion(options.stack.stackName, asset.requiresBootstrapStackVersion, asset.bootstrapStackVersionSsmParameter, toolkitInfo);
|
|
235
|
+
const { envResources } = await this.prepareSdkFor(options.stack, options.roleArn, Mode.ForWriting);
|
|
236
|
+
await this.validateBootstrapStackVersion(options.stack.stackName, asset.requiresBootstrapStackVersion, asset.bootstrapStackVersionSsmParameter, envResources);
|
|
247
237
|
const manifest = AssetManifest.fromFile(asset.file);
|
|
248
|
-
return { manifest, stackEnv };
|
|
238
|
+
return { manifest, stackEnv: envResources.environment };
|
|
249
239
|
}
|
|
250
240
|
/**
|
|
251
241
|
* Build all assets in a manifest
|
|
@@ -270,10 +260,9 @@ export class Deployments {
|
|
|
270
260
|
*/
|
|
271
261
|
// eslint-disable-next-line max-len
|
|
272
262
|
async buildSingleAsset(assetArtifact, assetManifest, asset, options) {
|
|
273
|
-
const {
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
const publisher = this.cachedPublisher(assetManifest, stackEnv, options.stackName);
|
|
263
|
+
const { resolvedEnvironment, envResources } = await this.prepareSdkFor(options.stack, options.roleArn, Mode.ForWriting);
|
|
264
|
+
await this.validateBootstrapStackVersion(options.stack.stackName, assetArtifact.requiresBootstrapStackVersion, assetArtifact.bootstrapStackVersionSsmParameter, envResources);
|
|
265
|
+
const publisher = this.cachedPublisher(assetManifest, resolvedEnvironment, options.stackName);
|
|
277
266
|
await publisher.buildEntry(asset);
|
|
278
267
|
}
|
|
279
268
|
/**
|
|
@@ -296,13 +285,12 @@ export class Deployments {
|
|
|
296
285
|
}
|
|
297
286
|
/**
|
|
298
287
|
* Validate that the bootstrap stack has the right version for this stack
|
|
288
|
+
*
|
|
289
|
+
* Call into envResources.validateVersion, but prepend the stack name in case of failure.
|
|
299
290
|
*/
|
|
300
|
-
async validateBootstrapStackVersion(stackName, requiresBootstrapStackVersion, bootstrapStackVersionSsmParameter,
|
|
301
|
-
if (requiresBootstrapStackVersion === undefined) {
|
|
302
|
-
return;
|
|
303
|
-
}
|
|
291
|
+
async validateBootstrapStackVersion(stackName, requiresBootstrapStackVersion, bootstrapStackVersionSsmParameter, envResources) {
|
|
304
292
|
try {
|
|
305
|
-
await
|
|
293
|
+
await envResources.validateVersion(requiresBootstrapStackVersion, bootstrapStackVersionSsmParameter);
|
|
306
294
|
}
|
|
307
295
|
catch (e) {
|
|
308
296
|
throw new Error(`${stackName}: ${e.message}`);
|
package/cli/commands/bind.js
CHANGED
|
@@ -81,13 +81,10 @@ export const bind = (program) => program
|
|
|
81
81
|
});
|
|
82
82
|
async function buildApp() {
|
|
83
83
|
const [_metafile, sstConfig] = await Stacks.load(project.paths.config);
|
|
84
|
-
const cwd = process.cwd();
|
|
85
|
-
process.chdir(project.paths.root);
|
|
86
84
|
await Stacks.synth({
|
|
87
85
|
fn: sstConfig.stacks,
|
|
88
86
|
mode: "remove",
|
|
89
87
|
});
|
|
90
|
-
process.chdir(cwd);
|
|
91
88
|
}
|
|
92
89
|
function isInSsrSite() {
|
|
93
90
|
const cwd = process.cwd();
|
package/cli/sst.js
CHANGED
|
@@ -3,7 +3,6 @@ import { blue, red } from "colorette";
|
|
|
3
3
|
import { program } from "./program.js";
|
|
4
4
|
import { SilentError, VisibleError } from "../error.js";
|
|
5
5
|
import { useSpinners } from "./spinner.js";
|
|
6
|
-
import { Logger } from "../logger.js";
|
|
7
6
|
import dotenv from "dotenv";
|
|
8
7
|
dotenv.config({
|
|
9
8
|
override: true,
|
|
@@ -48,7 +47,6 @@ if ("setSourceMapsEnabled" in process) {
|
|
|
48
47
|
}
|
|
49
48
|
process.removeAllListeners("uncaughtException");
|
|
50
49
|
process.on("uncaughtException", (err) => {
|
|
51
|
-
Logger.debug(err);
|
|
52
50
|
const spinners = useSpinners();
|
|
53
51
|
for (const spinner of spinners) {
|
|
54
52
|
if (spinner.isSpinning)
|
|
@@ -68,7 +66,7 @@ process.on("uncaughtException", (err) => {
|
|
|
68
66
|
});
|
|
69
67
|
// Check Node version
|
|
70
68
|
const nodeVersion = process.versions.node;
|
|
71
|
-
if (Number(nodeVersion.split(".")[0]) <
|
|
72
|
-
throw new VisibleError(`Node.js version ${nodeVersion} is not supported by SST. Please upgrade to Node.js
|
|
69
|
+
if (Number(nodeVersion.split(".")[0]) < 18) {
|
|
70
|
+
throw new VisibleError(`Node.js version ${nodeVersion} is not supported by SST. Please upgrade to Node.js 18 or later.`);
|
|
73
71
|
}
|
|
74
72
|
program.parse();
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"sideEffects": false,
|
|
3
3
|
"name": "sst",
|
|
4
|
-
"version": "2.
|
|
4
|
+
"version": "2.29.2",
|
|
5
5
|
"bin": {
|
|
6
6
|
"sst": "cli/sst.js"
|
|
7
7
|
},
|
|
@@ -87,7 +87,7 @@
|
|
|
87
87
|
"ora": "^6.1.2",
|
|
88
88
|
"react": "18.2.0",
|
|
89
89
|
"remeda": "^1.3.0",
|
|
90
|
-
"sst-aws-cdk": "2.
|
|
90
|
+
"sst-aws-cdk": "2.95.1",
|
|
91
91
|
"tree-kill": "^1.2.2",
|
|
92
92
|
"undici": "^5.12.0",
|
|
93
93
|
"uuid": "^9.0.0",
|
|
@@ -119,7 +119,7 @@
|
|
|
119
119
|
"@types/ws": "^8.5.3",
|
|
120
120
|
"@types/yargs": "^17.0.13",
|
|
121
121
|
"archiver": "^5.3.1",
|
|
122
|
-
"astro-sst": "2.
|
|
122
|
+
"astro-sst": "2.29.2",
|
|
123
123
|
"tsx": "^3.12.1",
|
|
124
124
|
"typescript": "^5.2.2",
|
|
125
125
|
"vitest": "^0.33.0"
|
package/runtime/handlers.js
CHANGED
|
@@ -114,6 +114,7 @@ export const useRuntimeHandlers = lazy(() => {
|
|
|
114
114
|
export const useFunctionBuilder = lazy(() => {
|
|
115
115
|
const artifacts = new Map();
|
|
116
116
|
const handlers = useRuntimeHandlers();
|
|
117
|
+
const semaphore = new Semaphore(4);
|
|
117
118
|
const result = {
|
|
118
119
|
artifact: (functionID) => {
|
|
119
120
|
if (artifacts.has(functionID))
|
|
@@ -121,13 +122,19 @@ export const useFunctionBuilder = lazy(() => {
|
|
|
121
122
|
return result.build(functionID);
|
|
122
123
|
},
|
|
123
124
|
build: async (functionID) => {
|
|
124
|
-
const
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
125
|
+
const unlock = await semaphore.lock();
|
|
126
|
+
try {
|
|
127
|
+
const result = await handlers.build(functionID, "start");
|
|
128
|
+
if (!result)
|
|
129
|
+
return;
|
|
130
|
+
if (result.type === "error")
|
|
131
|
+
return;
|
|
132
|
+
artifacts.set(functionID, result);
|
|
133
|
+
return artifacts.get(functionID);
|
|
134
|
+
}
|
|
135
|
+
finally {
|
|
136
|
+
unlock();
|
|
137
|
+
}
|
|
131
138
|
},
|
|
132
139
|
};
|
|
133
140
|
const watcher = useWatcher();
|
|
@@ -149,3 +156,30 @@ export const useFunctionBuilder = lazy(() => {
|
|
|
149
156
|
});
|
|
150
157
|
return result;
|
|
151
158
|
});
|
|
159
|
+
class Semaphore {
|
|
160
|
+
queue = [];
|
|
161
|
+
locked = 0;
|
|
162
|
+
maxLocks;
|
|
163
|
+
constructor(maxLocks = 1) {
|
|
164
|
+
this.maxLocks = maxLocks;
|
|
165
|
+
}
|
|
166
|
+
lock() {
|
|
167
|
+
return new Promise((resolve) => {
|
|
168
|
+
const unlock = () => {
|
|
169
|
+
this.locked--;
|
|
170
|
+
const next = this.queue.shift();
|
|
171
|
+
if (next) {
|
|
172
|
+
this.locked++;
|
|
173
|
+
next(unlock);
|
|
174
|
+
}
|
|
175
|
+
};
|
|
176
|
+
if (this.locked < this.maxLocks) {
|
|
177
|
+
this.locked++;
|
|
178
|
+
resolve(unlock);
|
|
179
|
+
}
|
|
180
|
+
else {
|
|
181
|
+
this.queue.push(unlock);
|
|
182
|
+
}
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
}
|
package/stacks/build.js
CHANGED
|
@@ -14,7 +14,13 @@ export async function load(input, shallow) {
|
|
|
14
14
|
const root = await findAbove(input, "package.json");
|
|
15
15
|
if (!root)
|
|
16
16
|
throw new VisibleError("Could not find a package.json file");
|
|
17
|
-
|
|
17
|
+
// Create the output file in the current working directory. This is because
|
|
18
|
+
// the sst cli command might not always run from where `sst.config.ts` is. When
|
|
19
|
+
// running from another workspace (ie. running `sst bind` in frontend workspace),
|
|
20
|
+
// the "sst" package imported from within the output file might resolve to a
|
|
21
|
+
// different "sst" package than the one resolved by the sst cli command. This
|
|
22
|
+
// will cause issues like "Project not initialized".
|
|
23
|
+
const outfile = path.join(process.cwd(), `.${parsed.name}.${Date.now()}.mjs`);
|
|
18
24
|
const pkg = JSON.parse(await fs.readFile(path.join(root, "package.json")).then((x) => x.toString()));
|
|
19
25
|
try {
|
|
20
26
|
// Logger.debug("running esbuild on", input);
|
package/stacks/synth.js
CHANGED
|
@@ -11,62 +11,75 @@ export async function synth(opts) {
|
|
|
11
11
|
const cxapi = await import("@aws-cdk/cx-api");
|
|
12
12
|
const { Configuration } = await import("sst-aws-cdk/lib/settings.js");
|
|
13
13
|
const project = useProject();
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
await
|
|
63
|
-
|
|
64
|
-
|
|
14
|
+
const cwd = process.cwd();
|
|
15
|
+
process.chdir(project.paths.root);
|
|
16
|
+
try {
|
|
17
|
+
return await synthInRoot();
|
|
18
|
+
}
|
|
19
|
+
catch (e) {
|
|
20
|
+
throw e;
|
|
21
|
+
}
|
|
22
|
+
finally {
|
|
23
|
+
process.chdir(cwd);
|
|
24
|
+
}
|
|
25
|
+
async function synthInRoot() {
|
|
26
|
+
const identity = await useSTSIdentity();
|
|
27
|
+
opts = {
|
|
28
|
+
...opts,
|
|
29
|
+
buildDir: opts.buildDir || path.join(project.paths.out, "dist"),
|
|
30
|
+
};
|
|
31
|
+
await fs.rm(opts.buildDir, { recursive: true, force: true });
|
|
32
|
+
await fs.mkdir(opts.buildDir, { recursive: true });
|
|
33
|
+
/*
|
|
34
|
+
console.log(JSON.stringify(cfg.context));
|
|
35
|
+
const executable = new CloudExecutable({
|
|
36
|
+
sdkProvider: await useAWSProvider(),
|
|
37
|
+
configuration: cfg,
|
|
38
|
+
synthesizer: async () => app.synth() as any
|
|
39
|
+
});
|
|
40
|
+
const { assembly } = await executable.synthesize(true);
|
|
41
|
+
*/
|
|
42
|
+
const cfg = new Configuration();
|
|
43
|
+
await cfg.load();
|
|
44
|
+
let previous = new Set();
|
|
45
|
+
while (true) {
|
|
46
|
+
const app = new App({
|
|
47
|
+
account: identity.Account,
|
|
48
|
+
stage: project.config.stage,
|
|
49
|
+
name: project.config.name,
|
|
50
|
+
region: project.config.region,
|
|
51
|
+
mode: opts.mode,
|
|
52
|
+
debugIncreaseTimeout: opts.increaseTimeout,
|
|
53
|
+
debugScriptVersion: opts.scriptVersion,
|
|
54
|
+
isActiveStack: opts.isActiveStack,
|
|
55
|
+
}, {
|
|
56
|
+
outdir: opts.buildDir,
|
|
57
|
+
context: {
|
|
58
|
+
...cfg.context.all,
|
|
59
|
+
[cxapi.PATH_METADATA_ENABLE_CONTEXT]: project.config.cdk?.pathMetadata ?? false,
|
|
60
|
+
},
|
|
61
|
+
});
|
|
62
|
+
await opts.fn(app);
|
|
63
|
+
await app.finish();
|
|
64
|
+
const assembly = app.synth();
|
|
65
|
+
Logger.debug(assembly.manifest.missing);
|
|
66
|
+
const { missing } = assembly.manifest;
|
|
67
|
+
const provider = await useAWSProvider();
|
|
68
|
+
if (missing && missing.length) {
|
|
69
|
+
const next = missing.map((x) => x.key);
|
|
70
|
+
if (next.length === previous.size && next.every((x) => previous.has(x)))
|
|
71
|
+
throw new VisibleError(formatErrorMessage(next.join("")));
|
|
72
|
+
Logger.debug("Looking up context for:", next, "Previous:", previous);
|
|
73
|
+
previous = new Set(next);
|
|
74
|
+
await contextproviders.provideContextValues(missing, cfg.context, provider);
|
|
75
|
+
if (cfg.context.keys.length) {
|
|
76
|
+
await cfg.saveContext();
|
|
77
|
+
}
|
|
78
|
+
continue;
|
|
65
79
|
}
|
|
66
|
-
|
|
80
|
+
Logger.debug("Finished synthesizing");
|
|
81
|
+
return assembly;
|
|
67
82
|
}
|
|
68
|
-
Logger.debug("Finished synthesizing");
|
|
69
|
-
return assembly;
|
|
70
83
|
}
|
|
71
84
|
}
|
|
72
85
|
function formatErrorMessage(message) {
|