Skip to content

Commit

Permalink
Passing in Docs Cache endpoint as environment variable (#763)
Browse files Browse the repository at this point in the history
  • Loading branch information
bsinghvi authored May 2, 2024
1 parent a07443e commit 913044b
Show file tree
Hide file tree
Showing 3 changed files with 96 additions and 17 deletions.
7 changes: 7 additions & 0 deletions .github/workflows/diff-fdr-dev.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
name: Diff FDR Dev

on:
pull_request:
branches:
- "*"
paths:
- "servers/fdr-deploy/scripts/**"
- ".github/workflows/diff-fdr-dev.yml"
- "pnpm-lock.yaml"
push:
branches:
- "*"
Expand Down
10 changes: 9 additions & 1 deletion servers/fdr-deploy/scripts/elasticache-stack.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
import { EnvironmentType } from "@fern-fern/fern-cloud-sdk/api";
import { Environment, Stack, StackProps, Token } from "aws-cdk-lib";
import { CfnOutput, Environment, Stack, StackProps, Token } from "aws-cdk-lib";
import { IVpc, Peer, Port, SecurityGroup } from "aws-cdk-lib/aws-ec2";
import { CfnReplicationGroup, CfnSubnetGroup } from "aws-cdk-lib/aws-elasticache";
import { Construct } from "constructs";

/*
TODO: Once ready, can use this out of the box to construct self-containing elasticache stacks.
- For now, keeping this as a reference.
*/

interface ElastiCacheStackProps extends StackProps {
readonly cacheName: string;
readonly IVpc: IVpc;
Expand Down Expand Up @@ -72,5 +77,8 @@ export class ElastiCacheStack extends Stack {
Port.tcp(Token.asNumber(this.redisEndpointPort)),
"Redis Port Ingress rule",
);

new CfnOutput(this, `${props.cacheName}Host`, { value: this.redisEndpointAddress });
new CfnOutput(this, `${props.cacheName}Port`, { value: this.redisEndpointPort });
}
}
96 changes: 80 additions & 16 deletions servers/fdr-deploy/scripts/fdr-deploy-stack.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { EnvironmentInfo, EnvironmentType } from "@fern-fern/fern-cloud-sdk/api";
import { CfnOutput, Duration, RemovalPolicy, Stack, StackProps } from "aws-cdk-lib";
import { CfnOutput, Duration, Environment, RemovalPolicy, Stack, StackProps, Token } from "aws-cdk-lib";
import { Certificate } from "aws-cdk-lib/aws-certificatemanager";
import { Alarm } from "aws-cdk-lib/aws-cloudwatch";
import * as actions from "aws-cdk-lib/aws-cloudwatch-actions";
import { Peer, Port, SecurityGroup, Vpc } from "aws-cdk-lib/aws-ec2";
import { IVpc, Peer, Port, SecurityGroup, Vpc } from "aws-cdk-lib/aws-ec2";
import { Cluster, ContainerImage, LogDriver, Volume } from "aws-cdk-lib/aws-ecs";
import { ApplicationLoadBalancedFargateService } from "aws-cdk-lib/aws-ecs-patterns";
import { CfnReplicationGroup, CfnSubnetGroup } from "aws-cdk-lib/aws-elasticache";
import { ApplicationProtocol, HttpCodeElb } from "aws-cdk-lib/aws-elasticloadbalancingv2";
import { LogGroup } from "aws-cdk-lib/aws-logs";
import { ARecord, HostedZone, RecordTarget } from "aws-cdk-lib/aws-route53";
Expand All @@ -15,14 +16,23 @@ import { PrivateDnsNamespace } from "aws-cdk-lib/aws-servicediscovery";
import * as sns from "aws-cdk-lib/aws-sns";
import { EmailSubscription } from "aws-cdk-lib/aws-sns-subscriptions";
import { Construct } from "constructs";
import { ElastiCacheStack } from "./elasticache-stack";

const CONTAINER_NAME = "fern-definition-registry";
const SERVICE_NAME = "fdr";

