cdk/lib/super-mode-calculator.ts (107 lines of code) (raw):
import { GuScheduledLambda } from '@guardian/cdk';
import type { GuStackProps } from '@guardian/cdk/lib/constructs/core';
import { GuStack } from '@guardian/cdk/lib/constructs/core';
import { type App, Duration, RemovalPolicy } from 'aws-cdk-lib';
import {
AttributeType,
BillingMode,
ProjectionType,
Table,
} from 'aws-cdk-lib/aws-dynamodb';
import { Schedule } from 'aws-cdk-lib/aws-events';
import { PolicyStatement, Role, ServicePrincipal } from 'aws-cdk-lib/aws-iam';
import { LoggingFormat, Runtime } from 'aws-cdk-lib/aws-lambda';
const appName = 'super-mode';
export class SuperModeCalculator extends GuStack {
constructor(scope: App, id: string, props: GuStackProps) {
super(scope, id, props);
const scheduleRules = [
{
schedule: Schedule.rate(Duration.minutes(60)),
},
];
const superModeCalculatorTable = new Table(
this,
'super-mode-calculator-table',
{
tableName: `super-mode-calculator-${this.stage}`,
removalPolicy: RemovalPolicy.RETAIN,
billingMode: BillingMode.PROVISIONED,
partitionKey: {
name: 'id',
type: AttributeType.STRING,
},
sortKey: {
name: 'startTimestamp',
type: AttributeType.STRING,
},
readCapacity: 5,
writeCapacity: 5,
pointInTimeRecovery: this.stage === 'PROD',
},
);
superModeCalculatorTable.addGlobalSecondaryIndex({
indexName: 'end',
partitionKey: { name: 'endDate', type: AttributeType.STRING },
sortKey: { name: 'endTimestamp', type: AttributeType.STRING },
projectionType: ProjectionType.ALL,
readCapacity: 5,
writeCapacity: 5,
});
const role = new Role(this, 'query-lambda-role', {
// Set the name of the role rather than using an autogenerated name.
// This is because if the ARN is too long then it breaks the authentication request to GCP
roleName: `${appName}-${this.stage}`,
assumedBy: new ServicePrincipal('lambda.amazonaws.com'),
});
role.addToPolicy(
// Logging permissions
new PolicyStatement({
actions: [
'logs:CreateLogGroup',
'logs:CreateLogStream',
'logs:PutLogEvents',
],
resources: ['*'],
}),
);
role.addToPolicy(
// Permission to read config from Parameter Store
new PolicyStatement({
actions: ['ssm:GetParameter'],
resources: [
`arn:aws:ssm:${this.region}:${this.account}:parameter/super-mode/${this.stage}/gcp-wif-credentials-config`,
],
}),
);
role.addToPolicy(
new PolicyStatement({
actions: ['dynamodb:Query'],
resources: [
`arn:aws:dynamodb:eu-west-1:${this.account}:table/super-mode-calculator-${this.stage}`,
`arn:aws:dynamodb:eu-west-1:${this.account}:table/super-mode-calculator-${this.stage}/index/*`,
],
}),
);
role.addToPolicy(
new PolicyStatement({
actions: ['dynamodb:BatchWriteItem'],
resources: [superModeCalculatorTable.tableArn],
}),
);
new GuScheduledLambda(this, 'SuperModeCalculator', {
app: appName,
functionName: `${appName}-${this.stage}`,
runtime: Runtime.NODEJS_20_X,
handler: 'lambdas/lambda.handler',
fileName: `${appName}.zip`,
rules: scheduleRules,
role,
timeout: Duration.minutes(2),
loggingFormat: LoggingFormat.TEXT,
monitoringConfiguration:
this.stage === 'PROD'
? {
toleratedErrorPercentage: 0,
snsTopicName: 'alarms-handler-topic-PROD',
}
: { noMonitoring: true },
});
}
}