Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ import {
shouldImportResources,
getResourcesToImport,
getEnvironmentName,
getChangeSetName,
chooseOptionalFlagSuggestion as chooseOptionalFlagMode,
getTags,
getOnStackFailure,
Expand Down Expand Up @@ -111,14 +110,8 @@ export function executeChangeSetCommand(client: LanguageClient, coordinator: Sta
}

export function deleteChangeSetCommand(client: LanguageClient) {
return commands.registerCommand(commandKey('stacks.deleteChangeSet'), async (params?: ChangeSetReference) => {
return commands.registerCommand(commandKey('stacks.deleteChangeSet'), async (params: ChangeSetReference) => {
try {
params = params ?? (await promptForChangeSetReference())

if (!params) {
return
}

const changeSetDeletion = new ChangeSetDeletion(params.stackName, params.changeSetName, client)

await changeSetDeletion.delete()
Expand All @@ -129,14 +122,8 @@ export function deleteChangeSetCommand(client: LanguageClient) {
}

export function viewChangeSetCommand(client: LanguageClient, diffProvider: DiffWebviewProvider) {
return commands.registerCommand(commandKey('stacks.viewChangeSet'), async (params?: ChangeSetReference) => {
return commands.registerCommand(commandKey('stacks.viewChangeSet'), async (params: ChangeSetReference) => {
try {
params = params ?? (await promptForChangeSetReference())

if (!params) {
return
}

const describeChangeSetResult = await describeChangeSet(client, {
changeSetName: params.changeSetName,
stackName: params.stackName,
Expand All @@ -157,16 +144,6 @@ export function viewChangeSetCommand(client: LanguageClient, diffProvider: DiffW
})
}

async function promptForChangeSetReference(): Promise<ChangeSetReference | undefined> {
const stackName = await getStackName()
const changeSetName = await getChangeSetName()
if (!stackName || !changeSetName) {
return undefined
}

return { stackName: stackName, changeSetName: changeSetName }
}

export function deployTemplateCommand(
client: LanguageClient,
diffProvider: DiffWebviewProvider,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,20 +15,20 @@ import {
import { deleteChangeSet, describeChangeSetDeletionStatus, getChangeSetDeletionStatus } from './stackActionApi'
import { createChangeSetDeletionParams } from './stackActionUtil'
import { getLogger } from '../../../../shared/logger/logger'
import { extractErrorMessage } from '../../utils'
import { commandKey, extractErrorMessage } from '../../utils'
import { commands } from 'vscode'
import globals from '../../../../shared/extensionGlobals'

export class ChangeSetDeletion {
private readonly id: string
private readonly stackName: string
private readonly changeSetName: string
private readonly client: LanguageClient
private status: StackActionPhase | undefined

constructor(stackName: string, changeSetName: string, client: LanguageClient) {
constructor(
private readonly stackName: string,
private readonly changeSetName: string,
private readonly client: LanguageClient
) {
this.id = uuidv4()
this.stackName = stackName
this.changeSetName = changeSetName
this.client = client
}

async delete() {
Expand All @@ -38,7 +38,7 @@ export class ChangeSetDeletion {
}

private pollForProgress() {
const interval = setInterval(() => {
const interval = globals.clock.setInterval(() => {
getChangeSetDeletionStatus(this.client, { id: this.id })
.then(async (deletionResult) => {
if (deletionResult.phase === this.status) {
Expand Down Expand Up @@ -66,7 +66,8 @@ export class ChangeSetDeletion {
describeDeplomentStatusResult.FailureReason ?? 'No failure reason provided'
)
}
clearInterval(interval)
void commands.executeCommand(commandKey('stacks.refresh'))
globals.clock.clearInterval(interval)
break
case StackActionPhase.DELETION_FAILED: {
const describeDeplomentStatusResult = await describeChangeSetDeletionStatus(this.client, {
Expand All @@ -77,15 +78,17 @@ export class ChangeSetDeletion {
this.stackName,
describeDeplomentStatusResult.FailureReason ?? 'No failure reason provided'
)
clearInterval(interval)
void commands.executeCommand(commandKey('stacks.refresh'))
globals.clock.clearInterval(interval)
break
}
}
})
.catch(async (error) => {
getLogger().error(`Error polling for deletion status: ${error}`)
showErrorMessage(`Error polling for deletion status: ${extractErrorMessage(error)}`)
clearInterval(interval)
void commands.executeCommand(commandKey('stacks.refresh'))
globals.clock.clearInterval(interval)
})
}, 1000)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
/*!
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/

import assert from 'assert'
import { SinonSandbox, SinonStub, createSandbox } from 'sinon'
import { commands } from 'vscode'
import { ChangeSetDeletion } from '../../../../../awsService/cloudformation/stacks/actions/changeSetDeletionWorkflow'
import {
StackActionPhase,
StackActionState,
} from '../../../../../awsService/cloudformation/stacks/actions/stackActionRequestType'
import { commandKey } from '../../../../../awsService/cloudformation/utils'
import { globals } from '../../../../../shared'

describe('ChangeSetDeletion', function () {
let sandbox: SinonSandbox

beforeEach(function () {
sandbox = createSandbox()
})

afterEach(function () {
sandbox.restore()
})

describe('delete', function () {
let mockClient: any
let executeCommandStub: SinonStub
let getChangeSetDeletionStatusStub: SinonStub
let describeChangeSetDeletionStatusStub: SinonStub

beforeEach(function () {
mockClient = { sendRequest: sandbox.stub().resolves({}) }
executeCommandStub = sandbox.stub(commands, 'executeCommand').resolves()

const stackActionApi = require('../../../../../awsService/cloudformation/stacks/actions/stackActionApi')
getChangeSetDeletionStatusStub = sandbox.stub(stackActionApi, 'getChangeSetDeletionStatus')
describeChangeSetDeletionStatusStub = sandbox.stub(stackActionApi, 'describeChangeSetDeletionStatus')
sandbox.stub(stackActionApi, 'deleteChangeSet').resolves()

sandbox.stub(globals.clock, 'setInterval').callsFake((callback: () => void) => {
setImmediate(() => callback())
return 1 as any
})
sandbox.stub(globals.clock, 'clearInterval')
})

it('should call refresh command after successful deletion', async function () {
getChangeSetDeletionStatusStub.resolves({
phase: StackActionPhase.DELETION_COMPLETE,
state: StackActionState.SUCCESSFUL,
})

const deletion = new ChangeSetDeletion('test-stack', 'test-changeset', mockClient)
await deletion.delete()
await new Promise((resolve) => setImmediate(resolve))

assert.ok(executeCommandStub.calledWith(commandKey('stacks.refresh')))
})

it('should call refresh command after failed deletion', async function () {
getChangeSetDeletionStatusStub.resolves({ phase: StackActionPhase.DELETION_FAILED })
describeChangeSetDeletionStatusStub.resolves({ FailureReason: 'Test failure' })

const deletion = new ChangeSetDeletion('test-stack', 'test-changeset', mockClient)
await deletion.delete()
await new Promise((resolve) => setImmediate(resolve))

assert.ok(executeCommandStub.calledWith(commandKey('stacks.refresh')))
})

it('should not call refresh command when polling encounters error', async function () {
getChangeSetDeletionStatusStub.rejects(new Error('Polling error'))

const deletion = new ChangeSetDeletion('test-stack', 'test-changeset', mockClient)
await deletion.delete()
await new Promise((resolve) => setImmediate(resolve))

assert.ok(executeCommandStub.calledWith(commandKey('stacks.refresh')))
})
})
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"type": "Bug Fix",
"description": "Refresh stacks after change set deletion"
}
Loading