export class FdrDeployStack extends Stack {
private readonly fernDocsCacheEndpoint: string;
interface ElastiCacheProps {
readonly cacheName: string;
readonly IVpc: IVpc;
readonly numCacheShards: number;
readonly numCacheReplicasPerShard: number | undefined;
readonly clusterMode: "enabled" | "disabled";
readonly cacheNodeType: string;
readonly envType: EnvironmentType;
readonly env?: Environment;
readonly ingressSecurityGroup?: SecurityGroup;
}

export class FdrDeployStack extends Stack {
constructor(
scope: Construct,
id: string,
Expand Down Expand Up @@ -79,6 +89,18 @@ export class FdrDeployStack extends Stack {
versioned: true,
});

const fernDocsCacheEndpoint = this.constructElastiCacheInstance(this, {
cacheName: "FernDocsCache",
IVpc: vpc,
numCacheShards: 1,
numCacheReplicasPerShard: environmentType === EnvironmentType.Prod ? 2 : undefined,
clusterMode: "enabled",
cacheNodeType: "cache.r7g.large",
envType: environmentType,
env: props?.env,
ingressSecurityGroup: fdrSg,
});

const cloudmapNamespaceName = environmentInfo.cloudMapNamespaceInfo.namespaceName;
const cloudMapNamespace = PrivateDnsNamespace.fromPrivateDnsNamespaceAttributes(this, "private-cloudmap", {
namespaceArn: environmentInfo.cloudMapNamespaceInfo.namespaceArn,
Expand Down Expand Up @@ -111,6 +133,7 @@ export class FdrDeployStack extends Stack {
ALGOLIA_SEARCH_API_KEY: getEnvironmentVariableOrThrow("ALGOLIA_SEARCH_API_KEY"),
SLACK_TOKEN: getEnvironmentVariableOrThrow("FERNIE_SLACK_APP_TOKEN"),
LOG_LEVEL: getLogLevel(environmentType),
DOCS_CACHE_ENDPOINT: fernDocsCacheEndpoint,
ENABLE_CUSTOMER_NOTIFICATIONS: (environmentType === "PROD").toString(),
},
containerName: CONTAINER_NAME,
Expand Down Expand Up @@ -197,20 +220,61 @@ export class FdrDeployStack extends Stack {
evaluationPeriods: 5,
});
lb500CountAlarm.addAlarmAction(new actions.SnsAction(snsTopic));
}

const fernDocsCache = new ElastiCacheStack(this, "FernDocsElastiCache", {
cacheName: "FernDocsElastiCache",
IVpc: vpc,
numCacheShards: 1,
numCacheReplicasPerShard: environmentType === EnvironmentType.Prod ? 2 : undefined,
clusterMode: "enabled",
cacheNodeType: "cache.r7g.large",
envType: environmentType,
env: props?.env,
private constructElastiCacheInstance(scope: Construct, props: ElastiCacheProps): string {
const envPrefix = props.envType + "-";

const cacheSecurityGroupName = envPrefix + props.cacheName + "SecurityGroup";
const cacheSecurityGroup = new SecurityGroup(scope, cacheSecurityGroupName, {
vpc: props.IVpc,
allowAllOutbound: true,
description: `${cacheSecurityGroupName} CDK`,
});

const cacheSubnetGroupName = envPrefix + props.cacheName + "SubnetGroup";
const cacheSubnetGroup = new CfnSubnetGroup(this, cacheSubnetGroupName, {
description: `${cacheSubnetGroupName} CDK`,
cacheSubnetGroupName,
subnetIds: props.IVpc.publicSubnets.map(({ subnetId }) => subnetId),
});

const cacheReplicationGroupName = envPrefix + props.cacheName + "ReplicationGroup";
const cacheReplicationGroup = new CfnReplicationGroup(this, cacheReplicationGroupName, {
replicationGroupId: cacheReplicationGroupName,
replicationGroupDescription: `Replication Group for the ${cacheReplicationGroupName} ElastiCache stack`,
automaticFailoverEnabled: true,
autoMinorVersionUpgrade: true,
engine: "redis",
engineVersion: "7.0",
cacheParameterGroupName: "default.redis7.cluster.on",
cacheNodeType: props.cacheNodeType,
numNodeGroups: props.numCacheShards,
replicasPerNodeGroup: props.numCacheReplicasPerShard,
clusterMode: props.clusterMode,
cacheSubnetGroupName: cacheSubnetGroup.ref,
securityGroupIds: [cacheSecurityGroup.securityGroupId],
});

this.fernDocsCacheEndpoint = `${fernDocsCache.redisEndpointAddress}:${fernDocsCache.redisEndpointPort}`;
new CfnOutput(this, "FernDocsCacheEndpoint", { value: this.fernDocsCacheEndpoint });
cacheReplicationGroup.cfnOptions.updatePolicy = {
useOnlineResharding: true,
};

cacheReplicationGroup.addDependency(cacheSubnetGroup);

const cacheEndpointAddress = cacheReplicationGroup.attrConfigurationEndPointAddress;
const cacheEndpointPort = cacheReplicationGroup.attrConfigurationEndPointPort;

cacheSecurityGroup.addIngressRule(
props.ingressSecurityGroup || Peer.anyIpv4(),
Port.tcp(Token.asNumber(cacheEndpointPort)),
"Redis Port Ingress rule",
);

new CfnOutput(this, `${props.cacheName}Host`, { value: cacheEndpointAddress });
new CfnOutput(this, `${props.cacheName}Port`, { value: cacheEndpointPort });

return `${cacheEndpointAddress}:${cacheEndpointPort}`;
}
}

Expand Down

0 comments on commit 913044b

Please sign in to comment.