Eslam λ Hefnawy 1adeed3435
fix(apig): fix enabling api gateway logs (SC-2491) (#12655)
* fix(apig): fix enabling api gateway logs

* chore: update comment

* docs: update apig logs docs
2024-07-01 15:04:43 -04:00

158 lines
4.3 KiB
JavaScript

'use strict'
/**
* IMPORTANT: This file is cached, so when you test changes made to this file,
* make sure you delete the .serverless directory, and un-memoize the packaging code.
* For more info search for the "utils/ensure-artifact.js" file in the codebase.
*/
const { wait, MAX_AWS_REQUEST_TRY } = require('../utils')
const { getEnvironment, handlerWrapper } = require('../utils')
const {
APIGatewayClient,
GetAccountCommand,
UpdateAccountCommand,
} = require('@aws-sdk/client-api-gateway')
const {
IAMClient,
ListAttachedRolePoliciesCommand,
CreateRoleCommand,
AttachRolePolicyCommand,
} = require('@aws-sdk/client-iam')
const apiGateway = new APIGatewayClient({ maxAttempts: MAX_AWS_REQUEST_TRY })
const iam = new IAMClient({ maxAttempts: MAX_AWS_REQUEST_TRY })
async 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 { RoleArn } = event.ResourceProperties
const {
Partition: partition,
AccountId: accountId,
Region: region,
} = getEnvironment(context)
apiGateway.config.region = () => region
iam.config.region = () => region
const assignedRoleArn = (await apiGateway.send(new GetAccountCommand({})))
.cloudwatchRoleArn
let roleArn = `arn:${partition}:iam::${accountId}:role/serverlessApiGatewayCloudWatchRole`
if (RoleArn) {
// if there's a roleArn in the Resource Properties, just re-use it here
roleArn = RoleArn
} else {
// Create an own API Gateway role if the roleArn was not set via Resource Properties
const apiGatewayPushToCloudWatchLogsPolicyArn = `arn:${partition}:iam::aws:policy/service-role/AmazonAPIGatewayPushToCloudWatchLogs`
const roleName = roleArn.slice(roleArn.lastIndexOf('/') + 1)
const attachedPolicies = await (async () => {
try {
return (
await iam.send(
new ListAttachedRolePoliciesCommand({ RoleName: roleName }),
)
).AttachedPolicies
} catch (error) {
if (
error.code === 'NoSuchEntity' ||
error.message.includes('cannot be found')
) {
// Role doesn't exist yet, create
await iam.send(
new CreateRoleCommand({
AssumeRolePolicyDocument: JSON.stringify({
Version: '2012-10-17',
Statement: [
{
Effect: 'Allow',
Principal: {
Service: ['apigateway.amazonaws.com'],
},
Action: ['sts:AssumeRole'],
},
],
}),
Path: '/',
RoleName: roleName,
}),
)
return []
}
throw error
}
})()
if (
!attachedPolicies.some(
(policy) =>
policy.PolicyArn === apiGatewayPushToCloudWatchLogsPolicyArn,
)
) {
await iam.send(
new AttachRolePolicyCommand({
PolicyArn: apiGatewayPushToCloudWatchLogsPolicyArn,
RoleName: roleName,
}),
)
}
}
// there's nothing to do if the role is the same
if (roleArn === assignedRoleArn) return null
const updateAccount = async (counter = 1) => {
try {
await apiGateway.send(
new UpdateAccountCommand({
patchOperations: [
{
op: 'replace',
path: '/cloudwatchRoleArn',
value: roleArn,
},
],
}),
)
} 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,
'CustomResourceApiGatewayAccountCloudWatchRole',
),
}