cdk/lib/prism.ts (112 lines of code) (raw):
import { AccessScope } from '@guardian/cdk/lib/constants';
import type { GuStackProps } from '@guardian/cdk/lib/constructs/core/stack';
import { GuStack } from '@guardian/cdk/lib/constructs/core/stack';
import {
GuAllowPolicy,
GuAssumeRolePolicy,
GuDynamoDBReadPolicy,
GuGetS3ObjectsPolicy,
} from '@guardian/cdk/lib/constructs/iam';
import { GuEc2AppExperimental } from '@guardian/cdk/lib/experimental/patterns/ec2-app';
import type { App } from 'aws-cdk-lib';
import { Duration } from 'aws-cdk-lib';
import type { CfnAutoScalingGroup } from 'aws-cdk-lib/aws-autoscaling';
import {
BlockDeviceVolume,
EbsDeviceVolumeType,
} from 'aws-cdk-lib/aws-autoscaling';
import {
InstanceClass,
InstanceSize,
InstanceType,
Peer,
} from 'aws-cdk-lib/aws-ec2';
interface PrismProps extends Omit<GuStackProps, 'description' | 'stack'> {
domainName: string;
minimumInstances: number;
/**
* Which application build to run.
* This will typically match the build number provided by CI.
*/
buildIdentifier: string;
}
export class Prism extends GuStack {
constructor(scope: App, id: string, props: PrismProps) {
const app = 'prism';
super(scope, id, {
...props,
description: 'Prism - service discovery',
stack: 'deploy',
app,
});
const { buildIdentifier } = props;
const filename = `${app}-${buildIdentifier}.deb`;
const pattern = new GuEc2AppExperimental(this, {
buildIdentifier,
applicationPort: 9000,
app,
applicationLogging: {
enabled: true,
},
imageRecipe: 'arm64-focal-java11-deploy-infrastructure',
instanceType: InstanceType.of(InstanceClass.T4G, InstanceSize.MEDIUM),
userData: {
distributable: {
fileName: filename,
executionStatement: `dpkg -i /${app}/${filename}`,
},
},
certificateProps: {
domainName: props.domainName,
},
monitoringConfiguration:
this.stage === 'PROD'
? {
snsTopicName: 'devx-alerts',
http5xxAlarm: false,
unhealthyInstancesAlarm: true,
}
: { noMonitoring: true },
access: {
scope: AccessScope.INTERNAL,
cidrRanges: [Peer.ipv4('10.0.0.0/8')],
},
roleConfiguration: {
additionalPolicies: [
new GuAllowPolicy(this, 'DescribeEC2BonusPolicy', {
resources: ['*'],
actions: ['EC2:Describe*'],
}),
new GuDynamoDBReadPolicy(this, 'ConfigPolicy', {
tableName: 'config-deploy',
}),
new GuGetS3ObjectsPolicy(this, 'DataPolicy', {
bucketName: 'prism-data',
}),
new GuAssumeRolePolicy(this, 'CrawlerPolicy', {
resources: [
'arn:aws:iam::*:role/*Prism*',
'arn:aws:iam::*:role/*prism*',
],
}),
],
},
scaling: {
minimumInstances: props.minimumInstances,
},
blockDevices: [
{
deviceName: '/dev/sda1',
volume: BlockDeviceVolume.ebs(8, {
volumeType: EbsDeviceVolumeType.GP2,
}),
},
],
});
// The pattern does not currently offer support for customising healthchecks via props
pattern.targetGroup.configureHealthCheck({
path: '/management/healthcheck',
unhealthyThresholdCount: 10,
interval: Duration.seconds(5),
timeout: Duration.seconds(3),
});
// Similarly the pattern does not offer support for extending the default ASG grace period via props
const cfnAsg = pattern.autoScalingGroup.node
.defaultChild as CfnAutoScalingGroup;
cfnAsg.healthCheckGracePeriod = Duration.minutes(15).toSeconds();
}
}