Skip to content

Commit 630e156

Browse files
atennak1Akila Tennakoon
andauthored
feat(cloudformation): Shorten/simplify deployment prompts by promptin… (#8367)
…g for deployment mode first ## Problem When customer wants to validate/deploy in REVERT_DRIFT mode, they have to answer pre-requisite prompts correctly before they get prompted for deployment mode. ## Solution Ask for deployment mode first and don't ask the other prompts with incompatible answers. --- - Treat all work as PUBLIC. Private `feature/x` branches will not be squash-merged at release time. - Your code changes must meet the guidelines in [CONTRIBUTING.md](https://github.com/aws/aws-toolkit-vscode/blob/master/CONTRIBUTING.md#guidelines). - License: I confirm that my contribution is made under the terms of the Apache 2.0 license. --------- Co-authored-by: Akila Tennakoon <[email protected]>
1 parent 33cfc10 commit 630e156

File tree

4 files changed

+91
-36
lines changed

4 files changed

+91
-36
lines changed

packages/core/src/awsService/cloudformation/commands/cfnCommands.ts

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ import { Command } from 'vscode-languageclient/node'
2222
import * as yaml from 'js-yaml'
2323

2424
import { Deployment } from '../stacks/actions/deploymentWorkflow'
25-
import { Parameter, Capability, OnStackFailure, Stack } from '@aws-sdk/client-cloudformation'
25+
import { Parameter, Capability, OnStackFailure, Stack, StackStatus } from '@aws-sdk/client-cloudformation'
2626
import {
2727
getParameterValues,
2828
getStackName,
@@ -210,13 +210,13 @@ type OptionalFlagSelection = ChangeSetOptionalFlags & {
210210
shouldSaveOptions?: boolean
211211
}
212212

213-
function shouldPromptForDeploymentMode(
213+
function isRevertDriftEligible(
214214
stackDetails: Stack | undefined,
215215
importExistingResources: boolean | undefined,
216216
includeNestedStacks: boolean | undefined,
217217
onStackFailure: OnStackFailure | undefined
218218
): boolean {
219-
const isCreate = !stackDetails
219+
const isCreate = !stackDetails || stackDetails.StackStatus === StackStatus.REVIEW_IN_PROGRESS
220220
const hasDisableRollback = onStackFailure === OnStackFailure.DO_NOTHING
221221

222222
return !isCreate && !importExistingResources && !includeNestedStacks && !hasDisableRollback
@@ -247,7 +247,7 @@ export async function promptForOptionalFlags(
247247
// default to REVERT_DRIFT if possible because it's generally useful
248248
deploymentMode:
249249
fileFlags?.deploymentMode ??
250-
(shouldPromptForDeploymentMode(
250+
(isRevertDriftEligible(
251251
stackDetails,
252252
fileFlags?.importExistingResources,
253253
fileFlags?.includeNestedStacks,
@@ -260,21 +260,21 @@ export async function promptForOptionalFlags(
260260

261261
break
262262
case OptionalFlagMode.Input: {
263-
const onStackFailure = fileFlags?.onStackFailure ?? (await getOnStackFailure(!!stackDetails))
264-
const includeNestedStacks = fileFlags?.includeNestedStacks ?? (await getIncludeNestedStacks())
265-
const importExistingResources = fileFlags?.importExistingResources ?? (await getImportExistingResources())
266-
267-
let deploymentMode = fileFlags?.deploymentMode
268-
if (
269-
!deploymentMode &&
270-
shouldPromptForDeploymentMode(
271-
stackDetails,
272-
importExistingResources,
273-
includeNestedStacks,
274-
onStackFailure
275-
)
276-
) {
277-
deploymentMode = await getDeploymentMode()
263+
// Only available for UPDATE stack and is incompatible with the other options
264+
const deploymentMode =
265+
fileFlags?.deploymentMode ??
266+
(isRevertDriftEligible(stackDetails, undefined, undefined, undefined)
267+
? await getDeploymentMode()
268+
: undefined)
269+
270+
let onStackFailure: OnStackFailure | undefined
271+
let includeNestedStacks: boolean | undefined
272+
let importExistingResources: boolean | undefined
273+
274+
if (deploymentMode !== DeploymentMode.REVERT_DRIFT) {
275+
onStackFailure = fileFlags?.onStackFailure ?? (await getOnStackFailure(stackDetails))
276+
includeNestedStacks = fileFlags?.includeNestedStacks ?? (await getIncludeNestedStacks())
277+
importExistingResources = fileFlags?.importExistingResources ?? (await getImportExistingResources())
278278
}
279279

280280
optionalFlags = {

packages/core/src/awsService/cloudformation/ui/inputBox.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import {
99
validateParameterValue,
1010
validateChangeSetName,
1111
} from '../stacks/actions/stackActionInputValidation'
12-
import { Parameter, Capability, Tag, OnStackFailure } from '@aws-sdk/client-cloudformation'
12+
import { Parameter, Capability, Tag, OnStackFailure, Stack, StackStatus } from '@aws-sdk/client-cloudformation'
1313
import {
1414
TemplateParameter,
1515
ResourceToImport,
@@ -255,13 +255,13 @@ export async function getImportExistingResources(): Promise<boolean | undefined>
255255
)?.value
256256
}
257257

258-
export async function getOnStackFailure(stackExists?: boolean): Promise<OnStackFailure | undefined> {
258+
export async function getOnStackFailure(stackDetails?: Stack): Promise<OnStackFailure | undefined> {
259259
const options: Array<{ label: string; description: string; value: OnStackFailure }> = [
260260
{ label: 'Do nothing', description: 'Leave stack in failed state', value: OnStackFailure.DO_NOTHING },
261261
{ label: 'Rollback', description: 'Rollback to previous state', value: OnStackFailure.ROLLBACK },
262262
]
263263

264-
if (!stackExists) {
264+
if (!stackDetails || stackDetails.StackStatus === StackStatus.REVIEW_IN_PROGRESS) {
265265
// only a valid option for CREATE
266266
options.unshift({ label: 'Delete', description: 'Delete the stack on failure', value: OnStackFailure.DELETE })
267267
}
@@ -276,7 +276,7 @@ export async function getDeploymentMode(): Promise<DeploymentMode | undefined> {
276276
[
277277
{
278278
label: 'Revert Drift',
279-
description: 'Revert drift during deployment',
279+
description: 'Revert drift during deployment (disables dev friendly flags)',
280280
value: DeploymentMode.REVERT_DRIFT,
281281
},
282282
{ label: 'Standard', description: 'No special handling during deployment', value: undefined },

packages/core/src/test/awsService/cloudformation/commands/cfnCommands.test.ts

Lines changed: 64 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ describe('CfnCommands', function () {
112112

113113
it('should set shouldSaveOptions to true when input mode collects new values', async function () {
114114
chooseOptionalFlagModeStub.resolves(OptionalFlagMode.Input)
115+
getDeploymentModeStub.resolves(undefined)
115116
getOnStackFailureStub.resolves(OnStackFailure.DELETE)
116117
getIncludeNestedStacksStub.resolves(true)
117118
getTagsStub.resolves([{ Key: 'Environment', Value: 'prod' }])
@@ -129,53 +130,80 @@ describe('CfnCommands', function () {
129130
})
130131
})
131132

132-
it('should prompt for deployment mode on stack update when conditions are met', async function () {
133+
it('should not prompt for deployment mode for CREATE stack', async function () {
133134
chooseOptionalFlagModeStub.resolves(OptionalFlagMode.Input)
134135
getOnStackFailureStub.resolves(OnStackFailure.ROLLBACK)
135136
getIncludeNestedStacksStub.resolves(false)
136137
getTagsStub.resolves(undefined)
137138
getImportExistingResourcesStub.resolves(false)
138-
getDeploymentModeStub.resolves('INCREMENTAL')
139139

140-
const stackDetails = { StackName: 'test-stack' }
141-
const result = await promptForOptionalFlags(undefined, stackDetails as any)
140+
const result = await promptForOptionalFlags()
142141

143-
assert.ok(getDeploymentModeStub.calledOnce)
142+
assert.ok(getDeploymentModeStub.notCalled)
143+
assert.ok(getOnStackFailureStub.calledOnce)
144+
assert.ok(getIncludeNestedStacksStub.calledOnce)
145+
assert.ok(getImportExistingResourcesStub.calledOnce)
144146
assert.deepStrictEqual(result, {
145147
onStackFailure: OnStackFailure.ROLLBACK,
146148
includeNestedStacks: false,
147149
tags: undefined,
148150
importExistingResources: false,
149-
deploymentMode: 'INCREMENTAL',
151+
deploymentMode: undefined,
150152
shouldSaveOptions: true,
151153
})
152154
})
153155

154-
it('should not prompt for deployment mode on stack create', async function () {
156+
it('should not prompt for deployment mode when stack is REVIEW_IN_PROGRESS', async function () {
155157
chooseOptionalFlagModeStub.resolves(OptionalFlagMode.Input)
156158
getOnStackFailureStub.resolves(OnStackFailure.ROLLBACK)
157159
getIncludeNestedStacksStub.resolves(false)
158160
getTagsStub.resolves(undefined)
159161
getImportExistingResourcesStub.resolves(false)
160162

161-
const result = await promptForOptionalFlags()
163+
const stackDetails = { StackName: 'test-stack', StackStatus: 'REVIEW_IN_PROGRESS' as any }
164+
const result = await promptForOptionalFlags(undefined, stackDetails as any)
162165

163166
assert.ok(getDeploymentModeStub.notCalled)
164167
assert.strictEqual(result?.deploymentMode, undefined)
165168
})
166169

167-
it('should not prompt for deployment mode when importExistingResources is true', async function () {
170+
it('should prompt for deployment mode and other flags when not REVERT_DRIFT', async function () {
168171
chooseOptionalFlagModeStub.resolves(OptionalFlagMode.Input)
169-
getOnStackFailureStub.resolves(OnStackFailure.ROLLBACK)
170-
getIncludeNestedStacksStub.resolves(false)
172+
getDeploymentModeStub.resolves(undefined)
173+
getOnStackFailureStub.resolves(OnStackFailure.DELETE)
174+
getIncludeNestedStacksStub.resolves(true)
171175
getTagsStub.resolves(undefined)
172176
getImportExistingResourcesStub.resolves(true)
173177

178+
const stackDetails = { StackName: 'test-stack' }
179+
await promptForOptionalFlags(undefined, stackDetails as any)
180+
181+
assert.ok(getDeploymentModeStub.calledOnce)
182+
assert.ok(getOnStackFailureStub.calledOnce)
183+
assert.ok(getIncludeNestedStacksStub.calledOnce)
184+
assert.ok(getImportExistingResourcesStub.calledOnce)
185+
})
186+
187+
it('should skip other prompts when deploymentMode is REVERT_DRIFT', async function () {
188+
chooseOptionalFlagModeStub.resolves(OptionalFlagMode.Input)
189+
getDeploymentModeStub.resolves('REVERT_DRIFT')
190+
getTagsStub.resolves(undefined)
191+
174192
const stackDetails = { StackName: 'test-stack' }
175193
const result = await promptForOptionalFlags(undefined, stackDetails as any)
176194

177-
assert.ok(getDeploymentModeStub.notCalled)
178-
assert.strictEqual(result?.deploymentMode, undefined)
195+
assert.ok(getDeploymentModeStub.calledOnce)
196+
assert.ok(getOnStackFailureStub.notCalled)
197+
assert.ok(getIncludeNestedStacksStub.notCalled)
198+
assert.ok(getImportExistingResourcesStub.notCalled)
199+
assert.deepStrictEqual(result, {
200+
onStackFailure: undefined,
201+
includeNestedStacks: undefined,
202+
tags: undefined,
203+
importExistingResources: undefined,
204+
deploymentMode: 'REVERT_DRIFT',
205+
shouldSaveOptions: true,
206+
})
179207
})
180208

181209
it('should include deploymentMode from fileFlags in skip mode', async function () {
@@ -246,6 +274,29 @@ describe('CfnCommands', function () {
246274
})
247275
})
248276

277+
it('should not default to REVERT_DRIFT in skip mode when stack is REVIEW_IN_PROGRESS', async function () {
278+
chooseOptionalFlagModeStub.resolves(OptionalFlagMode.Skip)
279+
280+
const fileFlags = {
281+
onStackFailure: OnStackFailure.ROLLBACK,
282+
includeNestedStacks: false,
283+
tags: undefined,
284+
importExistingResources: false,
285+
}
286+
287+
const stackDetails = { StackName: 'test-stack', StackStatus: 'REVIEW_IN_PROGRESS' as any }
288+
const result = await promptForOptionalFlags(fileFlags, stackDetails as any)
289+
290+
assert.deepStrictEqual(result, {
291+
onStackFailure: OnStackFailure.ROLLBACK,
292+
includeNestedStacks: false,
293+
tags: undefined,
294+
importExistingResources: false,
295+
deploymentMode: undefined,
296+
shouldSaveOptions: false,
297+
})
298+
})
299+
249300
it('should not default to REVERT_DRIFT in skip mode when includeNestedStacks is true', async function () {
250301
chooseOptionalFlagModeStub.resolves(OptionalFlagMode.Skip)
251302

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"type": "Feature",
3+
"description": "CloudFormation: Shorten/simplify deployment prompts by prompting for deployment mode first"
4+
}

0 commit comments

Comments
 (0)