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

feat: construct library for ECS #1041

Closed
wants to merge 113 commits into from
Closed
Changes from 1 commit
Commits
Show all changes
113 commits
Select commit Hold shift + click to select a range
939452c
Ignore swp files
SoManyHs Aug 23, 2018
7302a2f
Add ECS lib and example
SoManyHs Aug 23, 2018
4c7127a
Add service and task def constructs
SoManyHs Aug 24, 2018
98749ec
WIP adding VPC/ASG
SoManyHs Aug 24, 2018
6d0942f
Refactor ECS demo
Aug 24, 2018
bddfe56
Add more props on Service
SoManyHs Aug 24, 2018
3574611
Add TODO for AMI IDs
SoManyHs Sep 6, 2018
a63c377
Weird hack?
SoManyHs Sep 7, 2018
b00e0dd
Update example to instantiate service separately
SoManyHs Sep 7, 2018
80ba2e4
WIP Add Container Definitions
SoManyHs Sep 12, 2018
0980f52
WIP Add other Task Def properties
SoManyHs Sep 14, 2018
8648540
Merge remote-tracking branch 'official/master' into ecs-demo
Sep 28, 2018
2d53d89
Indentation 4 -> 2
Sep 28, 2018
dbdab86
Fix type errors
Sep 28, 2018
ee6b427
Use SSM parameter for ECS Optimized AMI
SoManyHs Sep 28, 2018
2b16dad
Move containerdefinition to its own file
Sep 28, 2018
16a92c0
Merge branch 'ecs-demo' of github.com:SoManyHs/aws-cdk into ecs-demo
Sep 28, 2018
ae881c2
Turning ContainerDefinition into a construct
Sep 28, 2018
0672e6a
Add enum for placement constraints
SoManyHs Oct 1, 2018
4f93a5b
Rename fleet to autoScalingGroup
SoManyHs Oct 1, 2018
9b61b89
Qualify "PlacementConstraintType" usage with package name
Oct 1, 2018
98bf81b
ECR can return itself as container image
Oct 1, 2018
ae1d525
Automatically generate execution and task roles
Oct 1, 2018
23072a9
Make taskRole public
Oct 1, 2018
e5cc7f2
Remove shellCommand from healthCheck
SoManyHs Oct 1, 2018
047eed8
Rename toContainerDefinitionJson to renderContainerDefinition
SoManyHs Oct 1, 2018
65d3f79
Some work on Service
Oct 1, 2018
11541d3
Merge branch 'ecs-demo' of github.com:SoManyHs/aws-cdk into ecs-demo
Oct 1, 2018
fb17532
Separate Fargate and ECS clusters
SoManyHs Oct 1, 2018
be8cb86
Separate ECS and Fargate Service
SoManyHs Oct 1, 2018
b8cce3a
Separate ECS/Fargate TaskDefinition
SoManyHs Oct 1, 2018
2741608
Fix lint errors
SoManyHs Oct 1, 2018
a901912
Fix lint errors
SoManyHs Oct 1, 2018
a91bda7
Merge branch 'ecs-demo' of github.com:SoManyHs/aws-cdk into ecs-demo
Oct 2, 2018
51adff5
Fix bugs in service and task def
SoManyHs Oct 2, 2018
70ae682
Fix AMI ID getter for ECS clusters
SoManyHs Oct 2, 2018
298e0a7
Add Capabilities in Linux Parameters on ContainerDefs
SoManyHs Oct 2, 2018
1541edd
Add Capabilities in Linux Parameters on ContainerDefs
SoManyHs Oct 2, 2018
a154d09
Add default arg to SSMProvider.getString
SoManyHs Oct 2, 2018
4b46685
Add Devices to LinuxParameters
SoManyHs Oct 2, 2018
9875c6e
Add Tmpfs to LinuxParameters
SoManyHs Oct 2, 2018
1044d1c
Update ecs demo with full Linux Parameters example
SoManyHs Oct 2, 2018
e7f8000
Rename renderLogDriver
SoManyHs Oct 2, 2018
e7f7b7d
Do most of Service
Oct 2, 2018
dda869d
Merge branch 'ecs-demo' of github.com:SoManyHs/aws-cdk into ecs-demo
Oct 2, 2018
97083dc
Add Ulimits
SoManyHs Oct 2, 2018
a3e3615
Fix log driver
SoManyHs Oct 2, 2018
b913f0e
Make add* functions consistently plural
SoManyHs Oct 2, 2018
fd21c0d
Make load balancers respect network mode
Oct 2, 2018
3b4695c
Merge branch 'ecs-demo' of github.com:SoManyHs/aws-cdk into ecs-demo
Oct 2, 2018
187f62b
Make add* functions consistently plural
SoManyHs Oct 2, 2018
6912e3f
Add portMappings to ContainerDefinition
SoManyHs Oct 2, 2018
2bf2d57
Missing semicolons
SoManyHs Oct 2, 2018
f477d8d
Add MountPoints to ContainerDefinition
SoManyHs Oct 2, 2018
b947f51
Change 'addContainer' to take the props and return ContainerDefinition.
Oct 2, 2018
ab83d35
Merge branch 'ecs-demo' of github.com:SoManyHs/aws-cdk into ecs-demo
Oct 2, 2018
30cb925
Add VolumesFrom to ContainerDefinition
SoManyHs Oct 2, 2018
c64e184
Add fargate example for demo purposes
SoManyHs Oct 3, 2018
566aac3
Remove Service Role, fix DesiredCount default, make task memory defin…
Oct 3, 2018
96e3105
Merge branch 'ecs-demo' of github.com:SoManyHs/aws-cdk into ecs-demo
Oct 3, 2018
48cd8d4
Starting on port mapping defaults
Oct 3, 2018
e311293
Add validation on port mappings with tests
SoManyHs Oct 3, 2018
4ffed09
Implement ingressPort method
SoManyHs Oct 3, 2018
97d3db3
Make ALB listener ACTUALLY default to 'true'
Oct 4, 2018
4f9b548
Add dependency on listener
SoManyHs Oct 4, 2018
e8aa186
Integ test for Fargate service
SoManyHs Oct 4, 2018
eb4a562
Merge branch 'ecs-demo' of github.com:SoManyHs/aws-cdk into ecs-demo
Oct 4, 2018
e06a3ec
Add VolumesFrom to ContainerDefinition
SoManyHs Oct 2, 2018
95b8d84
Add fargate example for demo purposes
SoManyHs Oct 3, 2018
5595707
Starting on port mapping defaults
Oct 3, 2018
a1b78f4
Make ALB listener ACTUALLY default to 'true'
Oct 4, 2018
6f08406
Add validation on port mappings with tests
SoManyHs Oct 3, 2018
5312362
Implement ingressPort method
SoManyHs Oct 3, 2018
6f42026
Add dependency on listener
SoManyHs Oct 4, 2018
1983858
Integ test for Fargate service
SoManyHs Oct 4, 2018
4ea6b8b
hello-cdk-fargate example works
SoManyHs Oct 4, 2018
e34d515
Add lazy evaluation of network configuration property
SoManyHs Oct 4, 2018
bf90c27
Add LoadBalancedFargateService L3 construct
SoManyHs Oct 4, 2018
ffbad9f
Start LoadBalancedFargateServiceApplet
SoManyHs Oct 4, 2018
d1b22bd
Application AutoScaling WIP
Oct 4, 2018
7840634
Merge branch 'ecs-demo' of github.com:SoManyHs/aws-cdk into ecs-demo
Oct 4, 2018
d1ec640
Make declarative example work
Oct 4, 2018
36e1ba2
Add import exports to Cluster
SoManyHs Oct 5, 2018
178898f
Fix portMappings/ingressPort
SoManyHs Oct 5, 2018
b73bdad
Clean up fargate integ test
SoManyHs Oct 5, 2018
049557e
Add integ test for load balanced service in bridge mode
SoManyHs Oct 5, 2018
2295ef2
Add integ test for awsvpc network mode on EcsCluster
SoManyHs Oct 5, 2018
f34c23c
Fix ingress/containerPort on LBs
SoManyHs Oct 5, 2018
c491a1d
Cleanup example
SoManyHs Oct 5, 2018
430f81e
Add cloudwatch metrics
SoManyHs Oct 5, 2018
dca6972
Bridge mode works with host port specified
SoManyHs Oct 5, 2018
cf528d6
Merge remote-tracking branch 'origin/master' into ecs-demo
Oct 12, 2018
ef51786
Fix libs to be in line with latest API changes from master
Oct 12, 2018
c3b543a
Merge remote-tracking branch 'origin/master' into ecs-demo
Oct 15, 2018
64b6d06
Fix build errors
Oct 17, 2018
af5a2a4
Merge remote-tracking branch 'origin/master' into ecs-demo
Oct 17, 2018
51097fd
Fix ingressPort/containerPort tests
SoManyHs Oct 22, 2018
7b19dd2
Add LoadBalancedEcsService L3 Construct
SoManyHs Oct 22, 2018
9bea6be
Add ability to specify cluster size
SoManyHs Oct 24, 2018
4ab455c
Merge remote-tracking branch 'origin/master' into ecs-demo
Oct 26, 2018
be9f447
feat(aws-ecs): add Task AutoScaling to Service
Oct 26, 2018
2eab513
Add unit tests for EcsCluster
SoManyHs Oct 26, 2018
57857a9
Merge branch 'master' into ecs-demo
SoManyHs Oct 29, 2018
4cf23fc
Unit tests for Cluster
SoManyHs Oct 29, 2018
37d3c4e
Set volumes on TaskDefinition
SoManyHs Oct 30, 2018
1ad3669
Clarify doc string for memoryLimit and memoryReservation
SoManyHs Oct 30, 2018
78d2a49
WIP Unit tests on EcsTaskDefinition
SoManyHs Oct 30, 2018
ca61a74
WIP Unit tests for EcsService
SoManyHs Oct 30, 2018
98345b3
WIP Unit tests for FargateService
SoManyHs Oct 30, 2018
35ebd9c
WIP unit tests Fargate Task Definition
SoManyHs Oct 30, 2018
09a182a
Merge branch 'huijbers/autoscaling' into ecs-demo
Oct 30, 2018
2fd2e7c
Add docs and readme and a couple of tests
Oct 30, 2018
9825127
Unit test for error case on service creation
SoManyHs Oct 30, 2018
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
Prev Previous commit
Next Next commit
Indentation 4 -> 2
Rico Huijbers committed Sep 28, 2018
commit 2d53d89f7480bfab41bc2295f4a871a244cac88f
204 changes: 102 additions & 102 deletions packages/@aws-cdk/aws-ecs/lib/cluster.ts
Original file line number Diff line number Diff line change
@@ -4,45 +4,45 @@ import cdk = require('@aws-cdk/cdk');
import { cloudformation, ClusterArn} from './ecs.generated';

