Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Mesh Updates #1109

Merged
merged 7 commits into from
Oct 16, 2023
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,356 changes: 1,354 additions & 2 deletions package-lock.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion packages/graphql-mesh-server/.npmignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
*.ts
!lib/handlers/*.ts
!assets/handlers/*.ts
!*.d.ts
!*.js

Expand Down
5 changes: 4 additions & 1 deletion packages/graphql-mesh-server/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# Prerender in Fargate
# GraphQL Mesh in Fargate
A construct host [GraphQL Mesh](https://the-guild.dev/graphql/mesh) server in Fargate.

## Deployment notifications
If notificationArn is set this construct creates a CodeStar notification rule, SNS topic and Lambda function to receive notifications for codepipeline executions and forward them to another SNS topic. This is so that you can setup AWS Chatbot either in this account OR another account and forward the notifications there.
## Props
- `vpc?`: VPC to attach Redis and Fargate instances to (default: create a vpc)
- `vpcName?`: If no VPC is provided create one with this name (default: 'graphql-server-vpc')
Expand All @@ -13,3 +15,4 @@ A construct host [GraphQL Mesh](https://the-guild.dev/graphql/mesh) server in Fa
- `memory?`: Amount of memory per Fargate instance (default: 1024)
- `redis?`: Redis instance to use for mesh caching
- `secrets?`: SSM values to pass through to the container as secrets
- `notificationArn?`: SNS Topic ARN to publish deployment notifications to
20 changes: 20 additions & 0 deletions packages/graphql-mesh-server/assets/handlers/notify-sns.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { PublishCommand, SNSClient } from "@aws-sdk/client-sns";
import { SNSEvent } from "aws-lambda";

const client = new SNSClient({ region: process.env.AWS_REGION });

export const handler = async (event: SNSEvent): Promise<void> => {
const record = event.Records[0];
const message = record.Sns.Message;

const command = new PublishCommand({
TopicArn: process.env.SNS_TOPIC,
Message: message,
});

try {
await client.send(command);
} catch (e) {
console.log(e);
}
};
24 changes: 14 additions & 10 deletions packages/graphql-mesh-server/lib/fargate.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Construct } from "constructs";
import { Duration, Token } from "aws-cdk-lib";
import { Duration } from "aws-cdk-lib";
import { RemovalPolicy } from "aws-cdk-lib";
import * as acm from "aws-cdk-lib/aws-certificatemanager";
import * as ecs from "aws-cdk-lib/aws-ecs";
Expand All @@ -8,7 +8,7 @@ import * as ecsPatterns from "aws-cdk-lib/aws-ecs-patterns";
import * as iam from "aws-cdk-lib/aws-iam";
import * as ssm from "aws-cdk-lib/aws-ssm";
import * as auto_scaling from "aws-cdk-lib/aws-autoscaling";
import { Port, SecurityGroup, Vpc } from "aws-cdk-lib/aws-ec2";
import { Port, SecurityGroup, IVpc, Vpc } from "aws-cdk-lib/aws-ec2";
import { RedisService } from "./redis-construct";
import {
ManagedRule,
Expand All @@ -20,7 +20,7 @@ export interface MeshServiceProps {
/**
* VPC to attach Redis instance to
*/
vpc?: Vpc;
vpc?: IVpc;
/**
* Repository to pull the container image from
*/
Expand All @@ -46,17 +46,20 @@ export interface MeshServiceProps {
*/
memory?: number;
/**
* Redis instance to use for mesh caching
* Redis configuration to use for mesh caching
*/
redis: RedisService;
redis: {
service: RedisService;
database?: string;
};
/**
* SSM values to pass through to the container as secrets
*/
secrets?: { [key: string]: ssm.IStringParameter | ssm.IStringListParameter };
}

