Skip to content
This repository was archived by the owner on Feb 24, 2022. It is now read-only.

Commit

Permalink
feat(efs): create EFS file systems (aws#6373)
Browse files Browse the repository at this point in the history
* feat(aws-efs): adding construct library for creating EFS

* 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

* addressing review comments

* setting correct ipaddress for mount targets

* feat(aws-efs): adding construct library for creating EFS

* 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

* addressing review comments

* setting correct ipaddress for mount targets

* address review comments v2

* removing mount targets info

Co-authored-by: Rico Huijbers <[email protected]>
Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
  • Loading branch information
2 people authored and horsmand committed Mar 9, 2020
1 parent b2e71dc commit b9b4ca3
Show file tree
Hide file tree
Showing 6 changed files with 552 additions and 8 deletions.
63 changes: 63 additions & 0 deletions packages/@aws-cdk/aws-efs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,67 @@
---
<!--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);
```
### Mounting the file system using User Data

In order to automatically mount this file system during instance launch,
following code can be used as reference:
```
const vpc = new ec2.Vpc(this, 'VPC');
const fileSystem = new efs.EfsFileSystem(this, 'EfsFileSystem', {
vpc,
encrypted: true,
lifecyclePolicy: efs.EfsLifecyclePolicyProperty.AFTER_14_DAYS,
performanceMode: efs.EfsPerformanceMode.GENERAL_PURPOSE,
throughputMode: efs.EfsThroughputMode.BURSTING
});
const inst = new Instance(this, 'inst', {
instanceType: InstanceType.of(InstanceClass.T2, InstanceSize.LARGE),
machineImage: new AmazonLinuxImage({
generation: AmazonLinuxGeneration.AMAZON_LINUX_2
}),
vpc,
vpcSubnets: {
subnetType: SubnetType.PUBLIC,
}
});
fileSystem.connections.allowDefaultPortFrom(inst);
inst.userData.addCommands("yum check-update -y", // Ubuntu: apt-get -y update
"yum upgrade -y", // Ubuntu: apt-get -y upgrade
"yum install -y amazon-efs-utils", // Ubuntu: apt-get -y install amazon-efs-utils
"yum install -y nfs-utils", // Ubuntu: apt-get -y install nfs-common
"file_system_id_1=" + fileSystem.fileSystemId,
"efs_mount_point_1=/mnt/efs/fs1",
"mkdir -p \"${efs_mount_point_1}\"",
"test -f \"/sbin/mount.efs\" && echo \"${file_system_id_1}:/ ${efs_mount_point_1} efs defaults,_netdev\" >> /etc/fstab || " +
"echo \"${file_system_id_1}.efs." + cdk.Stack.of(this).region + ".amazonaws.com:/ ${efs_mount_point_1} nfs4 nfsvers=4.1,rsize=1048576,wsize=1048576,hard,timeo=600,retrans=2,noresvport,_netdev 0 0\" >> /etc/fstab",
"mount -a -t efs,nfs4 defaults");
```

This module is part of the [AWS Cloud Development Kit](https://github.com/aws/aws-cdk) project.
282 changes: 282 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,282 @@
import * as ec2 from '@aws-cdk/aws-ec2';
import * as kms from '@aws-cdk/aws-kms';
import {Construct, Resource} from "@aws-cdk/core";
import {CfnFileSystem, CfnMountTarget} from "./efs.generated";

// tslint:disable: max-line-length
/**
* 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-elasticfilesystem-filesystem-lifecyclepolicies
*/
export enum EfsLifecyclePolicyProperty {
/**
* After 7 days of not being accessed.
*/
AFTER_7_DAYS,

/**
* After 14 days of not being accessed.
*/
AFTER_14_DAYS,

/**
* After 30 days of not being accessed.
*/
AFTER_30_DAYS,

/**
* After 60 days of not being accessed.
*/
AFTER_60_DAYS,

/**
* After 90 days of not being accessed.
*/
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"
}

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

/**
* Properties of EFS FileSystem.
*/
export interface EfsFileSystemProps {

/**
* 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 traffic
*/
readonly securityGroup?: ec2.ISecurityGroup;

/**
* Where to place the mount target within the VPC.
*
* @default - Private subnets
*/
readonly vpcSubnets?: ec2.SubnetSelection;

/**
* 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;

/**
* 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 IEfsFileSystem {

/**
* 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): IEfsFileSystem {
class Import extends EfsFileSystemBase implements IEfsFileSystem {
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 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, "Resource", {
encrypted: props.encrypted,
kmsKeyId: (props.kmsKey ? props.kmsKey.keyId : undefined),
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) => {
new CfnMountTarget(this,
"EfsMountTarget" + (++mountTargetCount),
{
fileSystemId: this.fileSystemId,
securityGroups: Array.of(securityGroup.securityGroupId),
subnetId
});
});
}
}
1 change: 1 addition & 0 deletions packages/@aws-cdk/aws-efs/lib/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
// AWS::EFS CloudFormation Resources:
export * from './efs-file-system';
export * from './efs.generated';
18 changes: 16 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,28 @@
"pkglint": "0.0.0"
},
"dependencies": {
"@aws-cdk/core": "0.0.0"
"@aws-cdk/core": "0.0.0",
"@aws-cdk/aws-ec2": "0.0.0",
"@aws-cdk/aws-kms": "0.0.0",
"@aws-cdk/cx-api": "0.0.0"
},
"homepage": "https://github.com/aws/aws-cdk",
"peerDependencies": {
"@aws-cdk/core": "0.0.0"
"@aws-cdk/core": "0.0.0",
"@aws-cdk/aws-ec2": "0.0.0",
"@aws-cdk/aws-kms": "0.0.0",
"@aws-cdk/cx-api": "0.0.0"
},
"engines": {
"node": ">= 10.3.0"
},
"awslint": {
"exclude": [
"props-physical-name:@aws-cdk/aws-efs.EfsFileSystemProps",
"resource-interface:@aws-cdk/aws-efs.EfsFileSystem",
"construct-interface-extends-iconstruct:@aws-cdk/aws-efs.IEfsFileSystem",
"resource-interface-extends-resource:@aws-cdk/aws-efs.IEfsFileSystem"
]
},
"stability": "experimental"
}
Loading

0 comments on commit b9b4ca3

Please sign in to comment.