Skip to content

Commit

Permalink
fix(aws-events-targets): Allow creating EcsTask from an imported task…
Browse files Browse the repository at this point in the history
… definition (aws#12811)

Fixes aws#12811

This change allows creating EcsTask from an imported task definition. It
does so by changing the taskDefinition type in EcsTaskProps from concrete class
TaskDefinition to interface ITaskDefinition.
  • Loading branch information
rgithubli committed Feb 25, 2021
1 parent 430c38b commit 829de1e
Show file tree
Hide file tree
Showing 7 changed files with 466 additions and 29 deletions.
86 changes: 82 additions & 4 deletions packages/@aws-cdk/aws-ecs/lib/base/task-definition.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,16 @@ export interface ITaskDefinition extends IResource {
* Return true if the task definition can be run on a Fargate cluster
*/
readonly isFargateCompatible: boolean;

/**
* The networking mode to use for the containers in the task.
*/
readonly networkMode: NetworkMode;

/**
* The name of the IAM role that grants containers in the task permission to call AWS APIs on your behalf.
*/
readonly taskRole: iam.IRole;
}

/**
Expand Down Expand Up @@ -175,10 +185,55 @@ export interface TaskDefinitionProps extends CommonTaskDefinitionProps {
readonly pidMode?: PidMode;
}

/**
* The common task definition attributes used across all types of task definitions.
*/
export interface CommonTaskDefinitionAttributes {
/**
* The arn of the task definition
*/
readonly taskDefinitionArn: string;

/**
* The networking mode to use for the containers in the task.
*
* @default NetworkMode.BRIDGE
*/
readonly networkMode?: NetworkMode;

/**
* The name of the IAM role that grants containers in the task permission to call AWS APIs on your behalf.
*
* @default undefined.
*/
readonly taskRole?: iam.IRole;
}

/**
* A reference to an existing task definition
*/
export interface TaskDefinitionAttributes extends CommonTaskDefinitionAttributes {
/**
* Execution role for this task definition
*
* @default: undefined
*/
readonly executionRole?: iam.IRole;

/**
* What launch types this task definition should be compatible with.
*
* @default Compatibility.EC2_AND_FARGATE
*/
readonly compatibility?: Compatibility;
}

abstract class TaskDefinitionBase extends Resource implements ITaskDefinition {

public abstract readonly compatibility: Compatibility;
public abstract readonly networkMode: NetworkMode;
public abstract readonly taskDefinitionArn: string;
public abstract readonly taskRole: iam.IRole;
public abstract readonly executionRole?: iam.IRole;

/**
Expand Down Expand Up @@ -207,10 +262,33 @@ export class TaskDefinition extends TaskDefinitionBase {
* The task will have a compatibility of EC2+Fargate.
*/
public static fromTaskDefinitionArn(scope: Construct, id: string, taskDefinitionArn: string): ITaskDefinition {
return TaskDefinition.fromTaskDefinitionAttributes(scope, id, { taskDefinitionArn: taskDefinitionArn });
}

/**
* Create a task definition from a task definition reference
*/
public static fromTaskDefinitionAttributes(scope: Construct, id: string, attrs: TaskDefinitionAttributes): ITaskDefinition {
class Import extends TaskDefinitionBase {
public readonly taskDefinitionArn = taskDefinitionArn;
public readonly compatibility = Compatibility.EC2_AND_FARGATE;
public readonly executionRole?: iam.IRole = undefined;
public readonly taskDefinitionArn = attrs.taskDefinitionArn;
public readonly compatibility = attrs.compatibility ?? Compatibility.EC2_AND_FARGATE;
public readonly executionRole = attrs.executionRole;

public get networkMode(): NetworkMode {
if (attrs.networkMode == undefined) {
throw new Error('NetworkMode is available only if it is given when importing the TaskDefinition.');
} else {
return attrs.networkMode;
}
}

public get taskRole(): iam.IRole {
if (attrs.taskRole == undefined) {
throw new Error('TaskRole is available only if it is given when importing the TaskDefinition.');
} else {
return attrs.taskRole;
}
}
}

return new Import(scope, id);
Expand Down Expand Up @@ -248,7 +326,7 @@ export class TaskDefinition extends TaskDefinitionBase {
public defaultContainer?: ContainerDefinition;

/**
* The task launch type compatiblity requirement.
* The task launch type compatibility requirement.
*/
public readonly compatibility: Compatibility;

Expand Down
51 changes: 49 additions & 2 deletions packages/@aws-cdk/aws-ecs/lib/ec2/ec2-task-definition.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,16 @@
import * as iam from '@aws-cdk/aws-iam';
import { Resource } from '@aws-cdk/core';
import { Construct } from 'constructs';
import { CommonTaskDefinitionProps, Compatibility, IpcMode, ITaskDefinition, NetworkMode, PidMode, TaskDefinition } from '../base/task-definition';
import {
CommonTaskDefinitionAttributes,
CommonTaskDefinitionProps,
Compatibility,
IpcMode,
ITaskDefinition,
NetworkMode,
PidMode,
TaskDefinition,
} from '../base/task-definition';
import { PlacementConstraint } from '../placement';

/**
Expand Down Expand Up @@ -51,6 +61,13 @@ export interface IEc2TaskDefinition extends ITaskDefinition {

}

/**
* Attributes used to import an existing EC2 task definition
*/
export interface Ec2TaskDefinitionAttributes extends CommonTaskDefinitionAttributes {

}

/**
* The details of a task definition run on an EC2 cluster.
*
Expand All @@ -62,12 +79,42 @@ export class Ec2TaskDefinition extends TaskDefinition implements IEc2TaskDefinit
* Imports a task definition from the specified task definition ARN.
*/
public static fromEc2TaskDefinitionArn(scope: Construct, id: string, ec2TaskDefinitionArn: string): IEc2TaskDefinition {
return Ec2TaskDefinition.fromEc2TaskDefinitionAttributes(
scope, id, { taskDefinitionArn: ec2TaskDefinitionArn },
);
}

/**
* Imports an existing Ec2 task definition from its attributes
*/
public static fromEc2TaskDefinitionAttributes(
scope: Construct,
id: string,
attrs: Ec2TaskDefinitionAttributes,
): IEc2TaskDefinition {
class Import extends Resource implements IEc2TaskDefinition {
public readonly taskDefinitionArn = ec2TaskDefinitionArn;
public readonly taskDefinitionArn = attrs.taskDefinitionArn;
public readonly compatibility = Compatibility.EC2;
public readonly isEc2Compatible = true;
public readonly isFargateCompatible = false;

public get networkMode(): NetworkMode {
if (attrs.networkMode == undefined) {
throw new Error('NetworkMode is available only if it is given when importing the Ec2 TaskDefinition.');
} else {
return attrs.networkMode;
}
}

public get taskRole(): iam.IRole {
if (attrs.taskRole == undefined) {
throw new Error('TaskRole is available only if it is given when importing the Ec2 TaskDefinition.');
} else {
return attrs.taskRole;
}
}
}

return new Import(scope, id);
}

Expand Down
48 changes: 46 additions & 2 deletions packages/@aws-cdk/aws-ecs/lib/fargate/fargate-task-definition.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
import * as iam from '@aws-cdk/aws-iam';
import { Resource, Tokenization } from '@aws-cdk/core';
import { Construct } from 'constructs';
import { CommonTaskDefinitionProps, Compatibility, ITaskDefinition, NetworkMode, TaskDefinition } from '../base/task-definition';
import {
CommonTaskDefinitionAttributes,
CommonTaskDefinitionProps,
Compatibility,
ITaskDefinition,
NetworkMode,
TaskDefinition,
} from '../base/task-definition';

/**
* The properties for a task definition.
Expand Down Expand Up @@ -51,6 +59,13 @@ export interface IFargateTaskDefinition extends ITaskDefinition {

}

/**
* Attributes used to import an existing Fargate task definition
*/
export interface FargateTaskDefinitionAttributes extends CommonTaskDefinitionAttributes {

}

/**
* The details of a task definition run on a Fargate cluster.
*
Expand All @@ -62,11 +77,40 @@ export class FargateTaskDefinition extends TaskDefinition implements IFargateTas
* Imports a task definition from the specified task definition ARN.
*/
public static fromFargateTaskDefinitionArn(scope: Construct, id: string, fargateTaskDefinitionArn: string): IFargateTaskDefinition {
return FargateTaskDefinition.fromFargateTaskDefinitionAttributes(
scope, id, { taskDefinitionArn: fargateTaskDefinitionArn },
);
}

/**
* Import an existing Fargate task definition from its attributes
*/
public static fromFargateTaskDefinitionAttributes(
scope: Construct,
id: string,
attrs: FargateTaskDefinitionAttributes,
): IFargateTaskDefinition {
class Import extends Resource implements IFargateTaskDefinition {
public readonly taskDefinitionArn = fargateTaskDefinitionArn;
public readonly taskDefinitionArn = attrs.taskDefinitionArn;
public readonly compatibility = Compatibility.FARGATE;
public readonly isEc2Compatible = false;
public readonly isFargateCompatible = true;

public get networkMode(): NetworkMode {
if (attrs.networkMode == undefined) {
throw new Error('NetworkMode is available only if it is given when importing the Fargate TaskDefinition.');
} else {
return attrs.networkMode;
}
}

public get taskRole(): iam.IRole {
if (attrs.taskRole == undefined) {
throw new Error('TaskRole is available only if it is given when importing the Fargate TaskDefinition.');
} else {
return attrs.taskRole;
}
}
}

return new Import(scope, id);
Expand Down
75 changes: 75 additions & 0 deletions packages/@aws-cdk/aws-ecs/test/ec2/ec2-task-definition.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1059,6 +1059,81 @@ describe('ec2 task definition', () => {
});
});

describe('When importing from an existing Ec2 TaskDefinition', () => {
test('can succeed using TaskDefinition Arn', () => {
// GIVEN
const stack = new cdk.Stack();
const expectTaskDefinitionArn = 'TD_ARN';

// WHEN
const taskDefinition = ecs.Ec2TaskDefinition.fromEc2TaskDefinitionArn(stack, 'EC2_TD_ID', expectTaskDefinitionArn);

// THEN
expect(taskDefinition.taskDefinitionArn).toBe(expectTaskDefinitionArn);
});
});

describe('When importing from an existing Ec2 TaskDefinition using attributes', () => {
test('can set the imported task attribuets successfully', () => {
// GIVEN
const stack = new cdk.Stack();
const expectTaskDefinitionArn = 'TD_ARN';
const expectNetworkMode = ecs.NetworkMode.AWS_VPC;
const expectTaskRole = new iam.Role(stack, 'TaskRole', {
assumedBy: new iam.ServicePrincipal('ecs-tasks.amazonaws.com'),
});

// WHEN
const taskDefinition = ecs.Ec2TaskDefinition.fromEc2TaskDefinitionAttributes(stack, 'TD_ID', {
taskDefinitionArn: expectTaskDefinitionArn,
networkMode: expectNetworkMode,
taskRole: expectTaskRole,
});

// THEN
expect(taskDefinition.taskDefinitionArn).toBe(expectTaskDefinitionArn);
expect(taskDefinition.compatibility).toBe(ecs.Compatibility.EC2);
expect(taskDefinition.isEc2Compatible).toBeTruthy();
expect(taskDefinition.isFargateCompatible).toBeFalsy();
expect(taskDefinition.networkMode).toBe(expectNetworkMode);
expect(taskDefinition.taskRole).toBe(expectTaskRole);
});

test('returns an Ec2 TaskDefinition that will throw an error when trying to access its yet to defined networkMode', () => {
// GIVEN
const stack = new cdk.Stack();
const expectTaskDefinitionArn = 'TD_ARN';
const expectTaskRole = new iam.Role(stack, 'TaskRole', {
assumedBy: new iam.ServicePrincipal('ecs-tasks.amazonaws.com'),
});

// WHEN
const taskDefinition = ecs.Ec2TaskDefinition.fromEc2TaskDefinitionAttributes(stack, 'TD_ID', {
taskDefinitionArn: expectTaskDefinitionArn,
taskRole: expectTaskRole,
});

// THEN
expect(() => taskDefinition.networkMode).toThrow(/NetworkMode is available only if it is given when importing the Ec2 TaskDefinition./);
});

test('returns an Ec2 TaskDefinition that will throw an error when trying to access its yet to defined taskRole', () => {
// GIVEN
const stack = new cdk.Stack();
const expectTaskDefinitionArn = 'TD_ARN';
const expectNetworkMode = ecs.NetworkMode.AWS_VPC;

// WHEN
const taskDefinition = ecs.Ec2TaskDefinition.fromEc2TaskDefinitionAttributes(stack, 'TD_ID', {
taskDefinitionArn: expectTaskDefinitionArn,
networkMode: expectNetworkMode,
});

// THEN
expect(() => taskDefinition.taskRole).toThrow(/TaskRole is available only if it is given when importing the Ec2 TaskDefinition./);
});
});

test('throws when setting proxyConfiguration without networkMode AWS_VPC', () => {
// GIVEN
const stack = new cdk.Stack();
Expand Down
Loading

0 comments on commit 829de1e

Please sign in to comment.