sst 2.14.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.
@@ -1,267 +0,0 @@
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 { AssetManifest } from "cdk-assets";
4
- import { debug, warning } from "sst-aws-cdk/lib/logging.js";
5
- import { buildAssets, publishAssets, } from "sst-aws-cdk/lib/util/asset-publishing.js";
6
- import { Mode } from "sst-aws-cdk/lib/api/aws-auth/credentials.js";
7
- import { deployStack, destroyStack, makeBodyParameterAndUpload, } from "./deploy-stack.js";
8
- import { loadCurrentTemplateWithNestedStacks, loadCurrentTemplate, } from "sst-aws-cdk/lib/api/nested-stack-helpers.js";
9
- import { ToolkitInfo } from "sst-aws-cdk/lib/api/toolkit-info.js";
10
- import { CloudFormationStack, } from "sst-aws-cdk/lib/api/util/cloudformation.js";
11
- import { replaceEnvPlaceholders } from "sst-aws-cdk/lib/api/util/placeholders.js";
12
- import { callWithRetry } from "./util.js";
13
- /**
14
- * Try to use the bootstrap lookupRole. There are two scenarios that are handled here
15
- * 1. The lookup role may not exist (it was added in bootstrap stack version 7)
16
- * 2. The lookup role may not have the correct permissions (ReadOnlyAccess was added in
17
- * bootstrap stack version 8)
18
- *
19
- * In the case of 1 (lookup role doesn't exist) `forEnvironment` will either:
20
- * 1. Return the default credentials if the default credentials are for the stack account
21
- * 2. Throw an error if the default credentials are not for the stack account.
22
- *
23
- * If we successfully assume the lookup role we then proceed to 2 and check whether the bootstrap
24
- * stack version is valid. If it is not we throw an error which should be handled in the calling
25
- * function (and fallback to use a different role, etc)
26
- *
27
- * If we do not successfully assume the lookup role, but do get back the default credentials
28
- * then return those and note that we are returning the default credentials. The calling
29
- * function can then decide to use them or fallback to another role.
30
- */
31
- export async function prepareSdkWithLookupRoleFor(sdkProvider, stack) {
32
- const resolvedEnvironment = await sdkProvider.resolveEnvironment(stack.environment);
33
- // Substitute any placeholders with information about the current environment
34
- const arns = await replaceEnvPlaceholders({
35
- lookupRoleArn: stack.lookupRole?.arn,
36
- }, resolvedEnvironment, sdkProvider);
37
- // try to assume the lookup role
38
- const warningMessage = `Could not assume ${arns.lookupRoleArn}, proceeding anyway.`;
39
- const upgradeMessage = `(To get rid of this warning, please upgrade to bootstrap version >= ${stack.lookupRole?.requiresBootstrapStackVersion})`;
40
- try {
41
- const stackSdk = await sdkProvider.forEnvironment(resolvedEnvironment, Mode.ForReading, {
42
- assumeRoleArn: arns.lookupRoleArn,
43
- assumeRoleExternalId: stack.lookupRole?.assumeRoleExternalId,
44
- });
45
- // if we succeed in assuming the lookup role, make sure we have the correct bootstrap stack version
46
- if (stackSdk.didAssumeRole &&
47
- stack.lookupRole?.bootstrapStackVersionSsmParameter &&
48
- stack.lookupRole.requiresBootstrapStackVersion) {
49
- const version = await ToolkitInfo.versionFromSsmParameter(stackSdk.sdk, stack.lookupRole.bootstrapStackVersionSsmParameter);
50
- if (version < stack.lookupRole.requiresBootstrapStackVersion) {
51
- throw new Error(`Bootstrap stack version '${stack.lookupRole.requiresBootstrapStackVersion}' is required, found version '${version}'.`);
52
- }
53
- // we may not have assumed the lookup role because one was not provided
54
- // if that is the case then don't print the upgrade warning
55
- }
56
- else if (!stackSdk.didAssumeRole &&
57
- stack.lookupRole?.requiresBootstrapStackVersion) {
58
- warning(upgradeMessage);
59
- }
60
- return { ...stackSdk, resolvedEnvironment };
61
- }
62
- catch (e) {
63
- debug(e);
64
- // only print out the warnings if the lookupRole exists AND there is a required
65
- // bootstrap version, otherwise the warnings will print `undefined`
66
- if (stack.lookupRole && stack.lookupRole.requiresBootstrapStackVersion) {
67
- warning(warningMessage);
68
- warning(upgradeMessage);
69
- }
70
- throw e;
71
- }
72
- }
73
- /**
74
- * Helper class for CloudFormation deployments
75
- *
76
- * Looks us the right SDK and Bootstrap stack to deploy a given
77
- * stack artifact.
78
- */
79
- export class CloudFormationDeployments {
80
- sdkProvider;
81
- constructor(props) {
82
- this.sdkProvider = props.sdkProvider;
83
- }
84
- async readCurrentTemplateWithNestedStacks(rootStackArtifact, retrieveProcessedTemplate = false) {
85
- const sdk = (await this.prepareSdkWithLookupOrDeployRole(rootStackArtifact))
86
- .stackSdk;
87
- return (await loadCurrentTemplateWithNestedStacks(rootStackArtifact, sdk, retrieveProcessedTemplate)).deployedTemplate;
88
- }
89
- async readCurrentTemplate(stackArtifact) {
90
- debug(`Reading existing template for stack ${stackArtifact.displayName}.`);
91
- const sdk = (await this.prepareSdkWithLookupOrDeployRole(stackArtifact))
92
- .stackSdk;
93
- return loadCurrentTemplate(stackArtifact, sdk);
94
- }
95
- async resourceIdentifierSummaries(stackArtifact, toolkitStackName) {
96
- debug(`Retrieving template summary for stack ${stackArtifact.displayName}.`);
97
- // Currently, needs to use `deploy-role` since it may need to read templates in the staging
98
- // bucket which have been encrypted with a KMS key (and lookup-role may not read encrypted things)
99
- const { stackSdk, resolvedEnvironment } = await this.prepareSdkFor(stackArtifact, undefined, Mode.ForReading);
100
- const cfn = stackSdk.cloudFormation();
101
- const toolkitInfo = await ToolkitInfo.lookup(resolvedEnvironment, stackSdk, toolkitStackName);
102
- // Upload the template, if necessary, before passing it to CFN
103
- const cfnParam = await makeBodyParameterAndUpload(stackArtifact, resolvedEnvironment, toolkitInfo, this.sdkProvider, stackSdk);
104
- const response = await cfn.getTemplateSummary(cfnParam).promise();
105
- if (!response.ResourceIdentifierSummaries) {
106
- debug('GetTemplateSummary API call did not return "ResourceIdentifierSummaries"');
107
- }
108
- return response.ResourceIdentifierSummaries ?? [];
109
- }
110
- async deployStack(options) {
111
- let deploymentMethod = options.deploymentMethod;
112
- if (options.changeSetName || options.execute !== undefined) {
113
- if (deploymentMethod) {
114
- throw new Error("You cannot supply both 'deploymentMethod' and 'changeSetName/execute'. Supply one or the other.");
115
- }
116
- deploymentMethod = {
117
- method: "change-set",
118
- changeSetName: options.changeSetName,
119
- execute: options.execute,
120
- };
121
- }
122
- const { stackSdk, resolvedEnvironment, cloudFormationRoleArn } = await this.prepareSdkFor(options.stack, options.roleArn);
123
- const toolkitInfo = await callWithRetry(() => ToolkitInfo.lookup(resolvedEnvironment, stackSdk, options.toolkitStackName));
124
- // Publish any assets before doing the actual deploy (do not publish any assets on import operation)
125
- if (options.resourcesToImport === undefined) {
126
- await this.publishStackAssets(options.stack, toolkitInfo, {
127
- buildAssets: options.buildAssets ?? true,
128
- publishOptions: {
129
- quiet: options.quiet,
130
- parallel: options.assetParallelism,
131
- },
132
- });
133
- }
134
- // Do a verification of the bootstrap stack version
135
- await this.validateBootstrapStackVersion(options.stack.stackName, options.stack.requiresBootstrapStackVersion, options.stack.bootstrapStackVersionSsmParameter, toolkitInfo);
136
- return deployStack({
137
- stack: options.stack,
138
- noMonitor: true,
139
- resolvedEnvironment,
140
- deployName: options.deployName,
141
- notificationArns: options.notificationArns,
142
- quiet: options.quiet,
143
- sdk: stackSdk,
144
- sdkProvider: this.sdkProvider,
145
- roleArn: cloudFormationRoleArn,
146
- reuseAssets: options.reuseAssets,
147
- toolkitInfo,
148
- tags: options.tags,
149
- deploymentMethod,
150
- force: options.force,
151
- parameters: options.parameters,
152
- usePreviousParameters: options.usePreviousParameters,
153
- progress: options.progress,
154
- ci: options.ci,
155
- rollback: options.rollback,
156
- hotswap: options.hotswap,
157
- extraUserAgent: options.extraUserAgent,
158
- resourcesToImport: options.resourcesToImport,
159
- overrideTemplate: options.overrideTemplate,
160
- assetParallelism: options.assetParallelism,
161
- });
162
- }
163
- async destroyStack(options) {
164
- const { stackSdk, cloudFormationRoleArn: roleArn } = await this.prepareSdkFor(options.stack, options.roleArn);
165
- return destroyStack({
166
- sdk: stackSdk,
167
- roleArn,
168
- stack: options.stack,
169
- deployName: options.deployName,
170
- quiet: options.quiet,
171
- ci: options.ci,
172
- });
173
- }
174
- async stackExists(options) {
175
- const { stackSdk } = await this.prepareSdkFor(options.stack, undefined, Mode.ForReading);
176
- const stack = await CloudFormationStack.lookup(stackSdk.cloudFormation(), options.deployName ?? options.stack.stackName);
177
- return stack.exists;
178
- }
179
- async prepareSdkWithLookupOrDeployRole(stackArtifact) {
180
- // try to assume the lookup role
181
- try {
182
- const result = await prepareSdkWithLookupRoleFor(this.sdkProvider, stackArtifact);
183
- if (result.didAssumeRole) {
184
- return {
185
- resolvedEnvironment: result.resolvedEnvironment,
186
- stackSdk: result.sdk,
187
- };
188
- }
189
- }
190
- catch { }
191
- // fall back to the deploy role
192
- return this.prepareSdkFor(stackArtifact, undefined, Mode.ForReading);
193
- }
194
- /**
195
- * Get the environment necessary for touching the given stack
196
- *
197
- * Returns the following:
198
- *
199
- * - The resolved environment for the stack (no more 'unknown-account/unknown-region')
200
- * - SDK loaded with the right credentials for calling `CreateChangeSet`.
201
- * - The Execution Role that should be passed to CloudFormation.
202
- */
203
- async prepareSdkFor(stack, roleArn, mode = Mode.ForWriting) {
204
- if (!stack.environment) {
205
- throw new Error(`The stack ${stack.displayName} does not have an environment`);
206
- }
207
- const resolvedEnvironment = await this.sdkProvider.resolveEnvironment(stack.environment);
208
- // Substitute any placeholders with information about the current environment
209
- const arns = await replaceEnvPlaceholders({
210
- assumeRoleArn: stack.assumeRoleArn,
211
- // Use the override if given, otherwise use the field from the stack
212
- cloudFormationRoleArn: roleArn ?? stack.cloudFormationExecutionRoleArn,
213
- }, resolvedEnvironment, this.sdkProvider);
214
- const stackSdk = await this.sdkProvider.forEnvironment(resolvedEnvironment, mode, {
215
- assumeRoleArn: arns.assumeRoleArn,
216
- assumeRoleExternalId: stack.assumeRoleExternalId,
217
- });
218
- return {
219
- stackSdk: stackSdk.sdk,
220
- resolvedEnvironment,
221
- cloudFormationRoleArn: arns.cloudFormationRoleArn,
222
- };
223
- }
224
- /**
225
- * Build a stack's assets.
226
- */
227
- async buildStackAssets(options) {
228
- const { stackSdk, resolvedEnvironment } = await this.prepareSdkFor(options.stack, options.roleArn);
229
- const toolkitInfo = await ToolkitInfo.lookup(resolvedEnvironment, stackSdk, options.toolkitStackName);
230
- const stackEnv = await this.sdkProvider.resolveEnvironment(options.stack.environment);
231
- const assetArtifacts = options.stack.dependencies.filter(cxapi.AssetManifestArtifact.isAssetManifestArtifact);
232
- for (const assetArtifact of assetArtifacts) {
233
- await this.validateBootstrapStackVersion(options.stack.stackName, assetArtifact.requiresBootstrapStackVersion, assetArtifact.bootstrapStackVersionSsmParameter, toolkitInfo);
234
- const manifest = AssetManifest.fromFile(assetArtifact.file);
235
- await buildAssets(manifest, this.sdkProvider, stackEnv, options.buildOptions);
236
- }
237
- }
238
- /**
239
- * Publish all asset manifests that are referenced by the given stack
240
- */
241
- async publishStackAssets(stack, toolkitInfo, options = {}) {
242
- const stackEnv = await this.sdkProvider.resolveEnvironment(stack.environment);
243
- const assetArtifacts = stack.dependencies.filter(cxapi.AssetManifestArtifact.isAssetManifestArtifact);
244
- for (const assetArtifact of assetArtifacts) {
245
- await this.validateBootstrapStackVersion(stack.stackName, assetArtifact.requiresBootstrapStackVersion, assetArtifact.bootstrapStackVersionSsmParameter, toolkitInfo);
246
- const manifest = AssetManifest.fromFile(assetArtifact.file);
247
- await publishAssets(manifest, this.sdkProvider, stackEnv, {
248
- ...options.publishOptions,
249
- buildAssets: options.buildAssets ?? true,
250
- });
251
- }
252
- }
253
- /**
254
- * Validate that the bootstrap stack has the right version for this stack
255
- */
256
- async validateBootstrapStackVersion(stackName, requiresBootstrapStackVersion, bootstrapStackVersionSsmParameter, toolkitInfo) {
257
- if (requiresBootstrapStackVersion === undefined) {
258
- return;
259
- }
260
- try {
261
- await toolkitInfo.validateVersion(requiresBootstrapStackVersion, bootstrapStackVersionSsmParameter);
262
- }
263
- catch (e) {
264
- throw new Error(`${stackName}: ${e.message}`);
265
- }
266
- }
267
- }