Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
13 changes: 12 additions & 1 deletion lib/plugins/aws/deploy/lib/check-for-changes.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,16 @@ function getDeploymentDirectoryTimestamp(directory) {
return Number(directory.slice(0, directory.indexOf('-')));
}

function isCloudFormationMissingResourceError(error) {
const message = error && error.message;
return (
typeof message === 'string' &&
(message.includes('does not exist for stack') ||
message === 'Resource does not exist' ||
(message.startsWith('Resource ') && message.includes(' does not exist')))
);
}

module.exports = {
async checkForChanges() {
this.serverless.service.provider.shouldNotDeploy = false;
Expand Down Expand Up @@ -413,7 +423,8 @@ module.exports = {
);

return physicalResourceId === StackResourceDetail.PhysicalResourceId;
} catch {
} catch (error) {
if (!isCloudFormationMissingResourceError(error)) throw error;
return false;
}
},
Expand Down
63 changes: 63 additions & 0 deletions test/unit/lib/plugins/aws/deploy/lib/check-for-changes.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -1533,6 +1533,69 @@ describe('test/unit/lib/plugins/aws/deploy/lib/checkForChanges.test.js', () => {
expect(deleteStub).to.not.have.been.called;
});

it('treats missing CloudFormation subscription filter resources as external', async () => {
const awsDeploy = createAwsDeployTestInstance();
const requestStub = sandbox
.stub(awsDeploy.provider, 'request')
.rejects(
new Error(
`Resource ${awsDeploy.provider.naming.getCloudWatchLogLogicalId(
'Fn1',
1
)} does not exist for stack ${awsDeploy.provider.naming.getStackName()}`
)
);

try {
await expect(
awsDeploy.isInternalSubscriptionFilter(
awsDeploy.provider.naming.getStackName(),
awsDeploy.provider.naming.getCloudWatchLogLogicalId('Fn1', 1),
'physical-id'
)
).to.eventually.equal(false);
expect(requestStub).to.have.been.calledOnce;
} finally {
awsDeploy.provider.request.restore();
}
});

it('surfaces CloudFormation access errors during subscription filter classification', async () => {
const awsDeploy = createAwsDeployTestInstance();
sandbox
.stub(awsDeploy.provider, 'request')
.rejects(Object.assign(new Error('denied'), { code: 'AccessDenied' }));

try {
await expect(
awsDeploy.isInternalSubscriptionFilter(
awsDeploy.provider.naming.getStackName(),
awsDeploy.provider.naming.getCloudWatchLogLogicalId('Fn1', 1),
'physical-id'
)
).to.be.rejectedWith('denied');
} finally {
awsDeploy.provider.request.restore();
}
});

it('does not treat missing stacks with Resource in the name as missing resources', async () => {
const awsDeploy = createAwsDeployTestInstance();
sandbox.stub(awsDeploy.provider, 'request').rejects(
Object.assign(new Error('Stack with id MyResourceStack does not exist'), {
code: 'ValidationError',
})
);

try {
await expect(
awsDeploy.isInternalSubscriptionFilter('MyResourceStack', 'LogicalId', 'physical-id')
).to.be.rejectedWith('MyResourceStack');
} finally {
awsDeploy.provider.request.restore();
}
});

it('should not attempt to delete filter for 2 subscription filter per log group include externals', async () => {
const deleteStub = sandbox.stub();
let serverless;
Expand Down