Skip to content

Commit

Permalink
feat(cloud9): Support Cloud9 EC2 Environment (#6298)
Browse files Browse the repository at this point in the history
* clean commit

* add usage example

* update package.json

* remove jest from package.json

* add more tests

* minor syntax update

* add more tests

* fix issues

* update package.json

* add more tests and fix package.json format error

* update package.json

* update package.json

* fix issues from the review

* update README

* rename EnvironmentEc2 to Ec2Environment

* revert yarn.lock

* revert

* fix subnetSelection syntax

* fix json syntax error

Co-authored-by: Adam Ruka <[email protected]>
Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
  • Loading branch information
3 people authored Mar 20, 2020
1 parent cfecdb7 commit f50b876
Show file tree
Hide file tree
Showing 7 changed files with 636 additions and 0 deletions.
35 changes: 35 additions & 0 deletions packages/@aws-cdk/aws-cloud9/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,38 @@
<!--END STABILITY BANNER-->

This module is part of the [AWS Cloud Development Kit](https://github.com/aws/aws-cdk) project.

AWS Cloud9 is a cloud-based integrated development environment (IDE) that lets you write, run, and debug your code with just a browser. It includes a code editor, debugger, and terminal. Cloud9 comes prepackaged with essential tools for popular programming languages, including JavaScript, Python, PHP, and more, so you don’t need to install files or configure your development machine to start new projects. Since your Cloud9 IDE is cloud-based, you can work on your projects from your office, home, or anywhere using an internet-connected machine. Cloud9 also provides a seamless experience for developing serverless applications enabling you to easily define resources, debug, and switch between local and remote execution of serverless applications. With Cloud9, you can quickly share your development environment with your team, enabling you to pair program and track each other's inputs in real time.


### Creating EC2 Environment

EC2 Environments are defined with `Ec2Environment`. To create an EC2 environment in the private subnet, specify `subnetSelection` with private `subnetType`.


```ts
import * as cloud9 from '@aws-cdk/aws-cloud9';

// create a cloud9 ec2 environment in a new VPC
const vpc = new ec2.Vpc(this, 'VPC', { maxAzs: 3});
new cloud9.Ec2Environment(this, 'Cloud9Env', { vpc });

// or create the cloud9 environment in the default VPC with specific instanceType
const defaultVpc = ec2.Vpc.fromLookup(this, 'DefaultVPC', { isDefault: true });
new cloud9.Ec2Environment(this, 'Cloud9Env2', {
vpc: defaultVpc,
instanceType: new ec2.InstanceType('t3.large')
});

// or specify in a different subnetSelection
const c9env = new cloud9.Ec2Environment(this, 'Cloud9Env3', {
vpc,
subnetSelection: {
subnetType: ec2.SubnetType.PRIVATE
}
});

// print the Cloud9 IDE URL in the output
new cdk.CfnOutput(this, 'URL', { value: c9env.ideUrl });
```

135 changes: 135 additions & 0 deletions packages/@aws-cdk/aws-cloud9/lib/environment.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
import * as ec2 from '@aws-cdk/aws-ec2';
import * as cdk from '@aws-cdk/core';
import { CfnEnvironmentEC2 } from '../lib/cloud9.generated';

/**
* A Cloud9 Environment
*
*/
export interface IEc2Environment extends cdk.IResource {
/**
* The name of the EnvironmentEc2
*
* @attribute environmentEc2Name
*/
readonly ec2EnvironmentName: string;

/**
* The arn of the EnvironmentEc2
*
* @attribute environmentE2Arn
*/
readonly ec2EnvironmentArn: string;

}

/**
* Properties for Ec2Environment
*/
export interface Ec2EnvironmentProps {
/**
* The type of instance to connect to the environment.
*
* @default - t2.micro
*/
readonly instanceType?: ec2.InstanceType;

/**
* The subnetSelection of the VPC that AWS Cloud9 will use to communicate with
* the Amazon EC2 instance.
*
* @default - all public subnets of the VPC are selected.
*/
readonly subnetSelection?: ec2.SubnetSelection;

/**
* The VPC that AWS Cloud9 will use to communicate with the Amazon Elastic Compute Cloud (Amazon EC2) instance.
*
*/
readonly vpc: ec2.IVpc;

/**
* Name of the environment
*
* @default - automatically generated name
*/
readonly ec2EnvironmentName?: string;

/**
* Description of the environment
*
* @default - no description
*/
readonly description?: string;
}

/**
* A Cloud9 Environment with Amazon EC2
* @resource AWS::Cloud9::EnvironmentEC2
*/
export class Ec2Environment extends cdk.Resource implements IEc2Environment {
/**
* import from EnvironmentEc2Name
*/
public static fromEc2EnvironmentName(scope: cdk.Construct, id: string, ec2EnvironmentName: string): IEc2Environment {
class Import extends cdk.Resource {
public ec2EnvironmentName = ec2EnvironmentName;
public ec2EnvironmentArn = cdk.Stack.of(this).formatArn({
service: 'cloud9',
resource: 'environment',
resourceName: this.ec2EnvironmentName,
});
}
return new Import(scope, id);
}

/**
* The environment name of this Cloud9 environment
*
* @attribute
*/
public readonly ec2EnvironmentName: string;

/**
* The environment ARN of this Cloud9 environment
*
* @attribute
*/
public readonly ec2EnvironmentArn: string;

/**
* The environment ID of this Cloud9 environment
*/
public readonly environmentId: string;

/**
* The complete IDE URL of this Cloud9 environment
*/
public readonly ideUrl: string;

/**
* VPC ID
*/
public readonly vpc: ec2.IVpc;

constructor(scope: cdk.Construct, id: string, props: Ec2EnvironmentProps) {
super(scope, id);

this.vpc = props.vpc;
if (!props.subnetSelection && this.vpc.publicSubnets.length === 0) {
throw new Error('no subnetSelection specified and no public subnet found in the vpc, please specify subnetSelection');
}

const vpcSubnets = props.subnetSelection ?? { subnetType: ec2.SubnetType.PUBLIC };
const c9env = new CfnEnvironmentEC2(this, 'Resource', {
name: props.ec2EnvironmentName,
description: props.description,
instanceType: props.instanceType?.toString() ?? ec2.InstanceType.of(ec2.InstanceClass.BURSTABLE2, ec2.InstanceSize.MICRO).toString(),
subnetId: this.vpc.selectSubnets(vpcSubnets).subnetIds[0] ,
});
this.environmentId = c9env.ref;
this.ec2EnvironmentArn = c9env.getAtt('Arn').toString();
this.ec2EnvironmentName = c9env.getAtt('Name').toString();
this.ideUrl = `https://${this.stack.region}.console.aws.amazon.com/cloud9/ide/${this.environmentId}`;
}
}
1 change: 1 addition & 0 deletions packages/@aws-cdk/aws-cloud9/lib/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
// AWS::Cloud9 CloudFormation Resources:
export * from './cloud9.generated';
export * from './environment';
10 changes: 10 additions & 0 deletions packages/@aws-cdk/aws-cloud9/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -81,21 +81,31 @@
"devDependencies": {
"@aws-cdk/assert": "0.0.0",
"cdk-build-tools": "0.0.0",
"cdk-integ-tools": "0.0.0",
"cfn2ts": "0.0.0",
"pkglint": "0.0.0"
},
"dependencies": {
"@aws-cdk/core": "0.0.0",
"@aws-cdk/aws-ec2": "0.0.0",
"constructs": "^1.1.2"
},
"homepage": "https://github.com/aws/aws-cdk",
"peerDependencies": {
"@aws-cdk/core": "0.0.0",
"@aws-cdk/aws-ec2": "0.0.0",
"constructs": "^1.1.2"
},
"engines": {
"node": ">= 10.3.0"
},
"awslint": {
"exclude": [
"resource-attribute:@aws-cdk/aws-cloud9.Ec2Environment.environmentEc2Arn",
"resource-attribute:@aws-cdk/aws-cloud9.Ec2Environment.environmentEc2Name",
"props-physical-name:@aws-cdk/aws-cloud9.Ec2EnvironmentProps"
]
},
"stability": "experimental",
"awscdkio": {
"announce": false
Expand Down
69 changes: 69 additions & 0 deletions packages/@aws-cdk/aws-cloud9/test/cloud9.environment.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import { expect as expectCDK, haveResource } from '@aws-cdk/assert';
import * as ec2 from '@aws-cdk/aws-ec2';
import * as cdk from '@aws-cdk/core';
import * as cloud9 from '../lib';

let stack: cdk.Stack;
let vpc: ec2.IVpc;

beforeEach(() => {
stack = new cdk.Stack();
vpc = new ec2.Vpc(stack, 'VPC');
});

test('create resource correctly with only vpc provide', () => {
// WHEN
new cloud9.Ec2Environment(stack, 'C9Env', { vpc });
// THEN
expectCDK(stack).to(haveResource('AWS::Cloud9::EnvironmentEC2'));
});

test('create resource correctly with both vpc and subnetSelectio', () => {
// WHEN
new cloud9.Ec2Environment(stack, 'C9Env', {
vpc,
subnetSelection: {
subnetType: ec2.SubnetType.PRIVATE
}
});
// THEN
expectCDK(stack).to(haveResource('AWS::Cloud9::EnvironmentEC2'));
});

test('import correctly from existing environment', () => {
// WHEN
const c9env = cloud9.Ec2Environment.fromEc2EnvironmentName(stack, 'ImportedEnv', 'existingEnvName');
// THEN
expect(c9env).toHaveProperty('ec2EnvironmentName');
});

test('create correctly with instanceType specified', () => {
// WHEN
new cloud9.Ec2Environment(stack, 'C9Env', {
vpc,
instanceType: ec2.InstanceType.of(ec2.InstanceClass.C5, ec2.InstanceSize.LARGE)
});
// THEN
expectCDK(stack).to(haveResource('AWS::Cloud9::EnvironmentEC2'));
});

test('throw error when subnetSelection not specified and the provided VPC has no public subnets', () => {
// WHEN
const privateOnlyVpc = new ec2.Vpc(stack, 'PrivateOnlyVpc', {
maxAzs: 2,
subnetConfiguration: [
{
subnetType: ec2.SubnetType.ISOLATED,
name: 'IsolatedSubnet',
cidrMask: 24
}
]
});
// THEN
expect(() => {
new cloud9.Ec2Environment(stack, 'C9Env', {
vpc: privateOnlyVpc,
instanceType: ec2.InstanceType.of(ec2.InstanceClass.C5, ec2.InstanceSize.LARGE)
});
}).toThrow(/no subnetSelection specified and no public subnet found in the vpc, please specify subnetSelection/);
});
Loading

0 comments on commit f50b876

Please sign in to comment.