Skip to content

Commit

Permalink
feat(aws-efs): adding construct library for creating EFS
Browse files Browse the repository at this point in the history
* 100% unit test coverage.
* This was tested by creating an EFS using this construct in a cdk application.
  A instance was also created in this app, which successfully mounted it.

closes aws#6286
  • Loading branch information
yashda committed Feb 19, 2020
1 parent 747bdb2 commit 9b208a6
Show file tree
Hide file tree
Showing 7 changed files with 544 additions and 8 deletions.
25 changes: 25 additions & 0 deletions packages/@aws-cdk/aws-efs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,29 @@
---
<!--END STABILITY BANNER-->

This construct library allows you to set up AWS Elastic File System (EFS).

```ts
import efs = require('@aws-cdk/aws-efs');

const myVpc = new ec2.Vpc(this, 'VPC');
const fileSystem = new efs.EfsFileSystem(this, 'MyEfsFileSystem', {
vpc: myVpc,
encrypted: true,
lifecyclePolicy: EfsLifecyclePolicyProperty.AFTER_14_DAYS,
performanceMode: EfsPerformanceMode.GENERAL_PURPOSE,
throughputMode: EfsThroughputMode.BURSTING
});
```

### Connecting

To control who can access the EFS, use the `.connections` attribute. EFS has
a fixed default port, so you don't need to specify the port:

```ts
fileSystem.connections.allowDefaultPortFrom(instance);
```