export class MeshService extends Construct {
public readonly vpc: Vpc;
public readonly vpc: IVpc;
public readonly repository: ecr.Repository;
public readonly service: ecs.FargateService;
public readonly firewall: WebApplicationFirewall;
Expand Down Expand Up @@ -131,13 +134,14 @@ export class MeshService extends Construct {

// If using Redis configure security group and pass connection string to container
if (props.redis) {
props.redis.securityGroup.addIngressRule(
props.redis.service.securityGroup.addIngressRule(
securityGroup,
Port.tcp(props.redis.connectionPort)
Port.tcp(props.redis.service.connectionPort)
);

environment["REDIS_ENDPOINT"] = props.redis.connectionEndPoint;
environment["REDIS_PORT"] = props.redis.connectionPort.toString();
environment["REDIS_ENDPOINT"] = props.redis.service.connectionEndPoint;
environment["REDIS_PORT"] = props.redis.service.connectionPort.toString();
environment["REDIS_DATABASE"] = props.redis.database ?? "0";
}

// Construct secrets from provided ssm values
Expand Down
29 changes: 20 additions & 9 deletions packages/graphql-mesh-server/lib/graphql-mesh-server.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { Construct } from "constructs";
import { MeshService, MeshServiceProps } from "./fargate";
import { RedisService, RedisServiceProps } from "./redis-construct";
import { MeshService } from "./fargate";
import { RedisService } from "./redis-construct";
import { CodePipelineService } from "./pipeline";
import { SecurityGroup, Vpc } from "aws-cdk-lib/aws-ec2";
import { SecurityGroup, IVpc, Vpc } from "aws-cdk-lib/aws-ec2";
import { Repository } from "aws-cdk-lib/aws-ecr";
import { FargateService } from "aws-cdk-lib/aws-ecs";
import { CfnCacheCluster } from "aws-cdk-lib/aws-elasticache";
Expand All @@ -12,7 +12,7 @@ export type MeshHostingProps = {
/**
* VPC to attach Redis and Fargate instances to (default: create a vpc)
*/
vpc?: Vpc;
vpc?: IVpc;
/**
* If no VPC is provided create one with this name (default: 'graphql-server-vpc')
*/
Expand Down Expand Up @@ -46,17 +46,24 @@ export type MeshHostingProps = {
*/
memory?: number;
/**
* Redis instance to use for mesh caching
* Redis configuration to use for mesh caching
*/
redis?: RedisService;
redis?: {
service: RedisService;
database?: string;
};
/**
* SSM values to pass through to the container as secrets
*/
secrets?: { [key: string]: ssm.IStringParameter | ssm.IStringListParameter };
/**
* ARN of the SNS Topic to send deployment notifications to
*/
notificationArn?: string;
};

export class MeshHosting extends Construct {
public readonly vpc: Vpc;
public readonly vpc: IVpc;
public readonly repository: Repository;
public readonly service: FargateService;
public readonly cacheCluster: CfnCacheCluster;
Expand All @@ -73,7 +80,7 @@ export class MeshHosting extends Construct {
});

const redis =
props.redis ||
props.redis?.service ||
new RedisService(this, "redis", {
...props,
vpc: this.vpc,
Expand All @@ -85,7 +92,10 @@ export class MeshHosting extends Construct {
const mesh = new MeshService(this, "mesh", {
...props,
vpc: this.vpc,
redis,
redis: {
service: redis,
database: props.redis?.database,
},
});

this.service = mesh.service;
Expand All @@ -94,6 +104,7 @@ export class MeshHosting extends Construct {
new CodePipelineService(this, "pipeline", {
repository: this.repository,
service: this.service,
notificationArn: props.notificationArn,
});
}
}
53 changes: 53 additions & 0 deletions packages/graphql-mesh-server/lib/pipeline.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,15 @@ import { Construct } from "constructs";
import * as fs from "fs";
import * as path from "path";
import * as YAML from "yaml";
import { NodejsFunction } from "aws-cdk-lib/aws-lambda-nodejs";
import { Runtime } from "aws-cdk-lib/aws-lambda";
import { Effect, PolicyStatement } from "aws-cdk-lib/aws-iam";
import { Topic } from "aws-cdk-lib/aws-sns";
import { LambdaSubscription } from "aws-cdk-lib/aws-sns-subscriptions";
import {
DetailType,
NotificationRule,
} from "aws-cdk-lib/aws-codestarnotifications";

export interface CodePipelineServiceProps {
/**
Expand All @@ -24,6 +33,11 @@ export interface CodePipelineServiceProps {
* Path to buildspec.yml (default: '../assets/buildspec.yml')
*/
buildspecPath?: string;

/**
* ARN of the SNS Topic to send deployment notifications to
*/
notificationArn?: string;
}

export class CodePipelineService extends Construct {
Expand Down Expand Up @@ -90,5 +104,44 @@ export class CodePipelineService extends Construct {
}),
],
});

if (props.notificationArn) {
const notifier = new NodejsFunction(this, "NotifierLambda", {
entry: path.resolve(__dirname, "../assets/handlers/notify-sns.ts"),
gowrizrh marked this conversation as resolved.
Show resolved Hide resolved
description:
"Lambda function to forward SNS messages to another account.",
runtime: Runtime.NODEJS_18_X,
handler: "index.handler",
timeout: Duration.seconds(10),
environment: {
SNS_TOPIC: props.notificationArn,
},
});

notifier.addToRolePolicy(
new PolicyStatement({
actions: ["sns:publish"],
resources: [props.notificationArn],
effect: Effect.ALLOW,
})
);

const topic = new Topic(this, "NotifierTopic");
topic.addSubscription(new LambdaSubscription(notifier));

new NotificationRule(this, "CodeStarNotificationRule", {
detailType: DetailType.FULL,
events: [
"codepipeline-pipeline-pipeline-execution-failed",
"codepipeline-pipeline-pipeline-execution-canceled",
"codepipeline-pipeline-pipeline-execution-started",
"codepipeline-pipeline-pipeline-execution-resumed",
"codepipeline-pipeline-pipeline-execution-succeeded",
"codepipeline-pipeline-pipeline-execution-superseded",
],
targets: [topic],
source: this.pipeline,
});
}
}
}
6 changes: 3 additions & 3 deletions packages/graphql-mesh-server/lib/redis-construct.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
import { SecurityGroup, Vpc } from "aws-cdk-lib/aws-ec2";
import { SecurityGroup, IVpc } from "aws-cdk-lib/aws-ec2";
import {
CfnCacheCluster,
CfnSubnetGroup,
CfnParameterGroup,
} from "aws-cdk-lib/aws-elasticache";
import { CfnOutput, Reference, Token } from "aws-cdk-lib";

Check warning on line 7 in packages/graphql-mesh-server/lib/redis-construct.ts

View workflow job for this annotation

GitHub Actions / build

'Reference' is defined but never used
import { Construct } from "constructs";

export interface RedisServiceProps {
/**
* VPC to attach Redis instance to
*/
vpc: Vpc;
vpc: IVpc;
/**
* Cache node type (default: 'cache.t2.micro')
*/
Expand All @@ -20,7 +20,7 @@

export class RedisService extends Construct {
public readonly cacheCluster: CfnCacheCluster;
public readonly vpc: Vpc;
public readonly vpc: IVpc;
public readonly securityGroup: SecurityGroup;

constructor(scope: Construct, id: string, props: RedisServiceProps) {
Expand Down
3 changes: 2 additions & 1 deletion packages/graphql-mesh-server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
"yaml": "^2.3.1",
"aws-cdk-lib": "2.97.0",
"constructs": "^10.0.0",
"source-map-support": "^0.5.21"
"source-map-support": "^0.5.21",
"@aws-sdk/client-sns": "^3.413.0"
}
}
Loading