export interface ClusterProps {
/**
* A name for the cluster.
*
* @default CloudFormation-generated name
*/
clusterName?: string;

/**
* The VPC where your ECS instances will be running
*/
vpc: ec2.VpcNetworkRef;

/**
* Whether or not the containers can access the instance role
*
* @default false
*/
containersAccessInstanceRole?: boolean;
/**
* A name for the cluster.
*
* @default CloudFormation-generated name
*/
clusterName?: string;

/**
* The VPC where your ECS instances will be running
*/
vpc: ec2.VpcNetworkRef;

/**
* Whether or not the containers can access the instance role
*
* @default false
*/
containersAccessInstanceRole?: boolean;
}

// TODO: replace this with call to SSM, stored as "/aws/service/ecs/optimized-ami/amazon-linux/recommended"
// https://raw.githubusercontent.com/awslabs/aws-cloudformation-templates/master/aws/services/ECS/EC2LaunchType/clusters/public-vpc.yml
export const ECS_OPTIMIZED_AMI = new ec2.GenericLinuxImage({
'us-east-2': 'ami-028a9de0a7e353ed9',
'us-east-1': 'ami-00129b193dc81bc31',
'us-west-2': 'ami-00d4f478',
'us-west-1': 'ami-0d438d09af26c9583',
'eu-west-2': 'ami-a44db8c3',
'eu-west-3': 'ami-07da674f0655ef4e1',
'eu-west-1': 'ami-0af844a965e5738db',
'eu-central-1': 'ami-0291ba887ba0d515f',
'ap-northeast-2': 'ami-047d2a61f94f862dc',
'ap-northeast-1': 'ami-0041c416aa23033a2',
'ap-southeast-2': 'ami-0092e55c70015d8c3',
'ap-southeast-1': 'ami-091bf462afdb02c60',
'ca-central-1': 'ami-192fa27d',
'ap-south-1': 'ami-0c179ca015d301829',
'sa-east-1': 'ami-0018ff8ee48970ac3',
'us-gov-west-1': 'ami-c6079ba7',
'us-east-2': 'ami-028a9de0a7e353ed9',
'us-east-1': 'ami-00129b193dc81bc31',
'us-west-2': 'ami-00d4f478',
'us-west-1': 'ami-0d438d09af26c9583',
'eu-west-2': 'ami-a44db8c3',
'eu-west-3': 'ami-07da674f0655ef4e1',
'eu-west-1': 'ami-0af844a965e5738db',
'eu-central-1': 'ami-0291ba887ba0d515f',
'ap-northeast-2': 'ami-047d2a61f94f862dc',
'ap-northeast-1': 'ami-0041c416aa23033a2',
'ap-southeast-2': 'ami-0092e55c70015d8c3',
'ap-southeast-1': 'ami-091bf462afdb02c60',
'ca-central-1': 'ami-192fa27d',
'ap-south-1': 'ami-0c179ca015d301829',
'sa-east-1': 'ami-0018ff8ee48970ac3',
'us-gov-west-1': 'ami-c6079ba7',
});

