Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(AWS Deploy): Handle error for abnormal CloudFormation stack state during AWS deployment #11863

Merged
merged 7 commits into from Mar 29, 2023
16 changes: 16 additions & 0 deletions lib/plugins/aws/deploy/lib/create-stack.js
Expand Up @@ -88,6 +88,22 @@ module.exports = {
this.provider.disableTransferAccelerationForCurrentDeploy();
}
}

const inactiveStateNames = new Set(['REVIEW_IN_PROGRESS']);
medikoo marked this conversation as resolved.
Show resolved Hide resolved
const stacksExist = Array.isArray(data.Stacks);
medikoo marked this conversation as resolved.
Show resolved Hide resolved
const isSingleStack = stacksExist && data.Stacks.length <= 1;
const shouldCheckStackStatus =
isSingleStack && inactiveStateNames.has(data.Stacks[0].StackStatus);
if (shouldCheckStackStatus) {
const errorMessage = [
'Service cannot be deployed as the CloudFormation stack ',
"is in the 'REVIEW_IN_PROGRESS' state. ",
medikoo marked this conversation as resolved.
Show resolved Hide resolved
'This may signal either that stack is currently deployed by a different entity, ',
'or that the previous deployment failed and was left in an abnormal state, ',
"in which case you can mitigate the issue by running 'sls remove' command",
].join('');
throw new ServerlessError(errorMessage, 'AWS_CLOUDFORMATION_INACTIVE_STACK');
}
return BbPromise.resolve('alreadyCreated');
})
)
Expand Down
57 changes: 57 additions & 0 deletions test/unit/lib/plugins/aws/deploy/index.test.js
Expand Up @@ -651,6 +651,63 @@ describe('test/unit/lib/plugins/aws/deploy/index.test.js', () => {
expect(deleteObjectsStub).not.to.be.called;
});

it('with nonexistent stack - should output an appropriate error message for an abnormal stack state', async () => {
const describeStacksStub = sinon
.stub()
.onFirstCall()
.resolves({
Stacks: [
{
StackStatus: 'REVIEW_IN_PROGRESS',
},
],
});
const createChangeSetStub = sinon.stub().resolves({});
const executeChangeSetStub = sinon.stub().resolves({});
const s3UploadStub = sinon.stub().resolves();
const deleteObjectsStub = sinon.stub().resolves({});
const awsRequestStubMap = {
...baseAwsRequestStubMap,
ECR: {
describeRepositories: sinon.stub().throws({
providerError: { code: 'RepositoryNotFoundException' },
}),
},
S3: {
deleteObjects: deleteObjectsStub,
listObjectsV2: { Contents: [] },
upload: s3UploadStub,
headBucket: {},
},
CloudFormation: {
describeStacks: describeStacksStub,
createChangeSet: createChangeSetStub,
executeChangeSet: executeChangeSetStub,
deleteChangeSet: {},
describeChangeSet: {
ChangeSetName: 'new-service-dev-change-set',
ChangeSetId: 'some-change-set-id',
StackName: 'new-service-dev',
Status: 'CREATE_COMPLETE',
},
describeStackEvents: {},
describeStackResource: {
StackResourceDetail: { PhysicalResourceId: 's3-bucket-resource' },
},
validateTemplate: {},
listStackResources: {},
},
};

await expect(
runServerless({
fixture: 'function',
command: 'deploy',
awsRequestStubMap,
})
).to.have.been.eventually.rejected.with.property('code', 'AWS_CLOUDFORMATION_INACTIVE_STACK');
});

it('with existing stack - subsequent deploy', async () => {
const s3BucketPrefix = 'serverless/test-aws-deploy-with-existing-stack/dev';
const s3UploadStub = sinon.stub().resolves();
Expand Down