Skip to content

Commit

Permalink
feat(ecs): allow ECS to be used declaratively
Browse files Browse the repository at this point in the history
Minor changes to the ECS APIs so that they can be instantiated via a declarative
template (see deCDK #1618).

Merge `IContainerImage` and the base `ContainerImage` into an abstract class
so it's now an "enum-like" class with static methods. It also improves discoverability
for all other users and more aligned with how other constructs expose union types
(e.g. `lambda.Code`).

Normalize the ctor of `ContainerDefinition` to "scope, id, props" so that it can
be instantiated as a deCDK resource.
  • Loading branch information
Elad Ben-Israel committed Feb 12, 2019
1 parent 5c11680 commit 2417b5b
Show file tree
Hide file tree
Showing 8 changed files with 55 additions and 44 deletions.
14 changes: 9 additions & 5 deletions packages/@aws-cdk/aws-ecs/lib/base/task-definition.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import iam = require('@aws-cdk/aws-iam');
import cdk = require('@aws-cdk/cdk');
import { ContainerDefinition, ContainerDefinitionProps } from '../container-definition';
import { ContainerDefinition, ContainerDefinitionOptions } from '../container-definition';
import { CfnTaskDefinition } from '../ecs.generated';
import { isEc2Compatible, isFargateCompatible } from '../util';

Expand Down Expand Up @@ -225,14 +225,18 @@ export class TaskDefinition extends cdk.Construct {
/**
* Create a new container to this task definition
*/
public addContainer(id: string, props: ContainerDefinitionProps) {
const container = new ContainerDefinition(this, id, this, props);
public addContainer(id: string, props: ContainerDefinitionOptions) {
return new ContainerDefinition(this, id, { task: this, ...props });
}

/**
* (internal) Links a container to this task definition.
*/
public _linkContainer(container: ContainerDefinition) {
this.containers.push(container);
if (this.defaultContainer === undefined && container.essential) {
this.defaultContainer = container;
}

return container;
}

/**
Expand Down
24 changes: 16 additions & 8 deletions packages/@aws-cdk/aws-ecs/lib/container-definition.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,20 @@
import iam = require('@aws-cdk/aws-iam');
import cdk = require('@aws-cdk/cdk');
import { NetworkMode, TaskDefinition } from './base/task-definition';
import { IContainerImage } from './container-image';
import { ContainerImage } from './container-image';
import { CfnTaskDefinition } from './ecs.generated';
import { LinuxParameters } from './linux-parameters';
import { LogDriver } from './log-drivers/log-driver';

/**
* Properties of a container definition
*/
export interface ContainerDefinitionProps {
export interface ContainerDefinitionOptions {
/**
* The image to use for a container.
*
* You can use images in the Docker Hub registry or specify other
* repositories (repository-url/image:tag).
* TODO: Update these to specify using classes of IContainerImage
*/
image: IContainerImage;
image: ContainerImage;

/**
* The CMD value to pass to the container.
Expand Down Expand Up @@ -173,6 +170,16 @@ export interface ContainerDefinitionProps {
logging?: LogDriver;
}

/**
* Properties of a container definition
*/
export interface ContainerDefinitionProps extends ContainerDefinitionOptions {
/**
* The task this container definition belongs to.
*/
task: TaskDefinition;
}

/**
* A definition for a single container in a Task
*/
Expand Down Expand Up @@ -222,14 +229,15 @@ export class ContainerDefinition extends cdk.Construct {
*/
private readonly links = new Array<string>();

constructor(scope: cdk.Construct, id: string, taskDefinition: TaskDefinition, private readonly props: ContainerDefinitionProps) {
constructor(scope: cdk.Construct, id: string, private readonly props: ContainerDefinitionProps) {
super(scope, id);
this.essential = props.essential !== undefined ? props.essential : true;
this.taskDefinition = taskDefinition;
this.taskDefinition = props.task;
this.memoryLimitSpecified = props.memoryLimitMiB !== undefined || props.memoryReservationMiB !== undefined;

props.image.bind(this);
if (props.logging) { props.logging.bind(this); }
props.task._linkContainer(this);
}

/**
Expand Down
34 changes: 15 additions & 19 deletions packages/@aws-cdk/aws-ecs/lib/container-image.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,29 +2,11 @@ import ecr = require('@aws-cdk/aws-ecr');
import cdk = require('@aws-cdk/cdk');

import { ContainerDefinition } from './container-definition';
import { AssetImage, AssetImageProps } from './images/asset-image';
import { DockerHubImage } from './images/dockerhub';
import { EcrImage } from './images/ecr';

/**
* A container image
*/
export interface IContainerImage {
/**
* Name of the image
*/
readonly imageName: string;

/**
* Called when the image is used by a ContainerDefinition
*/
bind(containerDefinition: ContainerDefinition): void;
}

/**
* Constructs for types of container images
*/
export class ContainerImage {
export abstract class ContainerImage {
/**
* Reference an image on DockerHub
*/
Expand All @@ -45,4 +27,18 @@ export class ContainerImage {
public static fromAsset(scope: cdk.Construct, id: string, props: AssetImageProps) {
return new AssetImage(scope, id, props);
}

/**
* Name of the image
*/
public abstract readonly imageName: string;

/**
* Called when the image is used by a ContainerDefinition
*/
public abstract bind(containerDefinition: ContainerDefinition): void;
}

import { AssetImage, AssetImageProps } from './images/asset-image';
import { DockerHubImage } from './images/dockerhub';
import { EcrImage } from './images/ecr';
12 changes: 7 additions & 5 deletions packages/@aws-cdk/aws-ecs/lib/images/asset-image.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { DockerImageAsset } from '@aws-cdk/assets-docker';
import cdk = require('@aws-cdk/cdk');
import { ContainerDefinition } from '../container-definition';
import { IContainerImage } from '../container-image';
import { ContainerImage } from '../container-image';

export interface AssetImageProps {
/**
Expand All @@ -13,16 +13,18 @@ export interface AssetImageProps {
/**
* An image that will be built at synthesis time
*/
export class AssetImage extends DockerImageAsset implements IContainerImage {
export class AssetImage extends ContainerImage {
private readonly asset: DockerImageAsset;
constructor(scope: cdk.Construct, id: string, props: AssetImageProps) {
super(scope, id, { directory: props.directory });
super();
this.asset = new DockerImageAsset(scope, id, { directory: props.directory });
}

public bind(containerDefinition: ContainerDefinition): void {
this.repository.grantPull(containerDefinition.taskDefinition.obtainExecutionRole());
this.asset.repository.grantPull(containerDefinition.taskDefinition.obtainExecutionRole());
}

public get imageName() {
return this.imageUri;
return this.asset.imageUri;
}
}
5 changes: 3 additions & 2 deletions packages/@aws-cdk/aws-ecs/lib/images/dockerhub.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { ContainerDefinition } from "../container-definition";
import { IContainerImage } from "../container-image";
import { ContainerImage } from "../container-image";

/**
* A DockerHub image
*/
export class DockerHubImage implements IContainerImage {
export class DockerHubImage extends ContainerImage {
constructor(public readonly imageName: string) {
super();
}

public bind(_containerDefinition: ContainerDefinition): void {
Expand Down
5 changes: 3 additions & 2 deletions packages/@aws-cdk/aws-ecs/lib/images/ecr.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
import ecr = require('@aws-cdk/aws-ecr');
import { ContainerDefinition } from '../container-definition';
import { IContainerImage } from '../container-image';
import { ContainerImage } from '../container-image';

/**
* An image from an ECR repository
*/
export class EcrImage implements IContainerImage {
export class EcrImage extends ContainerImage {
public readonly imageName: string;
private readonly repository: ecr.IRepository;

constructor(repository: ecr.IRepository, tag: string) {
super();
this.imageName = repository.repositoryUriForTag(tag);
this.repository = repository;
}
Expand Down
4 changes: 2 additions & 2 deletions packages/@aws-cdk/aws-ecs/lib/load-balanced-service-base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import elbv2 = require('@aws-cdk/aws-elasticloadbalancingv2');
import cdk = require('@aws-cdk/cdk');
import { BaseService } from './base/base-service';
import { ICluster } from './cluster';
import { IContainerImage } from './container-image';
import { ContainerImage } from './container-image';

export enum LoadBalancerType {
Application,
Expand All @@ -19,7 +19,7 @@ export interface LoadBalancedServiceBaseProps {
/**
* The image to start.
*/
image: IContainerImage;
image: ContainerImage;

/**
* The container port of the application load balancer attached to your Fargate service. Corresponds to container port mapping.
Expand Down
1 change: 0 additions & 1 deletion packages/@aws-cdk/aws-ecs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,6 @@
"exclude": [
"resource-attribute:@aws-cdk/aws-ecs.ICluster.clusterArn",
"construct-ctor:@aws-cdk/aws-ecs.BaseService.<initializer>",
"construct-ctor:@aws-cdk/aws-ecs.ContainerDefinition.<initializer>",
"construct-ctor:@aws-cdk/aws-ecs.LoadBalancedFargateServiceApplet.<initializer>.params[0]"
]
}
Expand Down

0 comments on commit 2417b5b

Please sign in to comment.