-
Notifications
You must be signed in to change notification settings - Fork 5.7k
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
Setup APIGW CloudWatch role via custom resource #6531
Merged
Merged
Changes from all commits
Commits
Show all changes
14 commits
Select commit
Hold shift + click to select a range
8558e6a
Move stage configuration to directory
medikoo 2089e76
Allow to use async/await in custom resource lambdas
medikoo 16acdea
Introduce `wait` util
medikoo e421180
Introduce DX friendly function name validation
medikoo 64ce692
Setup APIGW CloudWatch role via custom resource
medikoo 09b1829
confirmCloudWatchLogs util
medikoo 13dd52e
Confirm in tests written APIGW logs
medikoo c3dcb06
Ensure extensive timeout on custom resource lambdas
medikoo 6f10b03
Minimize function name
medikoo cc6c194
Correct typo
medikoo e042f06
Configure tests for new naming functions
medikoo ea121e7
Ensure to bail after first fail in CI
medikoo 60f36e4
Clear invalid options
medikoo f0a8b8c
Mock npm install in tests
medikoo File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
module.exports = { | ||
parserOptions: { ecmaVersion: 2017 }, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
114 changes: 114 additions & 0 deletions
114
lib/plugins/aws/customResources/resources/apiGatewayCloudWatchRole/handler.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
'use strict'; | ||
|
||
const ApiGateway = require('aws-sdk/clients/apigateway'); | ||
const Iam = require('aws-sdk/clients/iam'); | ||
const { getEnvironment, handlerWrapper, wait } = require('../utils'); | ||
|
||
function handler(event, context) { | ||
if (event.RequestType === 'Create') { | ||
return create(event, context); | ||
} else if (event.RequestType === 'Update') { | ||
return update(event, context); | ||
} else if (event.RequestType === 'Delete') { | ||
return remove(event, context); | ||
} | ||
throw new Error(`Unhandled RequestType ${event.RequestType}`); | ||
} | ||
|
||
async function create(event, context) { | ||
const { AccountId: accountId } = getEnvironment(context); | ||
|
||
const apiGatewayPushToCloudWatchLogsPolicyArn = | ||
'arn:aws:iam::aws:policy/service-role/AmazonAPIGatewayPushToCloudWatchLogs'; | ||
const roleArn = `arn:aws:iam::${accountId}:role/serverlessApiGatewayCloudWatchRole`; | ||
|
||
const apiGateway = new ApiGateway(); | ||
|
||
const assignedRoleArn = (await apiGateway.getAccount().promise()).cloudwatchRoleArn; | ||
const roleName = roleArn.slice(roleArn.lastIndexOf('/') + 1); | ||
|
||
const iam = new Iam(); | ||
|
||
const attachedPolicies = await (async () => { | ||
try { | ||
return (await iam.listAttachedRolePolicies({ RoleName: roleName }).promise()) | ||
.AttachedPolicies; | ||
} catch (error) { | ||
if (error.code === 'NoSuchEntity') { | ||
// Role doesn't exist yet, create; | ||
await iam | ||
.createRole({ | ||
AssumeRolePolicyDocument: JSON.stringify({ | ||
Version: '2012-10-17', | ||
Statement: [ | ||
{ | ||
Effect: 'Allow', | ||
Principal: { | ||
Service: ['apigateway.amazonaws.com'], | ||
}, | ||
Action: ['sts:AssumeRole'], | ||
}, | ||
], | ||
}), | ||
Path: '/', | ||
RoleName: roleName, | ||
}) | ||
.promise(); | ||
return []; | ||
} | ||
throw error; | ||
} | ||
})(); | ||
|
||
if ( | ||
!attachedPolicies.some(policy => policy.PolicyArn === apiGatewayPushToCloudWatchLogsPolicyArn) | ||
) { | ||
await iam | ||
.attachRolePolicy({ | ||
PolicyArn: apiGatewayPushToCloudWatchLogsPolicyArn, | ||
RoleName: roleName, | ||
}) | ||
.promise(); | ||
} | ||
|
||
if (assignedRoleArn === roleArn) return null; | ||
|
||
const updateAccount = async (counter = 1) => { | ||
try { | ||
await apiGateway | ||
.updateAccount({ | ||
patchOperations: [ | ||
{ | ||
op: 'replace', | ||
path: '/cloudwatchRoleArn', | ||
value: roleArn, | ||
}, | ||
], | ||
}) | ||
.promise(); | ||
} catch (error) { | ||
if (counter < 10) { | ||
// Observed fails with errors marked as non-retryable. Still they're outcome of | ||
// temporary state where just created AWS role is not being ready for use (yet) | ||
await wait(10000); | ||
return updateAccount(++counter); | ||
} | ||
throw error; | ||
} | ||
return null; | ||
}; | ||
|
||
return updateAccount(); | ||
} | ||
|
||
function update() { | ||
// No actions | ||
} | ||
|
||
function remove() { | ||
// No actions | ||
} | ||
|
||
module.exports = { | ||
handler: handlerWrapper(handler, 'CustomResouceApiGatewayAccountCloudWatchRole'), | ||
medikoo marked this conversation as resolved.
Show resolved
Hide resolved
|
||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -468,18 +468,6 @@ describe('#naming()', () => { | |
}); | ||
}); | ||
|
||
describe('#getApiGatewayLogsRoleLogicalId()', () => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we want tests for the new naming functions? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Added |
||
it('should return the API Gateway logs IAM role logical id', () => { | ||
expect(sdk.naming.getApiGatewayLogsRoleLogicalId()).to.equal('IamRoleApiGatewayLogs'); | ||
}); | ||
}); | ||
|
||
describe('#getApiGatewayAccountLogicalId()', () => { | ||
it('should return the API Gateway account logical id', () => { | ||
expect(sdk.naming.getApiGatewayAccountLogicalId()).to.equal('ApiGatewayAccount'); | ||
}); | ||
}); | ||
|
||
describe('#getDeploymentBucketLogicalId()', () => { | ||
it('should return "ServerlessDeploymentBucket"', () => { | ||
expect(sdk.naming.getDeploymentBucketLogicalId()).to.equal('ServerlessDeploymentBucket'); | ||
|
@@ -811,4 +799,28 @@ describe('#naming()', () => { | |
).to.equal('MyDashfunctionCustomEventBridge1'); | ||
}); | ||
}); | ||
|
||
describe('#getCustomResourceApiGatewayAccountCloudWatchRoleHandlerFunctionName()', () => { | ||
it('should return the nane of the APIGW Account CloudWatch role custom resouce handler function', () => { | ||
expect( | ||
sdk.naming.getCustomResourceApiGatewayAccountCloudWatchRoleHandlerFunctionName() | ||
).to.equal('custom-resource-apigw-cw-role'); | ||
}); | ||
}); | ||
|
||
describe('#getCustomResourceApiGatewayAccountCloudWatchRoleHandlerFunctionLogicalId()', () => { | ||
it('should return the logical id of the APIGW Account CloudWatch role custom resouce handler function', () => { | ||
expect( | ||
sdk.naming.getCustomResourceApiGatewayAccountCloudWatchRoleHandlerFunctionLogicalId() | ||
).to.equal('CustomDashresourceDashapigwDashcwDashroleLambdaFunction'); | ||
}); | ||
}); | ||
|
||
describe('#getCustomResourceApiGatewayAccountCloudWatchRoleResourceLogicalId()', () => { | ||
it('should return the logical id of the APIGW Account CloudWatch role custom resouce', () => { | ||
expect( | ||
sdk.naming.getCustomResourceApiGatewayAccountCloudWatchRoleResourceLogicalId() | ||
).to.equal('CustomApiGatewayAccountCloudWatchRole'); | ||
}); | ||
}); | ||
}); |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would this happen if the service + stage name combo is too long? Is there anything we could do to help the user, or are they stuck?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe so.
This change is purely about exposing that error earlier than it happens now (during CF deployment).
I plan to refactor a custom resources handling, and with that I'll ensure we're more reliable here, but I don't want to focus on that in context of that PR