packages/cdk/lib/repocop.ts (130 lines of code) (raw):

import { GuScheduledLambda, type GuScheduledLambdaProps } from '@guardian/cdk'; import type { GuLambdaErrorPercentageMonitoringProps, NoMonitoring, } from '@guardian/cdk/lib/constructs/cloudwatch'; import type { GuStack } from '@guardian/cdk/lib/constructs/core'; import { GuLambdaFunction } from '@guardian/cdk/lib/constructs/lambda'; import { Duration } from 'aws-cdk-lib'; import type { IVpc, SecurityGroup } from 'aws-cdk-lib/aws-ec2'; import type { Schedule } from 'aws-cdk-lib/aws-events'; import { PolicyStatement } from 'aws-cdk-lib/aws-iam'; import { Architecture, Runtime } from 'aws-cdk-lib/aws-lambda'; import type { DatabaseInstance } from 'aws-cdk-lib/aws-rds'; import { Secret } from 'aws-cdk-lib/aws-secretsmanager'; import type { ITopic } from 'aws-cdk-lib/aws-sns'; import { Topic } from 'aws-cdk-lib/aws-sns'; import { LambdaSubscription } from 'aws-cdk-lib/aws-sns-subscriptions'; export class Repocop { constructor( guStack: GuStack, schedule: Schedule, anghammaradTopic: ITopic, cloudqueryDB: DatabaseInstance, monitoringConfiguration: | NoMonitoring | GuLambdaErrorPercentageMonitoringProps, vpc: IVpc, interactiveMonitorTopic: Topic, dbSecurityGroup: SecurityGroup, repocopGithubSecret: Secret, gitHubOrg: string, ) { const dependencyGraphIntegratorInputTopic = new Topic( guStack, `dependency-graph-integrator-input-topic-${guStack.stage}`, { displayName: 'Dependency Graph Integrator Input Topic', }, ); const repocopLampdaProps: GuScheduledLambdaProps = { app: 'repocop', architecture: Architecture.ARM_64, fileName: 'repocop.zip', handler: 'index.main', memorySize: 2048, monitoringConfiguration, rules: [{ schedule }], runtime: Runtime.NODEJS_20_X, environment: { ANGHAMMARAD_SNS_ARN: anghammaradTopic.topicArn, DATABASE_HOSTNAME: cloudqueryDB.dbInstanceEndpointAddress, QUERY_LOGGING: 'false', // Set this to 'true' to enable SQL query logging INTERACTIVE_MONITOR_TOPIC_ARN: interactiveMonitorTopic.topicArn, GITHUB_APP_SECRET: repocopGithubSecret.secretArn, INTERACTIVES_COUNT: guStack.stage === 'PROD' ? '40' : '3', DEPENDENCY_GRAPH_INPUT_TOPIC_ARN: dependencyGraphIntegratorInputTopic.topicArn, GITHUB_ORG: gitHubOrg, }, vpc, securityGroups: [dbSecurityGroup], timeout: Duration.minutes(5), }; const repocopLambda = new GuScheduledLambda( guStack, 'repocop', repocopLampdaProps, ); const policyStatement = new PolicyStatement({ actions: ['cloudwatch:PutMetricData'], resources: ['*'], conditions: { StringEquals: { 'cloudwatch:namespace': 'repocop', }, }, }); cloudqueryDB.grantConnect(repocopLambda, 'repocop'); repocopGithubSecret.grantRead(repocopLambda); anghammaradTopic.grantPublish(repocopLambda); interactiveMonitorTopic.grantPublish(repocopLambda); dependencyGraphIntegratorInputTopic.grantPublish(repocopLambda); repocopLambda.addToRolePolicy(policyStatement); const dependencyGraphIntegratorLambda = stageAwareIntegratorLambda( guStack, vpc, 'dependency-graph-integrator', gitHubOrg, ); dependencyGraphIntegratorInputTopic.addSubscription( new LambdaSubscription(dependencyGraphIntegratorLambda, {}), ); } } function stageAwareIntegratorLambda( guStack: GuStack, vpc: IVpc, app: `${string}-integrator`, gitHubOrg: string, ): GuLambdaFunction { const nonProdLambdaProps = { app, architecture: Architecture.ARM_64, fileName: `${app}.zip`, handler: 'index.handler', memorySize: 1024, runtime: Runtime.NODEJS_20_X, vpc, timeout: Duration.minutes(5), environment: { GITHUB_ORG: gitHubOrg, }, }; if (guStack.stage === 'PROD' || guStack.stage === 'TEST') { const githubAppSecret = new Secret(guStack, `${app}-github-app-auth`, { secretName: `/${guStack.stage}/${guStack.stack}/service-catalogue/${app}-github-app-secret`, }); const lambda = new GuLambdaFunction(guStack, app, { ...nonProdLambdaProps, environment: { ...nonProdLambdaProps.environment, GITHUB_APP_SECRET: githubAppSecret.secretArn, }, }); githubAppSecret.grantRead(lambda); return lambda; } else { return new GuLambdaFunction(guStack, app, nonProdLambdaProps); } }