// Needs to inherit from CloudFormationToken to make the string substitution
@@ -52,74 +52,74 @@ export class ClusterName extends cdk.CloudFormationToken {

export class Cluster extends cdk.Construct {

public readonly clusterArn: ClusterArn;

public readonly clusterName: ClusterName;

public readonly fleet: autoscaling.AutoScalingGroup;

constructor(parent: cdk.Construct, name: string, props: ClusterProps) {
super(parent, name);

const cluster = new cloudformation.ClusterResource(this, "Resource", {clusterName: props.clusterName});

this.clusterArn = cluster.clusterArn;

this.clusterName = new ClusterName(cluster.ref);

const fleet = new autoscaling.AutoScalingGroup(this, 'MyASG', {
vpc: props.vpc,
instanceType: new ec2.InstanceTypePair(ec2.InstanceClass.M4, ec2.InstanceSize.XLarge),
machineImage: ECS_OPTIMIZED_AMI,
updateType: autoscaling.UpdateType.ReplacingUpdate
});

// Tie instances to cluster
fleet.addUserData(`echo ECS_CLUSTER=${this.clusterName} >> /etc/ecs/ecs.config`);

if (!props.containersAccessInstanceRole) {
// Deny containers access to instance metadata service
// Source: https://docs.aws.amazon.com/AmazonECS/latest/developerguide/instance_IAM_role.html
fleet.addUserData('sudo iptables --insert FORWARD 1 --in-interface docker+ --destination 169.254.169.254/32 --jump DROP');
fleet.addUserData('sudo service iptables save');
}

// Note: if the fleet doesn't launch or doesn't register itself with
// ECS, *Cluster* stabilization will fail after timing our for an hour
// or so, because the *Service* doesn't have any running instances.
// During this time, you CANNOT DO ANYTHING ELSE WITH YOUR STACK.
//
// Apart from the weird relationship here between Cluster and Service
// (why is Cluster failing and not Service?), the experience is...
//
// NOT GREAT.
//
// Also, there's sort of a bidirectional dependency between Cluster and ASG:
//
// - ASG depends on Cluster to get the ClusterName (which needs to go into
// UserData).
// - Cluster depends on ASG to boot up, so the service is launched, so the
// Cluster can stabilize.

// ECS instances must be able to do these things
// Source: https://docs.aws.amazon.com/AmazonECS/latest/developerguide/instance_IAM_role.html
fleet.addToRolePolicy(new cdk.PolicyStatement().addActions(
"ecs:CreateCluster",
"ecs:DeregisterContainerInstance",
"ecs:DiscoverPollEndpoint",
"ecs:Poll",
"ecs:RegisterContainerInstance",
"ecs:StartTelemetrySession",
"ecs:Submit*",
"ecr:GetAuthorizationToken",
"ecr:BatchCheckLayerAvailability",
"ecr:GetDownloadUrlForLayer",
"ecr:BatchGetImage",
"logs:CreateLogStream",
"logs:PutLogEvents"
).addAllResources()); // Conceivably we might do better than all resources and add targeted ARNs

this.fleet = fleet;
public readonly clusterArn: ClusterArn;

public readonly clusterName: ClusterName;

public readonly fleet: autoscaling.AutoScalingGroup;

constructor(parent: cdk.Construct, name: string, props: ClusterProps) {
super(parent, name);

const cluster = new cloudformation.ClusterResource(this, "Resource", {clusterName: props.clusterName});

this.clusterArn = cluster.clusterArn;

this.clusterName = new ClusterName(cluster.ref);

const fleet = new autoscaling.AutoScalingGroup(this, 'MyASG', {
vpc: props.vpc,
instanceType: new ec2.InstanceTypePair(ec2.InstanceClass.M4, ec2.InstanceSize.XLarge),
machineImage: ECS_OPTIMIZED_AMI,
updateType: autoscaling.UpdateType.ReplacingUpdate
});

// Tie instances to cluster
fleet.addUserData(`echo ECS_CLUSTER=${this.clusterName} >> /etc/ecs/ecs.config`);

if (!props.containersAccessInstanceRole) {
// Deny containers access to instance metadata service
// Source: https://docs.aws.amazon.com/AmazonECS/latest/developerguide/instance_IAM_role.html
fleet.addUserData('sudo iptables --insert FORWARD 1 --in-interface docker+ --destination 169.254.169.254/32 --jump DROP');
fleet.addUserData('sudo service iptables save');
}

// Note: if the fleet doesn't launch or doesn't register itself with
// ECS, *Cluster* stabilization will fail after timing our for an hour
// or so, because the *Service* doesn't have any running instances.
// During this time, you CANNOT DO ANYTHING ELSE WITH YOUR STACK.
//
// Apart from the weird relationship here between Cluster and Service
// (why is Cluster failing and not Service?), the experience is...
//
// NOT GREAT.
//
// Also, there's sort of a bidirectional dependency between Cluster and ASG:
//
// - ASG depends on Cluster to get the ClusterName (which needs to go into
// UserData).
// - Cluster depends on ASG to boot up, so the service is launched, so the
// Cluster can stabilize.

// ECS instances must be able to do these things
// Source: https://docs.aws.amazon.com/AmazonECS/latest/developerguide/instance_IAM_role.html
fleet.addToRolePolicy(new cdk.PolicyStatement().addActions(
"ecs:CreateCluster",
"ecs:DeregisterContainerInstance",
"ecs:DiscoverPollEndpoint",
"ecs:Poll",
"ecs:RegisterContainerInstance",
"ecs:StartTelemetrySession",
"ecs:Submit*",
"ecr:GetAuthorizationToken",
"ecr:BatchCheckLayerAvailability",
"ecr:GetDownloadUrlForLayer",
"ecr:BatchGetImage",
"logs:CreateLogStream",
"logs:PutLogEvents"
).addAllResources()); // Conceivably we might do better than all resources and add targeted ARNs

this.fleet = fleet;

}
}
146 changes: 73 additions & 73 deletions packages/@aws-cdk/aws-ecs/lib/service.ts
Original file line number Diff line number Diff line change
@@ -4,89 +4,89 @@ import { cloudformation } from './ecs.generated';
import { TaskDefinitionArn } from './task-definition';

export interface ServiceProps {
/**
* Cluster where service will be deployed
*/
cluster: ClusterName; // should be required? do we assume 'default' exists?
/**
* Cluster where service will be deployed
*/
cluster: ClusterName; // should be required? do we assume 'default' exists?

/**
* Task Definition used for running tasks in the service
*/
taskDefinition: TaskDefinitionArn;
/**
* Task Definition used for running tasks in the service
*/
taskDefinition: TaskDefinitionArn;

/**
* Number of desired copies of running tasks
*
* @default 1
*/
desiredCount?: number;
/**
* Number of desired copies of running tasks
*
* @default 1
*/
desiredCount?: number;

/**
* A name for the service.
*
* @default CloudFormation-generated name
*/
serviceName?: string;
/**
* A name for the service.
*
* @default CloudFormation-generated name
*/
serviceName?: string;

/**
* Whether the service is hosted in EC2 or Fargate
*
* @default EC2
*/
launchType?: string; // maybe unnecessary if we have different ECS vs FG service
/**
* Whether the service is hosted in EC2 or Fargate
*
* @default EC2
*/
launchType?: string; // maybe unnecessary if we have different ECS vs FG service

/**
* The maximum number of tasks, specified as a percentage of the Amazon ECS
* service's DesiredCount value, that can run in a service during a
* deployment.
*
* @default 200
*/
maximumPercent?: number;
/**
* The maximum number of tasks, specified as a percentage of the Amazon ECS
* service's DesiredCount value, that can run in a service during a
* deployment.
*
* @default 200
*/
maximumPercent?: number;

/**
* The minimum number of tasks, specified as a percentage of
* the Amazon ECS service's DesiredCount value, that must
* continue to run and remain healthy during a deployment.
*
* @default 50
*/
minimumHealthyPercent?: number;
/**
* The minimum number of tasks, specified as a percentage of
* the Amazon ECS service's DesiredCount value, that must
* continue to run and remain healthy during a deployment.
*
* @default 50
*/
minimumHealthyPercent?: number;

/**
* The name or ARN of an AWS Identity and Access Management (IAM) role that
* allows your Amazon ECS container agent to make calls to your load
* balancer.
*/
role?: string;
/**
* The name or ARN of an AWS Identity and Access Management (IAM) role that
* allows your Amazon ECS container agent to make calls to your load
* balancer.
*/
role?: string;

///////// TBD ///////////////////////////////
// healthCheckGracePeriodSeconds?: number; // only needed with load balancers
// loadBalancers?: LoadBalancer[];
// placementConstraints?: PlacementConstraint[];
// placementStrategies?: PlacementStrategy[];
// networkConfiguration?: NetworkConfiguration;
// serviceRegistries?: ServiceRegistry[];
//
// platformVersion?: string; // FARGATE ONLY. default is LATEST. Other options: 1.2.0, 1.1.0, 1.0.0
////////////////////////////////////////////
///////// TBD ///////////////////////////////
// healthCheckGracePeriodSeconds?: number; // only needed with load balancers
// loadBalancers?: LoadBalancer[];
// placementConstraints?: PlacementConstraint[];
// placementStrategies?: PlacementStrategy[];
// networkConfiguration?: NetworkConfiguration;
// serviceRegistries?: ServiceRegistry[];
//
// platformVersion?: string; // FARGATE ONLY. default is LATEST. Other options: 1.2.0, 1.1.0, 1.0.0
////////////////////////////////////////////
}

export class Service extends cdk.Construct {
constructor(parent: cdk.Construct, name: string, props: ServiceProps) {
super(parent, name);
constructor(parent: cdk.Construct, name: string, props: ServiceProps) {
super(parent, name);

new cloudformation.ServiceResource(this, "Service", {
cluster: props.cluster,
taskDefinition: props.taskDefinition,
desiredCount: props.desiredCount,
serviceName: props.serviceName,
launchType: props.launchType,
deploymentConfiguration: {
maximumPercent: props.maximumPercent,
minimumHealthyPercent: props.minimumHealthyPercent
},
role: props.role,
});
}
new cloudformation.ServiceResource(this, "Service", {
cluster: props.cluster,
taskDefinition: props.taskDefinition,
desiredCount: props.desiredCount,
serviceName: props.serviceName,
launchType: props.launchType,
deploymentConfiguration: {
maximumPercent: props.maximumPercent,
minimumHealthyPercent: props.minimumHealthyPercent
},
role: props.role,
});
}
}
430 changes: 215 additions & 215 deletions packages/@aws-cdk/aws-ecs/lib/task-definition.ts
Original file line number Diff line number Diff line change
@@ -3,228 +3,228 @@ import { cloudformation } from './ecs.generated';

export interface TaskDefinitionProps {

containerDefinitions: ContainerDefinition[];

/**
* The number of cpu units used by the task. If using the EC2 launch type,
* this field is optional. Supported values are between 128 CPU units
* (0.125 vCPUs) and 10240 CPU units (10 vCPUs). If you are using the
* Fargate launch type, this field is required and you must use one of the
* following values, which determines your range of valid values for the
* memory parameter:
* 256 (.25 vCPU) - Available memory values: 0.5GB, 1GB, 2GB
* 512 (.5 vCPU) - Available memory values: 1GB, 2GB, 3GB, 4GB
* 1024 (1 vCPU) - Available memory values: 2GB, 3GB, 4GB, 5GB, 6GB, 7GB, 8GB
* 2048 (2 vCPU) - Available memory values: Between 4GB and 16GB in 1GB increments
* 4096 (4 vCPU) - Available memory values: Between 8GB and 30GB in 1GB increments
*
* @default 256
*/
cpu?: string;

/**
* The Amazon Resource Name (ARN) of the task execution role that
* containers in this task can assume. All containers in this task are
* granted the permissions that are specified in this role.
*
* Needed in Fargate to communicate with Cloudwatch Logs and ECR.
*/
executionRoleArn?: string;

/**
* Namespace for task definition versions
*
* @default CloudFormation-generated name
*/
family?: string;

/**
* The amount (in MiB) of memory used by the task. If using the EC2 launch
* type, this field is optional and any value can be used. If you are using
* the Fargate launch type, this field is required and you must use one of
* the following values, which determines your range of valid values for
* the cpu parameter:
*
* 0.5GB, 1GB, 2GB - Available cpu values: 256 (.25 vCPU)
*
* 1GB, 2GB, 3GB, 4GB - Available cpu values: 512 (.5 vCPU)
*
* 2GB, 3GB, 4GB, 5GB, 6GB, 7GB, 8GB - Available cpu values: 1024 (1 vCPU)
*
* Between 4GB and 16GB in 1GB increments - Available cpu values: 2048 (2 vCPU)
*
* Between 8GB and 30GB in 1GB increments - Available cpu values: 4096 (4 vCPU)
*
* @default 512
*/
memory?: string;

/**
* The Docker networking mode to use for the containers in the task, such as none, bridge, or host.
* For Fargate or to use task networking, "awsvpc" mode is required.
*
* @default bridge
*/
networkMode?: string;

/**
* An array of placement constraint objects to use for the task. You can
* specify a maximum of 10 constraints per task (this limit includes
* constraints in the task definition and those specified at run time).
*
* Not supported in Fargate.
*/
placementConstraints?: PlacementConstraint[];

/**
* Valid values include EC2 and FARGATE.
*
* @default EC2
*/
// requiresCompatibilities?: string[]; // FARGATE or EC2 -- set on ECS TD vs FG TD

/**
* The Amazon Resource Name (ARN) of an AWS Identity and Access Management
* (IAM) role that grants containers in the task permission to call AWS
* APIs on your behalf
*/
taskRoleArn?: string;

/**
* See: https://docs.aws.amazon.com/AmazonECS/latest/developerguide//task_definition_parameters.html#volumes
*/
volumes?: Volume[];
containerDefinitions: ContainerDefinition[];

/**
* The number of cpu units used by the task. If using the EC2 launch type,
* this field is optional. Supported values are between 128 CPU units
* (0.125 vCPUs) and 10240 CPU units (10 vCPUs). If you are using the
* Fargate launch type, this field is required and you must use one of the
* following values, which determines your range of valid values for the
* memory parameter:
* 256 (.25 vCPU) - Available memory values: 0.5GB, 1GB, 2GB
* 512 (.5 vCPU) - Available memory values: 1GB, 2GB, 3GB, 4GB
* 1024 (1 vCPU) - Available memory values: 2GB, 3GB, 4GB, 5GB, 6GB, 7GB, 8GB
* 2048 (2 vCPU) - Available memory values: Between 4GB and 16GB in 1GB increments
* 4096 (4 vCPU) - Available memory values: Between 8GB and 30GB in 1GB increments
*
* @default 256
*/
cpu?: string;

/**
* The Amazon Resource Name (ARN) of the task execution role that
* containers in this task can assume. All containers in this task are
* granted the permissions that are specified in this role.
*
* Needed in Fargate to communicate with Cloudwatch Logs and ECR.
*/
executionRoleArn?: string;

/**
* Namespace for task definition versions
*
* @default CloudFormation-generated name
*/
family?: string;

/**
* The amount (in MiB) of memory used by the task. If using the EC2 launch
* type, this field is optional and any value can be used. If you are using
* the Fargate launch type, this field is required and you must use one of
* the following values, which determines your range of valid values for
* the cpu parameter:
*
* 0.5GB, 1GB, 2GB - Available cpu values: 256 (.25 vCPU)
*
* 1GB, 2GB, 3GB, 4GB - Available cpu values: 512 (.5 vCPU)
*
* 2GB, 3GB, 4GB, 5GB, 6GB, 7GB, 8GB - Available cpu values: 1024 (1 vCPU)
*
* Between 4GB and 16GB in 1GB increments - Available cpu values: 2048 (2 vCPU)
*
* Between 8GB and 30GB in 1GB increments - Available cpu values: 4096 (4 vCPU)
*
* @default 512
*/
memory?: string;

/**
* The Docker networking mode to use for the containers in the task, such as none, bridge, or host.
* For Fargate or to use task networking, "awsvpc" mode is required.
*
* @default bridge
*/
networkMode?: string;

/**
* An array of placement constraint objects to use for the task. You can
* specify a maximum of 10 constraints per task (this limit includes
* constraints in the task definition and those specified at run time).
*
* Not supported in Fargate.
*/
placementConstraints?: PlacementConstraint[];

/**
* Valid values include EC2 and FARGATE.
*
* @default EC2
*/
// requiresCompatibilities?: string[]; // FARGATE or EC2 -- set on ECS TD vs FG TD

/**
* The Amazon Resource Name (ARN) of an AWS Identity and Access Management
* (IAM) role that grants containers in the task permission to call AWS
* APIs on your behalf
*/
taskRoleArn?: string;

/**
* See: https://docs.aws.amazon.com/AmazonECS/latest/developerguide//task_definition_parameters.html#volumes
*/
volumes?: Volume[];
}

export class TaskDefinitionArn extends cdk.Token {
}

export class TaskDefinition extends cdk.Construct {
public readonly taskDefinitionArn: TaskDefinitionArn;
private readonly containerDefinitions: cloudformation.TaskDefinitionResource.ContainerDefinitionProperty[] = [];
private readonly placementConstraints: cloudformation.TaskDefinitionResource.TaskDefinitionPlacementConstraintProperty[] = [];
private readonly volumes: cloudformation.TaskDefinitionResource.VolumeProperty[] = [];

constructor(parent: cdk.Construct, name: string, props: TaskDefinitionProps) {
super(parent, name);

props.containerDefinitions.forEach(cd => this.addContainer(cd));

if (props.placementConstraints) {
props.placementConstraints.forEach(pc => this.addPlacementConstraint(pc));
}

if (props.volumes) {
props.volumes.forEach(v => this.addVolume(v));
}

const taskDef = new cloudformation.TaskDefinitionResource(this, "TaskDef", {
containerDefinitions: new cdk.Token(() => this.containerDefinitions),
cpu: props.cpu,
executionRoleArn: props.executionRoleArn,
family: props.family,
memory: props.memory,
networkMode: props.networkMode,
placementConstraints: new cdk.Token(() => this.placementConstraints),
taskRoleArn: props.taskRoleArn
});

this.taskDefinitionArn = taskDef.ref;
}
public readonly taskDefinitionArn: TaskDefinitionArn;
private readonly containerDefinitions: cloudformation.TaskDefinitionResource.ContainerDefinitionProperty[] = [];
private readonly placementConstraints: cloudformation.TaskDefinitionResource.TaskDefinitionPlacementConstraintProperty[] = [];
private readonly volumes: cloudformation.TaskDefinitionResource.VolumeProperty[] = [];

private addContainer(definition: ContainerDefinition) {
const cd = this.renderContainerDefininition(definition);
this.containerDefinitions.push(cd);
}
constructor(parent: cdk.Construct, name: string, props: TaskDefinitionProps) {
super(parent, name);

private addPlacementConstraint(constraint: PlacementConstraint) {
const pc = this.renderPlacementConstraint(constraint);
this.placementConstraints.push(pc);
}

private addVolume(volume: Volume) {
// const v = this.renderVolume(volume);
this.volumes.push(volume);
}
props.containerDefinitions.forEach(cd => this.addContainer(cd));

// Populates task definition with container definition
private renderContainerDefininition(definition: ContainerDefinition): cloudformation.TaskDefinitionResource.ContainerDefinitionProperty {
// const logConfigs = this.renderLogConfiguration(definition.logConfiguration); // what to do if undefined?

return {
name: definition.name,
image: definition.image,
command: definition.command,
cpu: definition.cpu,
disableNetworking: definition.disableNetworking,
dnsSearchDomains: definition.dnsSearchDomains,
dnsServers: definition.dnsServers,
// dockerLabels: definition.dockerLabels,
dockerSecurityOptions: definition.dockerSecurityOptions,
entryPoint: definition.entryPoint,
essential: definition.essential,
hostname: definition.hostname,
links: definition.links,
// logConfiguration: logConfigs, // only set if passed in?
memory: definition.memory,
memoryReservation: definition.memoryReservation,
privileged: definition.privileged,
readonlyRootFilesystem: definition.readonlyRootFilesystem,
user: definition.user,
workingDirectory: definition.workingDirectory
};
if (props.placementConstraints) {
props.placementConstraints.forEach(pc => this.addPlacementConstraint(pc));
}

private renderPlacementConstraint(pc: PlacementConstraint): cloudformation.TaskDefinitionResource.TaskDefinitionPlacementConstraintProperty {
return {
type: pc.type,
expression: pc.expression
};
if (props.volumes) {
props.volumes.forEach(v => this.addVolume(v));
}

// private renderLogConfiguration(lc: LogConfiguration[]): cloudformation.TaskDefinitionResource.ContainerDefinitionProperty.LogConfiguration[] {
// return {
// logDriver: lc.logDriver,
// options: lc.options
// };
// }

// private renderVolume(volume: Volume): cloudformation.TaskDefinitionResource.VolumeProperty {
// return {
// host: this.renderHost(volume.host),
// name: volume.name
// };
// }

// private renderHost(host: Host): cloudformation.TaskDefinitionResource.VolumeProperty.Host {
// return {
// sourcePath: host.sourcePath
// }
// }
const taskDef = new cloudformation.TaskDefinitionResource(this, "TaskDef", {
containerDefinitions: new cdk.Token(() => this.containerDefinitions),
cpu: props.cpu,
executionRoleArn: props.executionRoleArn,
family: props.family,
memory: props.memory,
networkMode: props.networkMode,
placementConstraints: new cdk.Token(() => this.placementConstraints),
taskRoleArn: props.taskRoleArn
});

this.taskDefinitionArn = taskDef.ref;
}

private addContainer(definition: ContainerDefinition) {
const cd = this.renderContainerDefininition(definition);
this.containerDefinitions.push(cd);
}

private addPlacementConstraint(constraint: PlacementConstraint) {
const pc = this.renderPlacementConstraint(constraint);
this.placementConstraints.push(pc);
}

private addVolume(volume: Volume) {
// const v = this.renderVolume(volume);
this.volumes.push(volume);
}

// Populates task definition with container definition
private renderContainerDefininition(definition: ContainerDefinition): cloudformation.TaskDefinitionResource.ContainerDefinitionProperty {
// const logConfigs = this.renderLogConfiguration(definition.logConfiguration); // what to do if undefined?

return {
name: definition.name,
image: definition.image,
command: definition.command,
cpu: definition.cpu,
disableNetworking: definition.disableNetworking,
dnsSearchDomains: definition.dnsSearchDomains,
dnsServers: definition.dnsServers,
// dockerLabels: definition.dockerLabels,
dockerSecurityOptions: definition.dockerSecurityOptions,
entryPoint: definition.entryPoint,
essential: definition.essential,
hostname: definition.hostname,
links: definition.links,
// logConfiguration: logConfigs, // only set if passed in?
memory: definition.memory,
memoryReservation: definition.memoryReservation,
privileged: definition.privileged,
readonlyRootFilesystem: definition.readonlyRootFilesystem,
user: definition.user,
workingDirectory: definition.workingDirectory
};
}

private renderPlacementConstraint(pc: PlacementConstraint): cloudformation.TaskDefinitionResource.TaskDefinitionPlacementConstraintProperty {
return {
type: pc.type,
expression: pc.expression
};
}

// private renderLogConfiguration(lc: LogConfiguration[]): cloudformation.TaskDefinitionResource.ContainerDefinitionProperty.LogConfiguration[] {
// return {
// logDriver: lc.logDriver,
// options: lc.options
// };
// }

// private renderVolume(volume: Volume): cloudformation.TaskDefinitionResource.VolumeProperty {
// return {
// host: this.renderHost(volume.host),
// name: volume.name
// };
// }

// private renderHost(host: Host): cloudformation.TaskDefinitionResource.VolumeProperty.Host {
// return {
// sourcePath: host.sourcePath
// }
// }
}

export interface ContainerDefinition {
name: string;
image: string;

command?: string[];
cpu?: number;
disableNetworking?: boolean;
dnsSearchDomains?: string[];
dnsServers?: string[];
dockerLabels?: string[];
dockerSecurityOptions?: string[];
entryPoint?: string[];
essential?: boolean;
hostname?: string;
links?: string[];
logConfiguration?: LogConfiguration[];
memory?: number;
memoryReservation?: number;
privileged?: boolean;
readonlyRootFilesystem?: boolean;
user?: string;
workingDirectory?: string
name: string;
image: string;

command?: string[];
cpu?: number;
disableNetworking?: boolean;
dnsSearchDomains?: string[];
dnsServers?: string[];
dockerLabels?: string[];
dockerSecurityOptions?: string[];
entryPoint?: string[];
essential?: boolean;
hostname?: string;
links?: string[];
logConfiguration?: LogConfiguration[];
memory?: number;
memoryReservation?: number;
privileged?: boolean;
readonlyRootFilesystem?: boolean;
user?: string;
workingDirectory?: string
}
// environment?: list of key-value;
// extraHosts?: hostEntry[];
@@ -236,25 +236,25 @@ export interface ContainerDefinition {
// volumesFrom?: volumeFrom[];

export interface PlacementConstraint {
expression?: string;
type: string; // PlacementConstraintType;
expression?: string;
type: string; // PlacementConstraintType;
}

// enum PlacementConstraintType{
// DistinctInstance = "distinctInstance",
// MemberOf = "memberOf"
// DistinctInstance = "distinctInstance",
// MemberOf = "memberOf"
// }

export interface Volume {
host?: Host;
name?: string;
host?: Host;
name?: string;
}

export interface Host {
sourcePath?: string;
sourcePath?: string;
}

export interface LogConfiguration {
logDriver: string;
// options?:
logDriver: string;
// options?:
}