This module is part of the [AWS Cloud Development Kit](https://github.com/aws/aws-cdk) project.
260 changes: 260 additions & 0 deletions packages/@aws-cdk/aws-efs/lib/efs-file-system.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,260 @@
import * as ec2 from '@aws-cdk/aws-ec2';
import * as kms from '@aws-cdk/aws-kms';
import {Construct, Resource, Tag} from "@aws-cdk/core";
import {CfnFileSystem, CfnMountTarget} from "./efs.generated";
import {FileSystemProps, IFileSystem} from "./file-system";

/**
* EFS Lifecycle Policy, if a file is not accessed for given days, it will move to EFS Infrequent Access.
*
* @see http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-efs-filesystem.html#cfn-efs-filesystem-performancemode
*/
export enum EfsLifecyclePolicyProperty {
/**
* After 7 days of inaccessibility.
*/
AFTER_7_DAYS,

/**
* After 14 days of inaccessibility.
*/
AFTER_14_DAYS,

/**
* After 30 days of inaccessibility.
*/
AFTER_30_DAYS,

/**
* After 60 days of inaccessibility.
*/
AFTER_60_DAYS,

/**
* After 90 days of inaccessibility.
*/
AFTER_90_DAYS
}

/**
* EFS Performance mode.
*
* @see http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-efs-filesystem.html#cfn-efs-filesystem-performancemode
*/
export enum EfsPerformanceMode {
/**
* This is the general purpose performance mode for most file systems.
*/
GENERAL_PURPOSE = "generalPurpose",

/**
* This performance mode can scale to higher levels of aggregate throughput and operations per second with a
* tradeoff of slightly higher latencies.
*/
MAX_IO = "maxIO"
}

/**
* EFS Throughput mode.
*
* @see http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-efs-filesystem.html#cfn-elasticfilesystem-filesystem-throughputmode
*/
export enum EfsThroughputMode {
/**
* This mode on Amazon EFS scales as the size of the file system in the standard storage class grows.
*/
BURSTING = "bursting",

/**
* This mode can instantly provision the throughput of the file system (in MiB/s) independent of the amount of data stored.
*/
PROVISIONED = "provisioned"
}

/**
* Properties of EFS FileSystem.
*/
export interface EfsFileSystemProps extends FileSystemProps {
/**
* Defines if the data at rest in the file system is encrypted or not.
*
* @default - false
*/
readonly encrypted?: boolean;

/**
* The KMS key used for encryption. This is required to encrypt the data at rest if @encrypted is set to true.
*
* @default - if @encrypted is true, the default key for EFS (/aws/elasticfilesystem) is used
*/
readonly kmsKey?: kms.IKey;

/**
* The key value pair added to the File system.
*
* @default - no tags will be added
*/
readonly fileSystemTags?: Tag[];

/**
* A policy used by EFS lifecycle management to transition files to the Infrequent Access (IA) storage class.
*
* @default - none
*/
readonly lifecyclePolicy?: EfsLifecyclePolicyProperty;

/**
* Enum to mention the performance mode of the file system.
*
* @default - GENERAL_PURPOSE
*/
readonly performanceMode?: EfsPerformanceMode;

/**
* Enum to mention the throughput mode of the file system.
*
* @default - BURSTING
*/
readonly throughputMode?: EfsThroughputMode;

/**
* Provisioned throughput for the file system. This is a required property if the throughput mode is set to PROVISIONED.
* Valid values are 1-1024.
*
* @default - None, errors out
*/
readonly provisionedThroughputInMibps?: number;
}

/**
* A new or imported EFS File System.
*/
abstract class EFSFileSystemBase extends Resource implements IFileSystem {

/**
* The security groups/rules used to allow network connections to the file system.
*/
public abstract readonly connections: ec2.Connections;

/**
* @attribute
*/
public abstract readonly fileSystemID: string;
}

/**
* Properties that describe an existing EFS file system.
*/
export interface EfsFileSystemAttributes {
/**
* The security group of the file system
*/
readonly securityGroup: ec2.ISecurityGroup;

/**
* The File System's ID.
*/
readonly fileSystemID: string;
}

/**
* The Elastic File System implementation of IFileSystem.
* It creates a new, empty file system in Amazon Elastic File System (Amazon EFS).
* It also creates mount target (AWS::EFS::MountTarget) implicitly to mount the
* EFS file system on an Amazon Elastic Compute Cloud (Amazon EC2) instance or another resource.
*
* @see https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-efs-filesystem.html
*
* @resource AWS::EFS::FileSystem
*/
export class EfsFileSystem extends EFSFileSystemBase {

/**
* Import an existing File System from the given properties.
*/
public static fromEfsFileSystemAttributes(scope: Construct, id: string, attrs: EfsFileSystemAttributes): IFileSystem {
class Import extends EFSFileSystemBase implements IFileSystem {
public readonly fileSystemID = attrs.fileSystemID;
public readonly connections = new ec2.Connections({
securityGroups: [attrs.securityGroup],
defaultPort: ec2.Port.tcp(EfsFileSystem.DEFAULT_PORT)
});
}

return new Import(scope, id);
}

/**
* The default port File System listens on.
*/
private static readonly DEFAULT_PORT: number = 2049;

/**
* The security groups/rules used to allow network connections to the file system.
*/
public readonly connections: ec2.Connections;

/**
* @attribute
*/
public readonly fileSystemID: string;

private readonly mountTargets: CfnMountTarget[] = [];
private readonly efsFileSystem: CfnFileSystem;

/**
* Constructor for creating a new EFS FileSystem.
*/
constructor(scope: Construct, id: string, props: EfsFileSystemProps) {
super(scope, id);

if (props.throughputMode === EfsThroughputMode.PROVISIONED) {
if (props.provisionedThroughputInMibps === undefined) {
throw new Error('Property provisionedThroughputInMibps is required when throughputMode is PROVISIONED');
} else if (!Number.isInteger(props.provisionedThroughputInMibps)) {
throw new Error("Invalid input for provisionedThroughputInMibps");
} else if (props.provisionedThroughputInMibps < 1 || props.provisionedThroughputInMibps > 1024) {
this.node.addWarning("Valid values for throughput are 1-1024 MiB/s. You can get this limit increased by contacting AWS Support.");
}
}

this.efsFileSystem = new CfnFileSystem(this, "FileSystem", {
encrypted: props.encrypted,
kmsKeyId: (props.kmsKey ? props.kmsKey.keyId : undefined),
fileSystemTags: props.fileSystemTags,
lifecyclePolicies: (props.lifecyclePolicy ? Array.of({
transitionToIa: EfsLifecyclePolicyProperty[props.lifecyclePolicy]
} as CfnFileSystem.LifecyclePolicyProperty) : undefined),
performanceMode: props.performanceMode,
throughputMode: props.throughputMode,
provisionedThroughputInMibps: props.provisionedThroughputInMibps
});

this.fileSystemID = this.efsFileSystem.ref;
this.node.defaultChild = this.efsFileSystem;

const securityGroup = (props.securityGroup || new ec2.SecurityGroup(this, 'EfsSecurityGroup', {
vpc: props.vpc
}));

this.connections = new ec2.Connections({
securityGroups: [securityGroup],
defaultPort: ec2.Port.tcp(EfsFileSystem.DEFAULT_PORT)
});

const subnets = props.vpc.selectSubnets(props.vpcSubnets);

// We now have to create the mount target for each of the mentioned subnet
let mountTargetCount = 0;
subnets.subnetIds.forEach((subnetId: string) => {
const efsMountTarget = new CfnMountTarget(this,
"EfsMountTarget" + (++mountTargetCount),
{
fileSystemId: this.fileSystemID,
securityGroups: Array.of(securityGroup.securityGroupId),
subnetId
});
this.mountTargets.push(efsMountTarget);
});
}
}
38 changes: 38 additions & 0 deletions packages/@aws-cdk/aws-efs/lib/file-system.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import * as ec2 from '@aws-cdk/aws-ec2';
import {IResource} from "@aws-cdk/core";

/**
* Interface to implement AWS File Systems.
*/
export interface IFileSystem extends IResource, ec2.IConnectable {
/**
* The ID of the file system, assigned by Amazon EFS.
*
* @attribute
*/
readonly fileSystemID: string;
}

/**
* Properties of FileSystem
*/
export interface FileSystemProps {
/**
* VPC to launch the file system in.
*/
readonly vpc: ec2.IVpc;

/**
* Security Group to assign to this file system.
*
* @default - creates new security group which allow all out bound trafficcloudformation
*/
readonly securityGroup?: ec2.ISecurityGroup;

/**
* Where to place the mount target within the VPC.
*
* @default - Private subnets
*/
readonly vpcSubnets?: ec2.SubnetSelection;
}
2 changes: 2 additions & 0 deletions packages/@aws-cdk/aws-efs/lib/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
// AWS::EFS CloudFormation Resources:
export * from './file-system';
export * from './efs-file-system';
export * from './efs.generated';
16 changes: 14 additions & 2 deletions packages/@aws-cdk/aws-efs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -85,14 +85,26 @@
"pkglint": "1.24.0"
},
"dependencies": {
"@aws-cdk/core": "1.24.0"
"@aws-cdk/core": "1.24.0",
"@aws-cdk/aws-ec2": "1.24.0",
"@aws-cdk/aws-kms": "1.24.0",
"@aws-cdk/cx-api": "1.24.0"
},
"homepage": "https://github.com/aws/aws-cdk",
"peerDependencies": {
"@aws-cdk/core": "1.24.0"
"@aws-cdk/core": "1.24.0",
"@aws-cdk/aws-ec2": "1.24.0",
"@aws-cdk/aws-kms": "1.24.0",
"@aws-cdk/cx-api": "1.24.0"
},
"engines": {
"node": ">= 10.3.0"
},
"awslint": {
"exclude": [
"props-physical-name:@aws-cdk/aws-efs.EfsFileSystemProps",
"resource-interface:@aws-cdk/aws-efs.EfsFileSystem"
]
},
"stability": "experimental"
}
Loading

0 comments on commit 9b208a6

Please sign in to comment.