Skip to content

Commit

Permalink
feat(aws-fargate-sqs): Created new construct (#588)
Browse files Browse the repository at this point in the history
* created new construct

* fixed README

* updated integ test with encryption

* minor doc update

* fixed variable typo in unit test

* fixed variable typo in integ test

* fixed versioning in package.json

* added assertions for public and private api

* added assertions for dlq

* added queuePermissions prop

Co-authored-by: biffgaut <[email protected]>
  • Loading branch information
mickychetta and biffgaut committed Feb 22, 2022
1 parent 29a3b2a commit f7ddf3f
Show file tree
Hide file tree
Showing 13 changed files with 3,505 additions and 1 deletion.
2 changes: 1 addition & 1 deletion DESIGN_GUIDELINES.md
Original file line number Diff line number Diff line change
Expand Up @@ -305,7 +305,7 @@ Existing Inconsistencies would not be published, that’s for our internal use
| Name | Type | Notes |
| --- | --- | --- |
| queueProps? | sqs.QueueProps ||
| existingQueueObj? | sqs.Qeue ||
| existingQueueObj? | sqs.Queue ||
| deployDeadLetterQueue? | Boolean ||
| deadLetterQueueProps? | sqs.QueueProps ||
| maxReceiveCount | number ||
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
lib/*.js
test/*.js
*.d.ts
coverage
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
lib/*.js
test/*.js
*.js.map
*.d.ts
node_modules
*.generated.ts
dist
.jsii

.LAST_BUILD
.nyc_output
coverage
.nycrc
.LAST_PACKAGE
*.snk
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Exclude typescript source and config
*.ts
tsconfig.json
coverage
.nyc_output
*.tgz
*.snk
*.tsbuildinfo

# Include javascript files and typescript declarations
!*.js
!*.d.ts

# Exclude jsii outdir
dist

# Include .jsii
!.jsii

# Include .jsii
!.jsii
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
# aws-fargate-sqs module
<!--BEGIN STABILITY BANNER-->

---

![Stability: Experimental](https://img.shields.io/badge/stability-Experimental-important.svg?style=for-the-badge)

> All classes are under active development and subject to non-backward compatible changes or removal in any
> future version. These are not subject to the [Semantic Versioning](https://semver.org/) model.
> This means that while you may use them, you may need to update your source code when upgrading to a newer version of this package.
---
<!--END STABILITY BANNER-->

| **Reference Documentation**:| <span style="font-weight: normal">https://docs.aws.amazon.com/solutions/latest/constructs/</span>|
|:-------------|:-------------|
<div style="height:8px"></div>

| **Language** | **Package** |
|:-------------|-----------------|
|![Python Logo](https://docs.aws.amazon.com/cdk/api/latest/img/python32.png) Python|`aws_solutions_constructs.aws_fargate_sqs`|
|![Typescript Logo](https://docs.aws.amazon.com/cdk/api/latest/img/typescript32.png) Typescript|`@aws-solutions-constructs/aws-fargate-sqs`|
|![Java Logo](https://docs.aws.amazon.com/cdk/api/latest/img/java32.png) Java|`software.amazon.awsconstructs.services.fargatesqs`|

This AWS Solutions Construct implements an AWS Fargate service that can write to an Amazon SQS queue

Here is a minimal deployable pattern definition:

Typescript
``` typescript
import { FargateToSqs, FargateToSqsProps } from '@aws-solutions-constructs/aws-fargate-sqs';

const props: FargateToSqsProps = {
publicApi: true,
ecrRepositoryArn: "arn of a repo in ECR in your account",
});

new FargateToSqs(stack, 'test-construct', props);
```

## Pattern Construct Props

| **Name** | **Type** | **Description** |
|:-------------|:----------------|-----------------|
| publicApi | boolean | Whether the construct is deploying a private or public API. This has implications for the VPC and ALB. |
| vpcProps? | [ec2.VpcProps](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-ec2.VpcProps.html) | Optional custom properties for a VPC the construct will create. This VPC will be used by the new ALB and any Private Hosted Zone the construct creates (that's why loadBalancerProps and privateHostedZoneProps can't include a VPC). Providing both this and existingVpc is an error. |
| existingVpc? | [ec2.IVpc](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-ec2.IVpc.html) | An existing VPC in which to deploy the construct. Providing both this and vpcProps is an error. If the client provides an existing load balancer and/or existing Private Hosted Zone, those constructs must exist in this VPC. |
| clusterProps? | [ecs.ClusterProps](https://docs.aws.amazon.com/cdk/api/v1/docs/@aws-cdk_aws-ecs.ClusterProps.html) | Optional properties to create a new ECS cluster. To provide an existing cluster, use the cluster attribute of fargateServiceProps. |
| ecrRepositoryArn? | string | The arn of an ECR Repository containing the image to use to generate the containers. Either this or the image property of containerDefinitionProps must be provided. format: arn:aws:ecr:*region*:*account number*:repository/*Repository Name* |
| ecrImageVersion? | string | The version of the image to use from the repository. Defaults to 'Latest' |
| containerDefinitionProps? | [ecs.ContainerDefinitionProps \| any](https://docs.aws.amazon.com/cdk/api/v1/docs/@aws-cdk_aws-ecs.ContainerDefinitionProps.html) | Optional props to define the container created for the Fargate Service (defaults found in fargate-defaults.ts) |
| fargateTaskDefinitionProps? | [ecs.FargateTaskDefinitionProps \| any](https://docs.aws.amazon.com/cdk/api/v1/docs/@aws-cdk_aws-ecs.FargateTaskDefinitionProps.html) | Optional props to define the Fargate Task Definition for this construct (defaults found in fargate-defaults.ts) |
| fargateServiceProps? | [ecs.FargateServiceProps \| any](https://docs.aws.amazon.com/cdk/api/v1/docs/@aws-cdk_aws-ecs.FargateServiceProps.html) | Optional values to override default Fargate Task definition properties (fargate-defaults.ts). The construct will default to launching the service is the most isolated subnets available (precedence: Isolated, Private and Public). Override those and other defaults here. |
| existingFargateServiceObject? | [ecs.FargateService](https://docs.aws.amazon.com/cdk/api/v1/docs/@aws-cdk_aws-ecs.FargateService.html) | A Fargate Service already instantiated (probably by another Solutions Construct). If this is specified, then no props defining a new service can be provided, including: existingImageObject, ecrImageVersion, containerDefintionProps, fargateTaskDefinitionProps, ecrRepositoryArn, fargateServiceProps, clusterProps, existingClusterInterface |
| existingContainerDefinitionObject? | [ecs.ContainerDefinition](https://docs.aws.amazon.com/cdk/api/v1/docs/@aws-cdk_aws-ecs.ContainerDefinition.html) | A container definition already instantiated as part of a Fargate service. This must be the container in the existingFargateServiceObject |
|existingQueueObj?|[sqs.Queue](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-sqs.Queue.html)|An optional, existing SQS queue to be used instead of the default queue. Providing both this and queueProps will cause an error.|
|queueProps?|[sqs.QueueProps](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-sqs.QueueProps.html)|Optional user-provided properties to override the default properties for the SQS queue.|
|deployDeadLetterQueue?|boolean|Whether to create a secondary queue to be used as a dead letter queue. Defaults to `true`.|
|deadLetterQueueProps?|[sqs.QueueProps](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-sqs.QueueProps.html)|Optional user-provided props to override the default props for the dead letter queue. Only used if the `deployDeadLetterQueue` property is set to true.|
|maxReceiveCount?|integer|The number of times a message can be unsuccessfully dequeued before being moved to the dead letter queue. Defaults to `15`.|
|queueUrlEnvironmentVariableName?|string|Optional Name for the SQS queue name environment variable set for the container.|
|queuePermissions?|`string[]`|Optional queue permissions to grant to the Fargate service. One or more of the following may be specified: `Read`,`Write`. Default is `Write`|


## Pattern Properties

| **Name** | **Type** | **Description** |
|:-------------|:----------------|-----------------|
| vpc | [ec2.IVpc](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-ec2.IVpc.html) | The VPC used by the construct (whether created by the construct or provided by the client) |
| service | [ecs.FargateService](https://docs.aws.amazon.com/cdk/api/v1/docs/@aws-cdk_aws-ecs.FargateService.html) | The AWS Fargate service used by this construct (whether created by this construct or passed to this construct at initialization) |
| container | [ecs.ContainerDefinition](https://docs.aws.amazon.com/cdk/api/v1/docs/@aws-cdk_aws-ecs.ContainerDefinition.html) | The container associated with the AWS Fargate service in the service property. |
|sqsQueue|[sqs.Queue](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-sqs.Queue.html)|Returns an instance of the SQS queue created by the pattern. |
|deadLetterQueue?|[sqs.Queue](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-sqs.Queue.html)|Returns an instance of the dead letter queue created by the pattern, if one is deployed.|

## Default settings

Out of the box implementation of the Construct without any override will set the following defaults:

### AWS Fargate Service
* Sets up an AWS Fargate service
* Uses the existing service if provided
* Creates a new service if none provided.
* Service will run in isolated subnets if available, then private subnets if available and finally public subnets
* Adds environment variables to the container with the name of the SQS queue
* Add permissions to the container IAM role allowing it to publish to the SQS queue

### Amazon SQS Queue
* Sets up an Amazon SQS queue
* Uses an existing queue if one is provided, otherwise creates a new one
* Adds an Interface Endpoint to the VPC for SQS (the service by default runs in Isolated or Private subnets)

## Architecture
![Architecture Diagram](architecture.png)

***
&copy; Copyright 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
226 changes: 226 additions & 0 deletions source/patterns/@aws-solutions-constructs/aws-fargate-sqs/lib/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,226 @@
/**
* Copyright 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance
* with the License. A copy of the License is located at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES
* OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions
* and limitations under the License.
*/

import * as ec2 from "@aws-cdk/aws-ec2";
import * as sqs from "@aws-cdk/aws-sqs";
// Note: To ensure CDKv2 compatibility, keep the import statement for Construct separate
import { Construct } from "@aws-cdk/core";
import * as defaults from "@aws-solutions-constructs/core";
import * as ecs from "@aws-cdk/aws-ecs";

export interface FargateToSqsProps {
/**
* Optional custom properties for a VPC the construct will create. This VPC will
* be used by the new Fargate service the construct creates (that's
* why targetGroupProps can't include a VPC). Providing
* both this and existingVpc is an error. An SQS Interface
* endpoint will be included in this VPC.
*
* @default - none
*/
readonly vpcProps?: ec2.VpcProps;
/**
* An existing VPC in which to deploy the construct. Providing both this and
* vpcProps is an error. If the client provides an existing Fargate service,
* this value must be the VPC where the service is running. An SQS Interface
* endpoint will be added to this VPC.
*
* @default - none
*/
readonly existingVpc?: ec2.IVpc;
/**
* Whether the construct is deploying a private or public API. This has implications for the VPC deployed
* by this construct.
*
* @default - none
*/
readonly publicApi: boolean;
/**
* Optional properties to create a new ECS cluster
*/
readonly clusterProps?: ecs.ClusterProps;
/**
* The arn of an ECR Repository containing the image to use
* to generate the containers
*
* format:
* arn:aws:ecr:[region]:[account number]:repository/[Repository Name]
*/
readonly ecrRepositoryArn?: string;
/**
* The version of the image to use from the repository
*
* @default - 'latest'
*/
readonly ecrImageVersion?: string;
/*
* Optional props to define the container created for the Fargate Service
*
* defaults - fargate-defaults.ts
*/
readonly containerDefinitionProps?: ecs.ContainerDefinitionProps | any;
/*
* Optional props to define the Fargate Task Definition for this construct
*
* defaults - fargate-defaults.ts
*/
readonly fargateTaskDefinitionProps?: ecs.FargateTaskDefinitionProps | any;
/**
* Optional values to override default Fargate Task definition properties
* (fargate-defaults.ts). The construct will default to launching the service
* is the most isolated subnets available (precedence: Isolated, Private and
* Public). Override those and other defaults here.
*
* defaults - fargate-defaults.ts
*/
readonly fargateServiceProps?: ecs.FargateServiceProps | any;
/**
* A Fargate Service already instantiated (probably by another Solutions Construct). If
* this is specified, then no props defining a new service can be provided, including:
* existingImageObject, ecrImageVersion, containerDefintionProps, fargateTaskDefinitionProps,
* ecrRepositoryArn, fargateServiceProps, clusterProps, existingClusterInterface. If this value
* is provided, then existingContainerDefinitionObject must be provided as well.
*
* @default - none
*/
readonly existingFargateServiceObject?: ecs.FargateService;
/*
* A container definition already instantiated as part of a Fargate service. This must
* be the container in the existingFargateServiceObject.
*
* @default - None
*/
readonly existingContainerDefinitionObject?: ecs.ContainerDefinition;
/**
* Existing instance of SQS queue object, Providing both this and queueProps will cause an error.
*
* @default - Default props are used
*/
readonly existingQueueObj?: sqs.Queue;
/**
* Optional user-provided props to override the default props for the SQS queue.
*
* @default - Default props are used
*/
readonly queueProps?: sqs.QueueProps;
/**
* Optional user provided properties for the dead letter queue
*
* @default - Default props are used
*/
readonly deadLetterQueueProps?: sqs.QueueProps;
/**
* Whether to deploy a secondary queue to be used as a dead letter queue.
*
* @default - true.
*/
readonly deployDeadLetterQueue?: boolean;
/**
* The number of times a message can be unsuccessfully dequeued before being moved to the dead-letter queue.
*
* @default - required field if deployDeadLetterQueue=true.
*/
readonly maxReceiveCount?: number;
/**
* Optional Name for the SQS queue ARN environment variable to set for the container.
*
* @default - None
*/
readonly queueArnEnvironmentVariableName?: string;
/**
* Optional Name for the SQS queue name environment variable to set for the container.
*
* @default - None
*/
readonly queueUrlEnvironmentVariableName?: string;
/**
* Optional queue permissions to grant to the Fargate service. One or more of the following may be specified: `Read`,`Write`. Default is `Write`
*
* @default - Write
*/
readonly queuePermissions?: string[];
}

export class FargateToSqs extends Construct {
public readonly sqsQueue: sqs.Queue;
public readonly deadLetterQueue?: sqs.DeadLetterQueue;
public readonly service: ecs.FargateService;
public readonly vpc: ec2.IVpc;
public readonly container: ecs.ContainerDefinition;

constructor(scope: Construct, id: string, props: FargateToSqsProps) {
super(scope, id);
defaults.CheckProps(props);
defaults.CheckFargateProps(props);

this.vpc = defaults.buildVpc(scope, {
existingVpc: props.existingVpc,
defaultVpcProps: props.publicApi ? defaults.DefaultPublicPrivateVpcProps() : defaults.DefaultIsolatedVpcProps(),
userVpcProps: props.vpcProps,
constructVpcProps: { enableDnsHostnames: true, enableDnsSupport: true }
});

defaults.AddAwsServiceEndpoint(scope, this.vpc, defaults.ServiceEndpointTypes.SQS);

if (props.existingFargateServiceObject) {
this.service = props.existingFargateServiceObject;
// CheckFargateProps confirms that the container is provided
this.container = props.existingContainerDefinitionObject!;
} else {
[this.service, this.container] = defaults.CreateFargateService(
scope,
id,
this.vpc,
props.clusterProps,
props.ecrRepositoryArn,
props.ecrImageVersion,
props.fargateTaskDefinitionProps,
props.containerDefinitionProps,
props.fargateServiceProps
);
}

// Setup the dead letter queue, if applicable
this.deadLetterQueue = defaults.buildDeadLetterQueue(this, {
existingQueueObj: props.existingQueueObj,
deployDeadLetterQueue: props.deployDeadLetterQueue,
deadLetterQueueProps: props.deadLetterQueueProps,
maxReceiveCount: props.maxReceiveCount
});

// Setup the SQS Queue
[this.sqsQueue] = defaults.buildQueue(this, `${id}-queue`, {
queueProps: props.queueProps,
deadLetterQueue: this.deadLetterQueue,
existingQueueObj: props.existingQueueObj,
});

// Enable message send and receive permissions for Fargate service by default
if (props.queuePermissions) {
if (props.queuePermissions.includes('Read')) {
this.sqsQueue.grantConsumeMessages(this.service.taskDefinition.taskRole);
}
if (props.queuePermissions.includes('Write')) {
this.sqsQueue.grantSendMessages(this.service.taskDefinition.taskRole);
}
} else {
this.sqsQueue.grantSendMessages(this.service.taskDefinition.taskRole);
}

// Setting environment variables
const queueArnEnvironmentVariableName = props.queueArnEnvironmentVariableName || 'SQS_QUEUE_ARN';
this.container.addEnvironment(queueArnEnvironmentVariableName, this.sqsQueue.queueArn);
const queueUrlEnvironmentVariableName = props.queueUrlEnvironmentVariableName || 'SQS_QUEUE_URL';
this.container.addEnvironment(queueUrlEnvironmentVariableName, this.sqsQueue.queueUrl);
}
}
Loading

0 comments on commit f7ddf3f

Please sign in to comment.