diff --git a/packages/@aws-cdk/aws-ecs-patterns/lib/base/application-load-balanced-service-base.ts b/packages/@aws-cdk/aws-ecs-patterns/lib/base/application-load-balanced-service-base.ts index 36afdf4d2cf4e..27723c3fd90de 100644 --- a/packages/@aws-cdk/aws-ecs-patterns/lib/base/application-load-balanced-service-base.ts +++ b/packages/@aws-cdk/aws-ecs-patterns/lib/base/application-load-balanced-service-base.ts @@ -28,25 +28,11 @@ export interface ApplicationLoadBalancedServiceBaseProps { readonly vpc?: IVpc; /** - * The image used to start a container. - */ - readonly image: ContainerImage; - - /** - * The port number on the container that is bound to the user-specified or automatically assigned host port. - * - * If you are using containers in a task with the awsvpc or host network mode, exposed ports should be specified using containerPort. - * If you are using containers in a task with the bridge network mode and you specify a container port and not a host port, - * your container automatically receives a host port in the ephemeral port range. + * The properties required to create a new task definition. TaskDefinition or TaskImageOptions must be specified, but not both. * - * Port mappings that are automatically assigned in this way do not count toward the 100 reserved ports limit of a container instance. - * - * For more information, see - * [hostPort](https://docs.aws.amazon.com/AmazonECS/latest/APIReference/API_PortMapping.html#ECS-Type-PortMapping-hostPort). - * - * @default 80 + * @default none */ - readonly containerPort?: number; + readonly taskImageOptions?: ApplicationLoadBalancedTaskImageOptions; /** * Determines whether the Load Balancer will be internet-facing. @@ -72,27 +58,6 @@ export interface ApplicationLoadBalancedServiceBaseProps { */ readonly certificate?: ICertificate; - /** - * The environment variables to pass to the container. - * - * @default - No environment variables. - */ - readonly environment?: { [key: string]: string }; - - /** - * The secret to expose to the container as an environment variable. - * - * @default - No secret environment variables. - */ - readonly secrets?: { [key: string]: Secret }; - - /** - * Flag to indicate whether to enable logging. - * - * @default true - */ - readonly enableLogging?: boolean; - /** * The protocol for connections from clients to the load balancer. * The load balancer port is determined from the protocol (port 80 for @@ -102,7 +67,7 @@ export interface ApplicationLoadBalancedServiceBaseProps { * @default HTTP. If a certificate is specified, the protocol will be * set by default to HTTPS. */ - readonly protocol?: ApplicationProtocol; + readonly protocol?: ApplicationProtocol; /** * The domain name for the service, e.g. "api.example.com." @@ -119,32 +84,74 @@ export interface ApplicationLoadBalancedServiceBaseProps { readonly domainZone?: IHostedZone; /** - * The name of the task execution IAM role that grants the Amazon ECS container agent permission to call AWS APIs on your behalf. + * The name of the service. * - * @default - No value + * @default - CloudFormation-generated name. */ - readonly executionRole?: IRole; + readonly serviceName?: string; /** - * The name of the task IAM role that grants containers in the task permission to call AWS APIs on your behalf. + * The period of time, in seconds, that the Amazon ECS service scheduler ignores unhealthy + * Elastic Load Balancing target health checks after a task has first started. * - * @default - A task role is automatically created for you. + * @default - defaults to 60 seconds if at least one load balancer is in-use and it is not already set */ - readonly taskRole?: IRole; + readonly healthCheckGracePeriod?: cdk.Duration; /** - * The container name value to be specified in the task definition. + * The application load balancer that will serve traffic to the service. + * + * [disable-awslint:ref-via-interface] + * + * @default - a new load balancer will be created. + */ + readonly loadBalancer?: ApplicationLoadBalancer; + + /** + * Specifies whether to propagate the tags from the task definition or the service to the tasks in the service. + * Tags can only be propagated to the tasks within the service during service creation. * * @default - none */ - readonly containerName?: string; + readonly propagateTags?: PropagatedTagSource; /** - * The name of the service. + * Specifies whether to enable Amazon ECS managed tags for the tasks within the service. For more information, see + * [Tagging Your Amazon ECS Resources](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/ecs-using-tags.html) * - * @default - CloudFormation-generated name. + * @default false */ - readonly serviceName?: string; + readonly enableECSManagedTags?: boolean; +} + +export interface ApplicationLoadBalancedTaskImageOptions { + /** + * The image used to start a container. Image or taskDefinition must be specified, not both. + * + * @default - none + */ + readonly image: ContainerImage; + + /** + * The environment variables to pass to the container. + * + * @default - No environment variables. + */ + readonly environment?: { [key: string]: string }; + + /** + * The secret to expose to the container as an environment variable. + * + * @default - No secret environment variables. + */ + readonly secrets?: { [key: string]: Secret }; + + /** + * Flag to indicate whether to enable logging. + * + * @default true + */ + readonly enableLogging?: boolean; /** * The log driver to use. @@ -154,28 +161,41 @@ export interface ApplicationLoadBalancedServiceBaseProps { readonly logDriver?: LogDriver; /** - * The period of time, in seconds, that the Amazon ECS service scheduler ignores unhealthy - * Elastic Load Balancing target health checks after a task has first started. + * The name of the task execution IAM role that grants the Amazon ECS container agent permission to call AWS APIs on your behalf. * - * @default - defaults to 60 seconds if at least one load balancer is in-use and it is not already set + * @default - No value */ - readonly healthCheckGracePeriod?: cdk.Duration; + readonly executionRole?: IRole; /** - * Specifies whether to propagate the tags from the task definition or the service to the tasks in the service. - * Tags can only be propagated to the tasks within the service during service creation. + * The name of the task IAM role that grants containers in the task permission to call AWS APIs on your behalf. + * + * @default - A task role is automatically created for you. + */ + readonly taskRole?: IRole; + + /** + * The container name value to be specified in the task definition. * * @default - none */ - readonly propagateTags?: PropagatedTagSource; + readonly containerName?: string; /** - * Specifies whether to enable Amazon ECS managed tags for the tasks within the service. For more information, see - * [Tagging Your Amazon ECS Resources](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/ecs-using-tags.html) + * The port number on the container that is bound to the user-specified or automatically assigned host port. * - * @default false + * If you are using containers in a task with the awsvpc or host network mode, exposed ports should be specified using containerPort. + * If you are using containers in a task with the bridge network mode and you specify a container port and not a host port, + * your container automatically receives a host port in the ephemeral port range. + * + * Port mappings that are automatically assigned in this way do not count toward the 100 reserved ports limit of a container instance. + * + * For more information, see + * [hostPort](https://docs.aws.amazon.com/AmazonECS/latest/APIReference/API_PortMapping.html#ECS-Type-PortMapping-hostPort). + * + * @default 80 */ - readonly enableECSManagedTags?: boolean; + readonly containerPort?: number; } /** @@ -213,26 +233,17 @@ export abstract class ApplicationLoadBalancedServiceBase extends cdk.Construct { */ public readonly cluster: ICluster; - /** - * The log driver to use for logging. - */ - public readonly logDriver?: LogDriver; - /** * Constructs a new instance of the ApplicationLoadBalancedServiceBase class. */ - constructor(scope: cdk.Construct, id: string, props: ApplicationLoadBalancedServiceBaseProps) { + constructor(scope: cdk.Construct, id: string, props: ApplicationLoadBalancedServiceBaseProps = {}) { super(scope, id); if (props.cluster && props.vpc) { - throw new Error(`You can only specify either vpc or cluster. Alternatively, you can leave both blank`); + throw new Error('You can only specify either vpc or cluster. Alternatively, you can leave both blank'); } this.cluster = props.cluster || this.getDefaultCluster(this, props.vpc); - // Create log driver if logging is enabled - const enableLogging = props.enableLogging !== undefined ? props.enableLogging : true; - this.logDriver = props.logDriver !== undefined ? props.logDriver : enableLogging ? this.createAWSLogDriver(this.node.id) : undefined; - this.desiredCount = props.desiredCount || 1; const internetFacing = props.publicLoadBalancer !== undefined ? props.publicLoadBalancer : true; @@ -242,7 +253,7 @@ export abstract class ApplicationLoadBalancedServiceBase extends cdk.Construct { internetFacing }; - this.loadBalancer = new ApplicationLoadBalancer(this, 'LB', lbProps); + this.loadBalancer = props.loadBalancer !== undefined ? props.loadBalancer : new ApplicationLoadBalancer(this, 'LB', lbProps); const targetProps = { port: 80 @@ -313,7 +324,7 @@ export abstract class ApplicationLoadBalancedServiceBase extends cdk.Construct { this.targetGroup.addTarget(service); } - private createAWSLogDriver(prefix: string): AwsLogDriver { + protected createAWSLogDriver(prefix: string): AwsLogDriver { return new AwsLogDriver({ streamPrefix: prefix }); } } diff --git a/packages/@aws-cdk/aws-ecs-patterns/lib/base/network-load-balanced-service-base.ts b/packages/@aws-cdk/aws-ecs-patterns/lib/base/network-load-balanced-service-base.ts index d044e767b5764..d5d143a947979 100644 --- a/packages/@aws-cdk/aws-ecs-patterns/lib/base/network-load-balanced-service-base.ts +++ b/packages/@aws-cdk/aws-ecs-patterns/lib/base/network-load-balanced-service-base.ts @@ -27,25 +27,11 @@ export interface NetworkLoadBalancedServiceBaseProps { readonly vpc?: IVpc; /** - * The image used to start a container. - */ - readonly image: ContainerImage; - - /** - * The port number on the container that is bound to the user-specified or automatically assigned host port. - * - * If you are using containers in a task with the awsvpc or host network mode, exposed ports should be specified using containerPort. - * If you are using containers in a task with the bridge network mode and you specify a container port and not a host port, - * your container automatically receives a host port in the ephemeral port range. - * - * Port mappings that are automatically assigned in this way do not count toward the 100 reserved ports limit of a container instance. - * - * For more information, see - * [hostPort](https://docs.aws.amazon.com/AmazonECS/latest/APIReference/API_PortMapping.html#ECS-Type-PortMapping-hostPort). + * The properties required to create a new task definition. One of taskImageOptions or taskDefinition must be specified. * - * @default 80 + * @default - none */ - readonly containerPort?: number; + readonly taskImageOptions?: NetworkLoadBalancedTaskImageOptions; /** * Determines whether the Load Balancer will be internet-facing. @@ -62,67 +48,88 @@ export interface NetworkLoadBalancedServiceBaseProps { readonly desiredCount?: number; /** - * The environment variables to pass to the container. + * The domain name for the service, e.g. "api.example.com." * - * @default - No environment variables. + * @default - No domain name. */ - readonly environment?: { [key: string]: string }; + readonly domainName?: string; /** - * The secret to expose to the container as an environment variable. + * The Route53 hosted zone for the domain, e.g. "example.com." * - * @default - No secret environment variables. + * @default - No Route53 hosted domain zone. */ - readonly secrets?: { [key: string]: Secret }; + readonly domainZone?: IHostedZone; /** - * Flag to indicate whether to enable logging. + * The name of the service. * - * @default true + * @default - CloudFormation-generated name. */ - readonly enableLogging?: boolean; + readonly serviceName?: string; /** - * The domain name for the service, e.g. "api.example.com." + * The period of time, in seconds, that the Amazon ECS service scheduler ignores unhealthy + * Elastic Load Balancing target health checks after a task has first started. * - * @default - No domain name. + * @default - defaults to 60 seconds if at least one load balancer is in-use and it is not already set */ - readonly domainName?: string; + readonly healthCheckGracePeriod?: cdk.Duration; /** - * The Route53 hosted zone for the domain, e.g. "example.com." + * The network load balancer that will serve traffic to the service. * - * @default - No Route53 hosted domain zone. + * [disable-awslint:ref-via-interface] + * + * @default - a new load balancer will be created. */ - readonly domainZone?: IHostedZone; + readonly loadBalancer?: NetworkLoadBalancer; /** - * The name of the task execution IAM role that grants the Amazon ECS container agent permission to call AWS APIs on your behalf. + * Specifies whether to propagate the tags from the task definition or the service to the tasks in the service. + * Tags can only be propagated to the tasks within the service during service creation. * - * @default - No value + * @default - none */ - readonly executionRole?: IRole; + readonly propagateTags?: PropagatedTagSource; /** - * The name of the IAM role that grants containers in the task permission to call AWS APIs on your behalf. + * Specifies whether to enable Amazon ECS managed tags for the tasks within the service. For more information, see + * [Tagging Your Amazon ECS Resources](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/ecs-using-tags.html) * - * @default - A task role is automatically created for you. + * @default false */ - readonly taskRole?: IRole; + readonly enableECSManagedTags?: boolean; +} +export interface NetworkLoadBalancedTaskImageOptions { /** - * The container name value to be specified in the task definition. + * The image used to start a container. Image or taskDefinition must be specified, but not both. * * @default - none */ - readonly containerName?: string; + readonly image: ContainerImage; /** - * The name of the service. + * The environment variables to pass to the container. * - * @default - CloudFormation-generated name. + * @default - No environment variables. */ - readonly serviceName?: string; + readonly environment?: { [key: string]: string }; + + /** + * The secret to expose to the container as an environment variable. + * + * @default - No secret environment variables. + */ + readonly secrets?: { [key: string]: Secret }; + + /** + * Flag to indicate whether to enable logging. + * + * @default true + */ + readonly enableLogging?: boolean; /** * The log driver to use. @@ -132,28 +139,41 @@ export interface NetworkLoadBalancedServiceBaseProps { readonly logDriver?: LogDriver; /** - * The period of time, in seconds, that the Amazon ECS service scheduler ignores unhealthy - * Elastic Load Balancing target health checks after a task has first started. + * The name of the task execution IAM role that grants the Amazon ECS container agent permission to call AWS APIs on your behalf. * - * @default - defaults to 60 seconds if at least one load balancer is in-use and it is not already set + * @default - No value */ - readonly healthCheckGracePeriod?: cdk.Duration; + readonly executionRole?: IRole; /** - * Specifies whether to propagate the tags from the task definition or the service to the tasks in the service. - * Tags can only be propagated to the tasks within the service during service creation. + * The name of the task IAM role that grants containers in the task permission to call AWS APIs on your behalf. + * + * @default - A task role is automatically created for you. + */ + readonly taskRole?: IRole; + + /** + * The container name value to be specified in the task definition. * * @default - none */ - readonly propagateTags?: PropagatedTagSource; + readonly containerName?: string; /** - * Specifies whether to enable Amazon ECS managed tags for the tasks within the service. For more information, see - * [Tagging Your Amazon ECS Resources](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/ecs-using-tags.html) + * The port number on the container that is bound to the user-specified or automatically assigned host port. * - * @default false + * If you are using containers in a task with the awsvpc or host network mode, exposed ports should be specified using containerPort. + * If you are using containers in a task with the bridge network mode and you specify a container port and not a host port, + * your container automatically receives a host port in the ephemeral port range. + * + * Port mappings that are automatically assigned in this way do not count toward the 100 reserved ports limit of a container instance. + * + * For more information, see + * [hostPort](https://docs.aws.amazon.com/AmazonECS/latest/APIReference/API_PortMapping.html#ECS-Type-PortMapping-hostPort). + * + * @default 80 */ - readonly enableECSManagedTags?: boolean; + readonly containerPort?: number; } /** @@ -185,26 +205,17 @@ export abstract class NetworkLoadBalancedServiceBase extends cdk.Construct { */ public readonly cluster: ICluster; - /** - * The log driver to use for logging. - */ - public readonly logDriver?: LogDriver; - /** * Constructs a new instance of the NetworkLoadBalancedServiceBase class. */ - constructor(scope: cdk.Construct, id: string, props: NetworkLoadBalancedServiceBaseProps) { + constructor(scope: cdk.Construct, id: string, props: NetworkLoadBalancedServiceBaseProps = {}) { super(scope, id); if (props.cluster && props.vpc) { - throw new Error(`You can only specify either vpc or cluster. Alternatively, you can leave both blank`); + throw new Error('You can only specify either vpc or cluster. Alternatively, you can leave both blank'); } this.cluster = props.cluster || this.getDefaultCluster(this, props.vpc); - // Create log driver if logging is enabled - const enableLogging = props.enableLogging !== undefined ? props.enableLogging : true; - this.logDriver = props.logDriver !== undefined ? props.logDriver : enableLogging ? this.createAWSLogDriver(this.node.id) : undefined; - this.desiredCount = props.desiredCount || 1; const internetFacing = props.publicLoadBalancer !== undefined ? props.publicLoadBalancer : true; @@ -214,7 +225,7 @@ export abstract class NetworkLoadBalancedServiceBase extends cdk.Construct { internetFacing }; - this.loadBalancer = new NetworkLoadBalancer(this, 'LB', lbProps); + this.loadBalancer = props.loadBalancer !== undefined ? props.loadBalancer : new NetworkLoadBalancer(this, 'LB', lbProps); const targetProps = { port: 80 @@ -255,7 +266,7 @@ export abstract class NetworkLoadBalancedServiceBase extends cdk.Construct { this.targetGroup.addTarget(service); } - private createAWSLogDriver(prefix: string): AwsLogDriver { + protected createAWSLogDriver(prefix: string): AwsLogDriver { return new AwsLogDriver({ streamPrefix: prefix }); } } diff --git a/packages/@aws-cdk/aws-ecs-patterns/lib/base/scheduled-task-base.ts b/packages/@aws-cdk/aws-ecs-patterns/lib/base/scheduled-task-base.ts index 11bb22ab2b805..b983dfce51058 100644 --- a/packages/@aws-cdk/aws-ecs-patterns/lib/base/scheduled-task-base.ts +++ b/packages/@aws-cdk/aws-ecs-patterns/lib/base/scheduled-task-base.ts @@ -25,11 +25,6 @@ export interface ScheduledTaskBaseProps { */ readonly vpc?: IVpc; - /** - * The image used to start a container. - */ - readonly image: ContainerImage; - /** * The schedule or rate (frequency) that determines when CloudWatch Events * runs the rule. For more information, see @@ -39,20 +34,29 @@ export interface ScheduledTaskBaseProps { readonly schedule: Schedule; /** - * The command that is passed to the container. + * The desired number of instantiations of the task definition to keep running on the service. * - * If you provide a shell command as a single string, you have to quote command-line arguments. + * @default 1 + */ + readonly desiredTaskCount?: number; +} + +export interface ScheduledTaskImageProps { + /** + * The image used to start a container. Image or taskDefinition must be specified, but not both. * - * @default - CMD value built into container image. + * @default - none */ - readonly command?: string[]; + readonly image: ContainerImage; /** - * The desired number of instantiations of the task definition to keep running on the service. + * The command that is passed to the container. * - * @default 1 + * If you provide a shell command as a single string, you have to quote command-line arguments. + * + * @default - CMD value built into container image. */ - readonly desiredTaskCount?: number; + readonly command?: string[]; /** * The environment variables to pass to the container. @@ -94,11 +98,6 @@ export abstract class ScheduledTaskBase extends Construct { */ public readonly eventRule: Rule; - /** - * The AwsLogDriver to use for logging if logging is enabled. - */ - public readonly logDriver?: LogDriver; - /** * Constructs a new instance of the ScheduledTaskBase class. */ @@ -112,10 +111,6 @@ export abstract class ScheduledTaskBase extends Construct { this.eventRule = new Rule(this, 'ScheduledEventRule', { schedule: props.schedule, }); - - this.logDriver = props.logDriver !== undefined - ? props.logDriver - : this.createAWSLogDriver(this.node.id); } /** diff --git a/packages/@aws-cdk/aws-ecs-patterns/lib/ecs/application-load-balanced-ecs-service.ts b/packages/@aws-cdk/aws-ecs-patterns/lib/ecs/application-load-balanced-ecs-service.ts index 8a8edfec9ad51..5d8aa60fb3778 100644 --- a/packages/@aws-cdk/aws-ecs-patterns/lib/ecs/application-load-balanced-ecs-service.ts +++ b/packages/@aws-cdk/aws-ecs-patterns/lib/ecs/application-load-balanced-ecs-service.ts @@ -7,6 +7,15 @@ import { ApplicationLoadBalancedServiceBase, ApplicationLoadBalancedServiceBaseP */ export interface ApplicationLoadBalancedEc2ServiceProps extends ApplicationLoadBalancedServiceBaseProps { + /** + * The task definition to use for tasks in the service. TaskDefinition or TaskImageOptions must be specified, but not both.. + * + * [disable-awslint:ref-via-interface] + * + * @default - none + */ + readonly taskDefinition?: Ec2TaskDefinition; + /** * The number of cpu units used by the task. * @@ -27,6 +36,7 @@ export interface ApplicationLoadBalancedEc2ServiceProps extends ApplicationLoadB * @default none */ readonly cpu?: number; + /** * The hard limit (in MiB) of memory to present to the container. * @@ -71,27 +81,42 @@ export class ApplicationLoadBalancedEc2Service extends ApplicationLoadBalancedSe /** * Constructs a new instance of the ApplicationLoadBalancedEc2Service class. */ - constructor(scope: Construct, id: string, props: ApplicationLoadBalancedEc2ServiceProps) { + constructor(scope: Construct, id: string, props: ApplicationLoadBalancedEc2ServiceProps = {}) { super(scope, id, props); - this.taskDefinition = new Ec2TaskDefinition(this, 'TaskDef', { - executionRole: props.executionRole, - taskRole: props.taskRole - }); + if (props.taskDefinition && props.taskImageOptions) { + throw new Error('You must specify either a taskDefinition or taskImageOptions, not both.'); + } else if (props.taskDefinition) { + this.taskDefinition = props.taskDefinition; + } else if (props.taskImageOptions) { + const taskImageOptions = props.taskImageOptions; + this.taskDefinition = new Ec2TaskDefinition(this, 'TaskDef', { + executionRole: taskImageOptions.executionRole, + taskRole: taskImageOptions.taskRole + }); - const containerName = props.containerName !== undefined ? props.containerName : 'web'; - const container = this.taskDefinition.addContainer(containerName, { - image: props.image, - cpu: props.cpu, - memoryLimitMiB: props.memoryLimitMiB, - memoryReservationMiB: props.memoryReservationMiB, - environment: props.environment, - secrets: props.secrets, - logging: this.logDriver, - }); - container.addPortMappings({ - containerPort: props.containerPort || 80 - }); + // Create log driver if logging is enabled + const enableLogging = taskImageOptions.enableLogging !== undefined ? taskImageOptions.enableLogging : true; + const logDriver = taskImageOptions.logDriver !== undefined + ? taskImageOptions.logDriver : enableLogging + ? this.createAWSLogDriver(this.node.id) : undefined; + + const containerName = taskImageOptions.containerName !== undefined ? taskImageOptions.containerName : 'web'; + const container = this.taskDefinition.addContainer(containerName, { + image: taskImageOptions.image, + cpu: props.cpu, + memoryLimitMiB: props.memoryLimitMiB, + memoryReservationMiB: props.memoryReservationMiB, + environment: taskImageOptions.environment, + secrets: taskImageOptions.secrets, + logging: logDriver, + }); + container.addPortMappings({ + containerPort: taskImageOptions.containerPort || 80 + }); + } else { + throw new Error('You must specify one of: taskDefinition or image'); + } this.service = new Ec2Service(this, "Service", { cluster: this.cluster, diff --git a/packages/@aws-cdk/aws-ecs-patterns/lib/ecs/network-load-balanced-ecs-service.ts b/packages/@aws-cdk/aws-ecs-patterns/lib/ecs/network-load-balanced-ecs-service.ts index 7055fed2a52b6..6d25f10c008fd 100644 --- a/packages/@aws-cdk/aws-ecs-patterns/lib/ecs/network-load-balanced-ecs-service.ts +++ b/packages/@aws-cdk/aws-ecs-patterns/lib/ecs/network-load-balanced-ecs-service.ts @@ -6,6 +6,14 @@ import { NetworkLoadBalancedServiceBase, NetworkLoadBalancedServiceBaseProps } f * The properties for the NetworkLoadBalancedEc2Service service. */ export interface NetworkLoadBalancedEc2ServiceProps extends NetworkLoadBalancedServiceBaseProps { + /** + * The task definition to use for tasks in the service. TaskDefinition or TaskImageOptions must be specified, but not both.. + * + * [disable-awslint:ref-via-interface] + * + * @default - none + */ + readonly taskDefinition?: Ec2TaskDefinition; /** * The number of cpu units used by the task. @@ -71,27 +79,42 @@ export class NetworkLoadBalancedEc2Service extends NetworkLoadBalancedServiceBas /** * Constructs a new instance of the NetworkLoadBalancedEc2Service class. */ - constructor(scope: Construct, id: string, props: NetworkLoadBalancedEc2ServiceProps) { + constructor(scope: Construct, id: string, props: NetworkLoadBalancedEc2ServiceProps = {}) { super(scope, id, props); - this.taskDefinition = new Ec2TaskDefinition(this, 'TaskDef', { - executionRole: props.executionRole, - taskRole: props.taskRole - }); + if (props.taskDefinition && props.taskImageOptions) { + throw new Error('You must specify either a taskDefinition or an image, not both.'); + } else if (props.taskDefinition) { + this.taskDefinition = props.taskDefinition; + } else if (props.taskImageOptions) { + const taskImageOptions = props.taskImageOptions; + this.taskDefinition = new Ec2TaskDefinition(this, 'TaskDef', { + executionRole: taskImageOptions.executionRole, + taskRole: taskImageOptions.taskRole + }); - const containerName = props.containerName !== undefined ? props.containerName : 'web'; - const container = this.taskDefinition.addContainer(containerName, { - image: props.image, - cpu: props.cpu, - memoryLimitMiB: props.memoryLimitMiB, - memoryReservationMiB: props.memoryReservationMiB, - environment: props.environment, - secrets: props.secrets, - logging: this.logDriver, - }); - container.addPortMappings({ - containerPort: props.containerPort || 80 - }); + // Create log driver if logging is enabled + const enableLogging = taskImageOptions.enableLogging !== undefined ? taskImageOptions.enableLogging : true; + const logDriver = taskImageOptions.logDriver !== undefined + ? taskImageOptions.logDriver : enableLogging + ? this.createAWSLogDriver(this.node.id) : undefined; + + const containerName = taskImageOptions.containerName !== undefined ? taskImageOptions.containerName : 'web'; + const container = this.taskDefinition.addContainer(containerName, { + image: taskImageOptions.image, + cpu: props.cpu, + memoryLimitMiB: props.memoryLimitMiB, + memoryReservationMiB: props.memoryReservationMiB, + environment: taskImageOptions.environment, + secrets: taskImageOptions.secrets, + logging: logDriver, + }); + container.addPortMappings({ + containerPort: taskImageOptions.containerPort || 80 + }); + } else { + throw new Error('You must specify one of: taskDefinition or image'); + } this.service = new Ec2Service(this, "Service", { cluster: this.cluster, diff --git a/packages/@aws-cdk/aws-ecs-patterns/lib/ecs/scheduled-ecs-task.ts b/packages/@aws-cdk/aws-ecs-patterns/lib/ecs/scheduled-ecs-task.ts index efd7dbe7b4391..a97e94ed0b038 100644 --- a/packages/@aws-cdk/aws-ecs-patterns/lib/ecs/scheduled-ecs-task.ts +++ b/packages/@aws-cdk/aws-ecs-patterns/lib/ecs/scheduled-ecs-task.ts @@ -1,11 +1,32 @@ import { Ec2TaskDefinition } from '@aws-cdk/aws-ecs'; import { Construct } from '@aws-cdk/core'; -import { ScheduledTaskBase, ScheduledTaskBaseProps } from '../base/scheduled-task-base'; +import { ScheduledTaskBase, ScheduledTaskBaseProps, ScheduledTaskImageProps } from '../base/scheduled-task-base'; /** * The properties for the ScheduledEc2Task task. */ export interface ScheduledEc2TaskProps extends ScheduledTaskBaseProps { + /** + * The properties to define if using an existing TaskDefinition in this construct. + * ScheduledEc2TaskDefinitionOptions or ScheduledEc2TaskImageOptions must be defined, but not both. + * + * @default none + */ + readonly scheduledEc2TaskDefinitionOptions?: ScheduledEc2TaskDefinitionOptions; + + /** + * The properties to define if the construct is to create a TaskDefinition. + * ScheduledEc2TaskDefinitionOptions or ScheduledEc2TaskImageOptions must be defined, but not both. + * + * @default none + */ + readonly scheduledEc2TaskImageOptions?: ScheduledEc2TaskImageOptions; +} + +/** + * The properties for the ScheduledEc2Task using an image. + */ +export interface ScheduledEc2TaskImageOptions extends ScheduledTaskImageProps { /** * The minimum number of CPU units to reserve for the container. * @@ -40,6 +61,20 @@ export interface ScheduledEc2TaskProps extends ScheduledTaskBaseProps { readonly memoryReservationMiB?: number; } +/** + * The properties for the ScheduledEc2Task using a task definition. + */ +export interface ScheduledEc2TaskDefinitionOptions extends ScheduledTaskBaseProps { + /** + * The task definition to use for tasks in the service. One of image or taskDefinition must be specified. + * + * [disable-awslint:ref-via-interface] + * + * @default - none + */ + readonly taskDefinition: Ec2TaskDefinition; +} + /** * A scheduled EC2 task that will be initiated off of CloudWatch Events. */ @@ -56,18 +91,27 @@ export class ScheduledEc2Task extends ScheduledTaskBase { constructor(scope: Construct, id: string, props: ScheduledEc2TaskProps) { super(scope, id, props); - // Create a Task Definition for the container to start, also creates a log driver - this.taskDefinition = new Ec2TaskDefinition(this, 'ScheduledTaskDef'); - this.taskDefinition.addContainer('ScheduledContainer', { - image: props.image, - memoryLimitMiB: props.memoryLimitMiB, - memoryReservationMiB: props.memoryReservationMiB, - cpu: props.cpu, - command: props.command, - environment: props.environment, - secrets: props.secrets, - logging: this.logDriver - }); + if (props.scheduledEc2TaskDefinitionOptions && props.scheduledEc2TaskImageOptions) { + throw new Error('You must specify either a scheduledEc2TaskDefinitionOptions or scheduledEc2TaskOptions, not both.'); + } else if (props.scheduledEc2TaskDefinitionOptions) { + this.taskDefinition = props.scheduledEc2TaskDefinitionOptions.taskDefinition; + } else if (props.scheduledEc2TaskImageOptions) { + const taskImageOptions = props.scheduledEc2TaskImageOptions; + // Create a Task Definition for the container to start, also creates a log driver + this.taskDefinition = new Ec2TaskDefinition(this, 'ScheduledTaskDef'); + this.taskDefinition.addContainer('ScheduledContainer', { + image: taskImageOptions.image, + memoryLimitMiB: taskImageOptions.memoryLimitMiB, + memoryReservationMiB: taskImageOptions.memoryReservationMiB, + cpu: taskImageOptions.cpu, + command: taskImageOptions.command, + environment: taskImageOptions.environment, + secrets: taskImageOptions.secrets, + logging: taskImageOptions.logDriver !== undefined ? taskImageOptions.logDriver : this.createAWSLogDriver(this.node.id) + }); + } else { + throw new Error('You must specify a taskDefinition or image'); + } this.addTaskDefinitionToEventTarget(this.taskDefinition); } diff --git a/packages/@aws-cdk/aws-ecs-patterns/lib/fargate/application-load-balanced-fargate-service.ts b/packages/@aws-cdk/aws-ecs-patterns/lib/fargate/application-load-balanced-fargate-service.ts index a756a3d5b8997..e3c86b0ece4c4 100644 --- a/packages/@aws-cdk/aws-ecs-patterns/lib/fargate/application-load-balanced-fargate-service.ts +++ b/packages/@aws-cdk/aws-ecs-patterns/lib/fargate/application-load-balanced-fargate-service.ts @@ -6,6 +6,15 @@ import { ApplicationLoadBalancedServiceBase, ApplicationLoadBalancedServiceBaseP * The properties for the ApplicationLoadBalancedFargateService service. */ export interface ApplicationLoadBalancedFargateServiceProps extends ApplicationLoadBalancedServiceBaseProps { + /** + * The task definition to use for tasks in the service. TaskDefinition or TaskImageOptions must be specified, but not both. + * + * [disable-awslint:ref-via-interface] + * + * @default - none + */ + readonly taskDefinition?: FargateTaskDefinition; + /** * The number of cpu units used by the task. * @@ -79,28 +88,43 @@ export class ApplicationLoadBalancedFargateService extends ApplicationLoadBalanc /** * Constructs a new instance of the ApplicationLoadBalancedFargateService class. */ - constructor(scope: Construct, id: string, props: ApplicationLoadBalancedFargateServiceProps) { + constructor(scope: Construct, id: string, props: ApplicationLoadBalancedFargateServiceProps = {}) { super(scope, id, props); this.assignPublicIp = props.assignPublicIp !== undefined ? props.assignPublicIp : false; - this.taskDefinition = new FargateTaskDefinition(this, 'TaskDef', { - memoryLimitMiB: props.memoryLimitMiB, - cpu: props.cpu, - executionRole: props.executionRole, - taskRole: props.taskRole - }); + if (props.taskDefinition && props.taskImageOptions) { + throw new Error('You must specify either a taskDefinition or an image, not both.'); + } else if (props.taskDefinition) { + this.taskDefinition = props.taskDefinition; + } else if (props.taskImageOptions) { + const taskImageOptions = props.taskImageOptions; + this.taskDefinition = new FargateTaskDefinition(this, 'TaskDef', { + memoryLimitMiB: props.memoryLimitMiB, + cpu: props.cpu, + executionRole: taskImageOptions.executionRole, + taskRole: taskImageOptions.taskRole + }); - const containerName = props.containerName !== undefined ? props.containerName : 'web'; - const container = this.taskDefinition.addContainer(containerName, { - image: props.image, - logging: this.logDriver, - environment: props.environment, - secrets: props.secrets, - }); - container.addPortMappings({ - containerPort: props.containerPort || 80, - }); + // Create log driver if logging is enabled + const enableLogging = taskImageOptions.enableLogging !== undefined ? taskImageOptions.enableLogging : true; + const logDriver = taskImageOptions.logDriver !== undefined + ? taskImageOptions.logDriver : enableLogging + ? this.createAWSLogDriver(this.node.id) : undefined; + + const containerName = taskImageOptions.containerName !== undefined ? taskImageOptions.containerName : 'web'; + const container = this.taskDefinition.addContainer(containerName, { + image: taskImageOptions.image, + logging: logDriver, + environment: taskImageOptions.environment, + secrets: taskImageOptions.secrets, + }); + container.addPortMappings({ + containerPort: taskImageOptions.containerPort || 80, + }); + } else { + throw new Error('You must specify one of: taskDefinition or image'); + } this.service = new FargateService(this, "Service", { cluster: this.cluster, diff --git a/packages/@aws-cdk/aws-ecs-patterns/lib/fargate/network-load-balanced-fargate-service.ts b/packages/@aws-cdk/aws-ecs-patterns/lib/fargate/network-load-balanced-fargate-service.ts index c8706b4c70c3f..550cff52ea32b 100644 --- a/packages/@aws-cdk/aws-ecs-patterns/lib/fargate/network-load-balanced-fargate-service.ts +++ b/packages/@aws-cdk/aws-ecs-patterns/lib/fargate/network-load-balanced-fargate-service.ts @@ -6,6 +6,15 @@ import { NetworkLoadBalancedServiceBase, NetworkLoadBalancedServiceBaseProps } f * The properties for the NetworkLoadBalancedFargateService service. */ export interface NetworkLoadBalancedFargateServiceProps extends NetworkLoadBalancedServiceBaseProps { + /** + * The task definition to use for tasks in the service. TaskDefinition or TaskImageOptions must be specified, but not both. + * + * [disable-awslint:ref-via-interface] + * + * @default - none + */ + readonly taskDefinition?: FargateTaskDefinition; + /** * The number of cpu units used by the task. * @@ -75,28 +84,43 @@ export class NetworkLoadBalancedFargateService extends NetworkLoadBalancedServic /** * Constructs a new instance of the NetworkLoadBalancedFargateService class. */ - constructor(scope: Construct, id: string, props: NetworkLoadBalancedFargateServiceProps) { + constructor(scope: Construct, id: string, props: NetworkLoadBalancedFargateServiceProps = {}) { super(scope, id, props); this.assignPublicIp = props.assignPublicIp !== undefined ? props.assignPublicIp : false; - this.taskDefinition = new FargateTaskDefinition(this, 'TaskDef', { - memoryLimitMiB: props.memoryLimitMiB, - cpu: props.cpu, - executionRole: props.executionRole, - taskRole: props.taskRole - }); + if (props.taskDefinition && props.taskImageOptions) { + throw new Error('You must specify either a taskDefinition or an image, not both.'); + } else if (props.taskDefinition) { + this.taskDefinition = props.taskDefinition; + } else if (props.taskImageOptions) { + const taskImageOptions = props.taskImageOptions; + this.taskDefinition = new FargateTaskDefinition(this, 'TaskDef', { + memoryLimitMiB: props.memoryLimitMiB, + cpu: props.cpu, + executionRole: taskImageOptions.executionRole, + taskRole: taskImageOptions.taskRole + }); - const containerName = props.containerName !== undefined ? props.containerName : 'web'; - const container = this.taskDefinition.addContainer(containerName, { - image: props.image, - logging: this.logDriver, - environment: props.environment, - secrets: props.secrets, - }); - container.addPortMappings({ - containerPort: props.containerPort || 80, - }); + // Create log driver if logging is enabled + const enableLogging = taskImageOptions.enableLogging !== undefined ? taskImageOptions.enableLogging : true; + const logDriver = taskImageOptions.logDriver !== undefined + ? taskImageOptions.logDriver : enableLogging + ? this.createAWSLogDriver(this.node.id) : undefined; + + const containerName = taskImageOptions.containerName !== undefined ? taskImageOptions.containerName : 'web'; + const container = this.taskDefinition.addContainer(containerName, { + image: taskImageOptions.image, + logging: logDriver, + environment: taskImageOptions.environment, + secrets: taskImageOptions.secrets, + }); + container.addPortMappings({ + containerPort: taskImageOptions.containerPort || 80, + }); + } else { + throw new Error('You must specify one of: taskDefinition or image'); + } this.service = new FargateService(this, "Service", { cluster: this.cluster, diff --git a/packages/@aws-cdk/aws-ecs-patterns/lib/fargate/scheduled-fargate-task.ts b/packages/@aws-cdk/aws-ecs-patterns/lib/fargate/scheduled-fargate-task.ts index 0f7e2a19b4ed0..b7f4f5d57cf0b 100644 --- a/packages/@aws-cdk/aws-ecs-patterns/lib/fargate/scheduled-fargate-task.ts +++ b/packages/@aws-cdk/aws-ecs-patterns/lib/fargate/scheduled-fargate-task.ts @@ -1,11 +1,32 @@ import { FargateTaskDefinition } from '@aws-cdk/aws-ecs'; import { Construct } from '@aws-cdk/core'; -import { ScheduledTaskBase, ScheduledTaskBaseProps } from '../base/scheduled-task-base'; +import { ScheduledTaskBase, ScheduledTaskBaseProps, ScheduledTaskImageProps } from '../base/scheduled-task-base'; /** - * The properties for the ScheduledFargateTask service. + * The properties for the ScheduledFargateTask task. */ export interface ScheduledFargateTaskProps extends ScheduledTaskBaseProps { + /** + * The properties to define if using an existing TaskDefinition in this construct. + * ScheduledFargateTaskDefinitionOptions or ScheduledFargateTaskImageOptions must be defined, but not both. + * + * @default none + */ + readonly scheduledFargateTaskDefinitionOptions?: ScheduledFargateTaskDefinitionOptions; + + /** + * The properties to define if the construct is to create a TaskDefinition. + * ScheduledFargateTaskDefinitionOptions or ScheduledFargateTaskImageOptions must be defined, but not both. + * + * @default none + */ + readonly scheduledFargateTaskImageOptions?: ScheduledFargateTaskImageOptions; +} + +/** + * The properties for the ScheduledFargateTask using an image. + */ +export interface ScheduledFargateTaskImageOptions extends ScheduledTaskImageProps { /** * The number of cpu units used by the task. * @@ -38,6 +59,20 @@ export interface ScheduledFargateTaskProps extends ScheduledTaskBaseProps { readonly memoryLimitMiB?: number; } +/** + * The properties for the ScheduledFargateTask using a task definition. + */ +export interface ScheduledFargateTaskDefinitionOptions extends ScheduledTaskBaseProps { + /** + * The task definition to use for tasks in the service. Image or taskDefinition must be specified, but not both. + * + * [disable-awslint:ref-via-interface] + * + * @default - none + */ + readonly taskDefinition: FargateTaskDefinition; +} + /** * A scheduled Fargate task that will be initiated off of CloudWatch Events. */ @@ -53,17 +88,26 @@ export class ScheduledFargateTask extends ScheduledTaskBase { constructor(scope: Construct, id: string, props: ScheduledFargateTaskProps) { super(scope, id, props); - this.taskDefinition = new FargateTaskDefinition(this, 'ScheduledTaskDef', { - memoryLimitMiB: props.memoryLimitMiB || 512, - cpu: props.cpu || 256, - }); - this.taskDefinition.addContainer('ScheduledContainer', { - image: props.image, - command: props.command, - environment: props.environment, - secrets: props.secrets, - logging: this.logDriver - }); + if (props.scheduledFargateTaskDefinitionOptions && props.scheduledFargateTaskImageOptions) { + throw new Error('You must specify either a scheduledFargateTaskDefinitionOptions or scheduledFargateTaskOptions, not both.'); + } else if (props.scheduledFargateTaskDefinitionOptions) { + this.taskDefinition = props.scheduledFargateTaskDefinitionOptions.taskDefinition; + } else if (props.scheduledFargateTaskImageOptions) { + const taskImageOptions = props.scheduledFargateTaskImageOptions; + this.taskDefinition = new FargateTaskDefinition(this, 'ScheduledTaskDef', { + memoryLimitMiB: taskImageOptions.memoryLimitMiB || 512, + cpu: taskImageOptions.cpu || 256, + }); + this.taskDefinition.addContainer('ScheduledContainer', { + image: taskImageOptions.image, + command: taskImageOptions.command, + environment: taskImageOptions.environment, + secrets: taskImageOptions.secrets, + logging: taskImageOptions.logDriver !== undefined ? taskImageOptions.logDriver : this.createAWSLogDriver(this.node.id) + }); + } else { + throw new Error('You must specify one of: taskDefinition or image'); + } this.addTaskDefinitionToEventTarget(this.taskDefinition); } diff --git a/packages/@aws-cdk/aws-ecs-patterns/test/ec2/integ.scheduled-ecs-task.lit.ts b/packages/@aws-cdk/aws-ecs-patterns/test/ec2/integ.scheduled-ecs-task.lit.ts index 30210026c1062..4893fa561e0c9 100644 --- a/packages/@aws-cdk/aws-ecs-patterns/test/ec2/integ.scheduled-ecs-task.lit.ts +++ b/packages/@aws-cdk/aws-ecs-patterns/test/ec2/integ.scheduled-ecs-task.lit.ts @@ -22,11 +22,13 @@ class EventStack extends cdk.Stack { // Create the scheduled task new ScheduledEc2Task(this, 'ScheduledEc2Task', { cluster, - image: ecs.ContainerImage.fromRegistry('amazon/amazon-ecs-sample'), + scheduledEc2TaskImageOptions: { + image: ecs.ContainerImage.fromRegistry('amazon/amazon-ecs-sample'), + memoryLimitMiB: 512, + cpu: 1, + environment: { TRIGGER: 'CloudWatch Events' }, + }, desiredTaskCount: 2, - memoryLimitMiB: 512, - cpu: 1, - environment: { TRIGGER: 'CloudWatch Events' }, schedule: events.Schedule.rate(cdk.Duration.minutes(1)), }); /// !hide diff --git a/packages/@aws-cdk/aws-ecs-patterns/test/ec2/test.l3s.ts b/packages/@aws-cdk/aws-ecs-patterns/test/ec2/test.l3s.ts index 8c8cec67f6526..d8533caeb12b9 100644 --- a/packages/@aws-cdk/aws-ecs-patterns/test/ec2/test.l3s.ts +++ b/packages/@aws-cdk/aws-ecs-patterns/test/ec2/test.l3s.ts @@ -21,12 +21,14 @@ export = { new ecsPatterns.ApplicationLoadBalancedEc2Service(stack, 'Service', { cluster, memoryLimitMiB: 1024, - image: ecs.ContainerImage.fromRegistry('test'), + taskImageOptions: { + image: ecs.ContainerImage.fromRegistry('test'), + environment: { + TEST_ENVIRONMENT_VARIABLE1: "test environment variable 1 value", + TEST_ENVIRONMENT_VARIABLE2: "test environment variable 2 value" + } + }, desiredCount: 2, - environment: { - TEST_ENVIRONMENT_VARIABLE1: "test environment variable 1 value", - TEST_ENVIRONMENT_VARIABLE2: "test environment variable 2 value" - } }); // THEN - stack contains a load balancer and a service @@ -67,12 +69,14 @@ export = { new ecsPatterns.ApplicationLoadBalancedEc2Service(stack, 'Service', { vpc, memoryLimitMiB: 1024, - image: ecs.ContainerImage.fromRegistry('test'), + taskImageOptions: { + image: ecs.ContainerImage.fromRegistry('test'), + environment: { + TEST_ENVIRONMENT_VARIABLE1: "test environment variable 1 value", + TEST_ENVIRONMENT_VARIABLE2: "test environment variable 2 value" + } + }, desiredCount: 2, - environment: { - TEST_ENVIRONMENT_VARIABLE1: "test environment variable 1 value", - TEST_ENVIRONMENT_VARIABLE2: "test environment variable 2 value" - } }); // THEN - stack does not contain a LaunchConfiguration @@ -93,7 +97,9 @@ export = { test.throws(() => new ecsPatterns.NetworkLoadBalancedEc2Service(stack, 'Service', { cluster, vpc, - image: ecs.ContainerImage.fromRegistry("/aws/aws-example-app") + taskImageOptions: { + image: ecs.ContainerImage.fromRegistry("/aws/aws-example-app") + } })); test.done(); @@ -110,7 +116,9 @@ export = { new ecsPatterns.ApplicationLoadBalancedEc2Service(stack, 'Service', { cluster, memoryReservationMiB: 1024, - image: ecs.ContainerImage.fromRegistry('test') + taskImageOptions: { + image: ecs.ContainerImage.fromRegistry('test') + } }); // THEN - stack contains a load balancer and a service @@ -136,12 +144,14 @@ export = { // WHEN new ecsPatterns.ApplicationLoadBalancedFargateService(stack, 'Service', { cluster, - image: ecs.ContainerImage.fromRegistry('test'), + taskImageOptions: { + image: ecs.ContainerImage.fromRegistry('test'), + environment: { + TEST_ENVIRONMENT_VARIABLE1: "test environment variable 1 value", + TEST_ENVIRONMENT_VARIABLE2: "test environment variable 2 value" + } + }, desiredCount: 2, - environment: { - TEST_ENVIRONMENT_VARIABLE1: "test environment variable 1 value", - TEST_ENVIRONMENT_VARIABLE2: "test environment variable 2 value" - } }); // THEN - stack contains a load balancer and a service @@ -193,13 +203,15 @@ export = { // WHEN new ecsPatterns.ApplicationLoadBalancedFargateService(stack, 'Service', { cluster, - image: ecs.ContainerImage.fromRegistry('test'), + taskImageOptions: { + image: ecs.ContainerImage.fromRegistry('test'), + enableLogging: false, + environment: { + TEST_ENVIRONMENT_VARIABLE1: "test environment variable 1 value", + TEST_ENVIRONMENT_VARIABLE2: "test environment variable 2 value" + }, + }, desiredCount: 2, - enableLogging: false, - environment: { - TEST_ENVIRONMENT_VARIABLE1: "test environment variable 1 value", - TEST_ENVIRONMENT_VARIABLE2: "test environment variable 2 value" - } }); // THEN - stack contains a load balancer and a service @@ -240,7 +252,9 @@ export = { // WHEN new ecsPatterns.ApplicationLoadBalancedFargateService(stack, 'Service', { cluster, - image: ecs.ContainerImage.fromRegistry('test'), + taskImageOptions: { + image: ecs.ContainerImage.fromRegistry('test'), + }, domainName: 'api.example.com', domainZone: zone, certificate: Certificate.fromCertificateArn(stack, 'Cert', 'helloworld') @@ -287,7 +301,9 @@ export = { // WHEN new ecsPatterns.ApplicationLoadBalancedFargateService(stack, 'Service', { cluster, - image: ecs.ContainerImage.fromRegistry('test'), + taskImageOptions: { + image: ecs.ContainerImage.fromRegistry('test'), + }, domainName: 'api.example.com', domainZone: zone, protocol: ApplicationProtocol.HTTPS @@ -350,7 +366,9 @@ export = { test.throws(() => { new ecsPatterns.ApplicationLoadBalancedFargateService(stack, 'Service', { cluster, - image: ecs.ContainerImage.fromRegistry('test'), + taskImageOptions: { + image: ecs.ContainerImage.fromRegistry('test'), + }, domainName: 'api.example.com' }); }); @@ -368,7 +386,9 @@ export = { test.throws(() => { new ecsPatterns.ApplicationLoadBalancedFargateService(stack, 'Service', { cluster, - image: ecs.ContainerImage.fromRegistry('test'), + taskImageOptions: { + image: ecs.ContainerImage.fromRegistry('test'), + }, protocol: ApplicationProtocol.HTTP, certificate: Certificate.fromCertificateArn(stack, 'Cert', 'helloworld') }); @@ -387,7 +407,9 @@ export = { test.throws(() => { new ecsPatterns.ApplicationLoadBalancedFargateService(stack, 'Service', { cluster, - image: ecs.ContainerImage.fromRegistry('test'), + taskImageOptions: { + image: ecs.ContainerImage.fromRegistry('test'), + }, protocol: ApplicationProtocol.HTTPS }); }); @@ -404,16 +426,18 @@ export = { // WHEN new ecsPatterns.ApplicationLoadBalancedFargateService(stack, 'Service', { cluster, - image: ecs.ContainerImage.fromRegistry('test'), - desiredCount: 2, - enableLogging: false, - environment: { - TEST_ENVIRONMENT_VARIABLE1: "test environment variable 1 value", - TEST_ENVIRONMENT_VARIABLE2: "test environment variable 2 value" + taskImageOptions: { + image: ecs.ContainerImage.fromRegistry('test'), + enableLogging: false, + environment: { + TEST_ENVIRONMENT_VARIABLE1: "test environment variable 1 value", + TEST_ENVIRONMENT_VARIABLE2: "test environment variable 2 value" + }, + logDriver: new AwsLogDriver({ + streamPrefix: "TestStream" + }), }, - logDriver: new AwsLogDriver({ - streamPrefix: "TestStream" - }) + desiredCount: 2, }); // THEN - stack contains a load balancer and a service @@ -453,13 +477,15 @@ export = { // WHEN new ecsPatterns.ApplicationLoadBalancedFargateService(stack, 'Service', { cluster, - image: ecs.ContainerImage.fromRegistry('test'), + taskImageOptions: { + image: ecs.ContainerImage.fromRegistry('test'), + enableLogging: true, + environment: { + TEST_ENVIRONMENT_VARIABLE1: "test environment variable 1 value", + TEST_ENVIRONMENT_VARIABLE2: "test environment variable 2 value" + } + }, desiredCount: 2, - enableLogging: true, - environment: { - TEST_ENVIRONMENT_VARIABLE1: "test environment variable 1 value", - TEST_ENVIRONMENT_VARIABLE2: "test environment variable 2 value" - } }); // THEN - stack contains a load balancer and a service @@ -488,6 +514,76 @@ export = { ] })); + test.done(); + }, + 'test Fargate loadbalanced construct with both image and taskDefinition provided'(test: Test) { + // GIVEN + const stack = new cdk.Stack(); + const vpc = new ec2.Vpc(stack, 'VPC'); + const cluster = new ecs.Cluster(stack, 'Cluster', { vpc }); + + const taskDefinition = new ecs.Ec2TaskDefinition(stack, 'Ec2TaskDef'); + taskDefinition.addContainer("web", { + image: ecs.ContainerImage.fromRegistry("amazon/amazon-ecs-sample"), + memoryLimitMiB: 512 + }); + + // WHEN + test.throws(() => new ecsPatterns.ApplicationLoadBalancedFargateService(stack, 'Service', { + cluster, + taskImageOptions: { + image: ecs.ContainerImage.fromRegistry('test'), + enableLogging: true, + environment: { + TEST_ENVIRONMENT_VARIABLE1: "test environment variable 1 value", + TEST_ENVIRONMENT_VARIABLE2: "test environment variable 2 value" + }, + }, + desiredCount: 2, + taskDefinition + })); + + test.done(); + }, + 'test Fargate application loadbalanced construct with taskDefinition provided'(test: Test) { + // GIVEN + const stack = new cdk.Stack(); + const vpc = new ec2.Vpc(stack, 'VPC'); + const cluster = new ecs.Cluster(stack, 'Cluster', { vpc }); + + const taskDefinition = new ecs.FargateTaskDefinition(stack, 'FargateTaskDef'); + const container = taskDefinition.addContainer("passedTaskDef", { + image: ecs.ContainerImage.fromRegistry("amazon/amazon-ecs-sample"), + memoryLimitMiB: 512 + }); + container.addPortMappings({ + containerPort: 80, + }); + + // WHEN + new ecsPatterns.ApplicationLoadBalancedFargateService(stack, 'Service', { + cluster, + taskDefinition, + desiredCount: 2, + memoryLimitMiB: 1024, + }); + + expect(stack).to(haveResourceLike('AWS::ECS::TaskDefinition', { + ContainerDefinitions: [ + { + Image: "amazon/amazon-ecs-sample", + Memory: 512, + Name: "passedTaskDef", + PortMappings: [ + { + ContainerPort: 80, + Protocol: "tcp" + } + ], + } + ] + })); + test.done(); }, }; diff --git a/packages/@aws-cdk/aws-ecs-patterns/test/ec2/test.scheduled-ecs-task.ts b/packages/@aws-cdk/aws-ecs-patterns/test/ec2/test.scheduled-ecs-task.ts index 9fee74eb5af8d..28e921fa51fa3 100644 --- a/packages/@aws-cdk/aws-ecs-patterns/test/ec2/test.scheduled-ecs-task.ts +++ b/packages/@aws-cdk/aws-ecs-patterns/test/ec2/test.scheduled-ecs-task.ts @@ -18,8 +18,10 @@ export = { new ScheduledEc2Task(stack, 'ScheduledEc2Task', { cluster, - image: ecs.ContainerImage.fromRegistry('henk'), - memoryLimitMiB: 512, + scheduledEc2TaskImageOptions: { + image: ecs.ContainerImage.fromRegistry('henk'), + memoryLimitMiB: 512, + }, schedule: events.Schedule.expression('rate(1 minute)') }); @@ -76,11 +78,13 @@ export = { new ScheduledEc2Task(stack, 'ScheduledEc2Task', { cluster, - image: ecs.ContainerImage.fromRegistry('henk'), + scheduledEc2TaskImageOptions: { + image: ecs.ContainerImage.fromRegistry('henk'), + memoryLimitMiB: 512, + cpu: 2, + environment: { TRIGGER: 'CloudWatch Events' }, + }, desiredTaskCount: 2, - memoryLimitMiB: 512, - cpu: 2, - environment: { TRIGGER: 'CloudWatch Events' }, schedule: events.Schedule.expression('rate(1 minute)') }); @@ -144,8 +148,10 @@ export = { new ScheduledEc2Task(stack, 'ScheduledEc2Task', { cluster, - image: ecs.ContainerImage.fromRegistry('henk'), - memoryReservationMiB: 512, + scheduledEc2TaskImageOptions: { + image: ecs.ContainerImage.fromRegistry('henk'), + memoryReservationMiB: 512, + }, schedule: events.Schedule.expression('rate(1 minute)') }); @@ -187,9 +193,11 @@ export = { new ScheduledEc2Task(stack, 'ScheduledEc2Task', { cluster, - image: ecs.ContainerImage.fromRegistry('henk'), - memoryReservationMiB: 512, - command: ["-c", "4", "amazon.com"], + scheduledEc2TaskImageOptions: { + image: ecs.ContainerImage.fromRegistry('henk'), + memoryReservationMiB: 512, + command: ["-c", "4", "amazon.com"], + }, schedule: events.Schedule.expression('rate(1 minute)') }); diff --git a/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.asset-image.ts b/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.asset-image.ts index f17494760dd38..2382ac3db0ecc 100644 --- a/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.asset-image.ts +++ b/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.asset-image.ts @@ -16,8 +16,10 @@ Array.isArray(path); // Instantiate Fargate Service with just cluster and image const fargateService = new ecsPatterns.ApplicationLoadBalancedFargateService(stack, "FargateService", { cluster, - containerPort: 8000, - image: new ecs.AssetImage(path.join(__dirname, '..', 'demo-image')), + taskImageOptions: { + containerPort: 8000, + image: new ecs.AssetImage(path.join(__dirname, '..', 'demo-image')), + }, }); // CfnOutput the DNS where you can access your service diff --git a/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.executionrole.ts b/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.executionrole.ts index 842b0a2eda3eb..ea2cba3633eca 100644 --- a/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.executionrole.ts +++ b/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.executionrole.ts @@ -15,13 +15,15 @@ new ecsPatterns.ApplicationLoadBalancedFargateService(stack, 'L3', { cluster, memoryLimitMiB: 1024, cpu: 512, - image: ecs.ContainerImage.fromRegistry("amazon/amazon-ecs-sample"), - executionRole: new iam.Role(stack, 'ExecutionRole', { - assumedBy: new iam.CompositePrincipal( - new iam.ServicePrincipal("ecs.amazonaws.com"), - new iam.ServicePrincipal("ecs-tasks.amazonaws.com") - ) - }) + taskImageOptions: { + image: ecs.ContainerImage.fromRegistry("amazon/amazon-ecs-sample"), + executionRole: new iam.Role(stack, 'ExecutionRole', { + assumedBy: new iam.CompositePrincipal( + new iam.ServicePrincipal("ecs.amazonaws.com"), + new iam.ServicePrincipal("ecs-tasks.amazonaws.com") + ) + }) + }, }); app.synth(); diff --git a/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.https-fargate-service.lit.ts b/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.https-fargate-service.lit.ts index 88fe9ac2ffc03..dd4a3c3b018a5 100644 --- a/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.https-fargate-service.lit.ts +++ b/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.https-fargate-service.lit.ts @@ -22,7 +22,9 @@ class EventStack extends cdk.Stack { /// !show new ApplicationLoadBalancedFargateService(this, 'HttpsService', { cluster, - image: ecs.ContainerImage.fromRegistry("amazon/amazon-ecs-sample"), + taskImageOptions: { + image: ecs.ContainerImage.fromRegistry("amazon/amazon-ecs-sample"), + }, protocol: elb.ApplicationProtocol.HTTPS, domainName: 'test.example.com', domainZone diff --git a/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.l3-autocreate.ts b/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.l3-autocreate.ts index 1d99268e21a6e..dab563a82a25a 100644 --- a/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.l3-autocreate.ts +++ b/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.l3-autocreate.ts @@ -8,13 +8,17 @@ const stack = new cdk.Stack(app, 'aws-ecs-integ'); new ecsPatterns.ApplicationLoadBalancedFargateService(stack, 'L3', { memoryLimitMiB: 1024, cpu: 512, - image: ecs.ContainerImage.fromRegistry("amazon/amazon-ecs-sample"), + taskImageOptions: { + image: ecs.ContainerImage.fromRegistry("amazon/amazon-ecs-sample"), + }, }); new ecsPatterns.ApplicationLoadBalancedFargateService(stack, 'L3b', { memoryLimitMiB: 1024, cpu: 512, - image: ecs.ContainerImage.fromRegistry("amazon/amazon-ecs-sample"), + taskImageOptions: { + image: ecs.ContainerImage.fromRegistry("amazon/amazon-ecs-sample"), + }, }); app.synth(); diff --git a/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.l3-vpconly.ts b/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.l3-vpconly.ts index 51dba7ed27832..45cb5f55b2ae9 100644 --- a/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.l3-vpconly.ts +++ b/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.l3-vpconly.ts @@ -11,7 +11,9 @@ new ecsPatterns.ApplicationLoadBalancedFargateService(stack, 'L3', { vpc, memoryLimitMiB: 1024, cpu: 512, - image: ecs.ContainerImage.fromRegistry("amazon/amazon-ecs-sample"), + taskImageOptions: { + image: ecs.ContainerImage.fromRegistry("amazon/amazon-ecs-sample"), + }, }); const vpc2 = new ec2.Vpc(stack, 'Vpc2', { maxAzs: 2 }); @@ -19,14 +21,18 @@ new ecsPatterns.ApplicationLoadBalancedFargateService(stack, 'L3b', { vpc: vpc2, memoryLimitMiB: 1024, cpu: 512, - image: ecs.ContainerImage.fromRegistry("amazon/amazon-ecs-sample"), + taskImageOptions: { + image: ecs.ContainerImage.fromRegistry("amazon/amazon-ecs-sample"), + }, }); new ecsPatterns.ApplicationLoadBalancedFargateService(stack, 'L3c', { vpc: vpc2, memoryLimitMiB: 1024, cpu: 512, - image: ecs.ContainerImage.fromRegistry("amazon/amazon-ecs-sample"), + taskImageOptions: { + image: ecs.ContainerImage.fromRegistry("amazon/amazon-ecs-sample"), + }, }); app.synth(); diff --git a/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.l3.ts b/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.l3.ts index 9ca2b7ce09593..97c639c94fdfd 100644 --- a/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.l3.ts +++ b/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.l3.ts @@ -14,7 +14,9 @@ new ecsPatterns.ApplicationLoadBalancedFargateService(stack, 'L3', { cluster, memoryLimitMiB: 1024, cpu: 512, - image: ecs.ContainerImage.fromRegistry("amazon/amazon-ecs-sample"), + taskImageOptions: { + image: ecs.ContainerImage.fromRegistry("amazon/amazon-ecs-sample"), + }, }); app.synth(); diff --git a/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.scheduled-fargate-task.lit.ts b/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.scheduled-fargate-task.lit.ts index de1b39b615d85..ee4cbd98c922c 100644 --- a/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.scheduled-fargate-task.lit.ts +++ b/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.scheduled-fargate-task.lit.ts @@ -18,11 +18,13 @@ class EventStack extends cdk.Stack { // Create the scheduled task new ScheduledFargateTask(this, 'ScheduledFargateTask', { cluster, - image: new ecs.AssetImage(path.join(__dirname, '..', 'demo-image')), + scheduledFargateTaskImageOptions: { + image: new ecs.AssetImage(path.join(__dirname, '..', 'demo-image')), + memoryLimitMiB: 512, + cpu: 256, + environment: { TRIGGER: 'CloudWatch Events' }, + }, desiredTaskCount: 2, - memoryLimitMiB: 512, - cpu: 256, - environment: { TRIGGER: 'CloudWatch Events' }, schedule: events.Schedule.rate(cdk.Duration.minutes(2)), }); } diff --git a/packages/@aws-cdk/aws-ecs-patterns/test/fargate/test.load-balanced-fargate-service.ts b/packages/@aws-cdk/aws-ecs-patterns/test/fargate/test.load-balanced-fargate-service.ts index 0cf6044ba7ade..0990f7728e250 100644 --- a/packages/@aws-cdk/aws-ecs-patterns/test/fargate/test.load-balanced-fargate-service.ts +++ b/packages/@aws-cdk/aws-ecs-patterns/test/fargate/test.load-balanced-fargate-service.ts @@ -16,7 +16,9 @@ export = { // WHEN new ecsPatterns.NetworkLoadBalancedFargateService(stack, 'Service', { cluster, - image: ecs.ContainerImage.fromRegistry("/aws/aws-example-app") + taskImageOptions: { + image: ecs.ContainerImage.fromRegistry("/aws/aws-example-app") + }, }); // THEN @@ -37,7 +39,9 @@ export = { test.throws(() => new ecsPatterns.NetworkLoadBalancedFargateService(stack, 'Service', { cluster, vpc, - image: ecs.ContainerImage.fromRegistry("/aws/aws-example-app") + taskImageOptions: { + image: ecs.ContainerImage.fromRegistry("/aws/aws-example-app") + }, })); test.done(); @@ -60,8 +64,10 @@ export = { // WHEN new ecsPatterns.NetworkLoadBalancedFargateService(stack, 'Service', { cluster, - image: ecs.ContainerImage.fromRegistry("/aws/aws-example-app"), - executionRole + taskImageOptions: { + image: ecs.ContainerImage.fromRegistry("/aws/aws-example-app"), + executionRole + }, }); // THEN @@ -86,8 +92,10 @@ export = { // WHEN new ecsPatterns.NetworkLoadBalancedFargateService(stack, 'Service', { cluster, - image: ecs.ContainerImage.fromRegistry("/aws/aws-example-app"), - taskRole + taskImageOptions: { + image: ecs.ContainerImage.fromRegistry("/aws/aws-example-app"), + taskRole + }, }); // THEN @@ -105,8 +113,10 @@ export = { // WHEN new ecsPatterns.NetworkLoadBalancedFargateService(stack, 'Service', { cluster, - image: ecs.ContainerImage.fromRegistry("/aws/aws-example-app"), - containerName: 'bob' + taskImageOptions: { + image: ecs.ContainerImage.fromRegistry("/aws/aws-example-app"), + containerName: 'bob' + }, }); // THEN @@ -124,7 +134,9 @@ export = { // WHEN new ecsPatterns.NetworkLoadBalancedFargateService(stack, 'Service', { cluster, - image: ecs.ContainerImage.fromRegistry("/aws/aws-example-app"), + taskImageOptions: { + image: ecs.ContainerImage.fromRegistry("/aws/aws-example-app"), + }, }); // THEN @@ -142,7 +154,9 @@ export = { // WHEN new ecsPatterns.NetworkLoadBalancedFargateService(stack, 'Service', { cluster, - image: ecs.ContainerImage.fromRegistry("/aws/aws-example-app"), + taskImageOptions: { + image: ecs.ContainerImage.fromRegistry("/aws/aws-example-app"), + }, serviceName: 'bob', }); // THEN @@ -160,7 +174,9 @@ export = { // WHEN new ecsPatterns.NetworkLoadBalancedFargateService(stack, 'Service', { cluster, - image: ecs.ContainerImage.fromRegistry("/aws/aws-example-app"), + taskImageOptions: { + image: ecs.ContainerImage.fromRegistry("/aws/aws-example-app"), + }, }); // THEN @@ -175,7 +191,9 @@ export = { // WHEN new ecsPatterns.ApplicationLoadBalancedFargateService(stack, 'Service', { - image: ecs.ContainerImage.fromRegistry("/aws/aws-example-app"), + taskImageOptions: { + image: ecs.ContainerImage.fromRegistry("/aws/aws-example-app"), + }, healthCheckGracePeriod: cdk.Duration.seconds(600), }); // THEN diff --git a/packages/@aws-cdk/aws-ecs-patterns/test/fargate/test.scheduled-fargate-task.ts b/packages/@aws-cdk/aws-ecs-patterns/test/fargate/test.scheduled-fargate-task.ts index 05703296b0a31..2c4427e6875e6 100644 --- a/packages/@aws-cdk/aws-ecs-patterns/test/fargate/test.scheduled-fargate-task.ts +++ b/packages/@aws-cdk/aws-ecs-patterns/test/fargate/test.scheduled-fargate-task.ts @@ -15,8 +15,10 @@ export = { new ScheduledFargateTask(stack, 'ScheduledFargateTask', { cluster, - image: ecs.ContainerImage.fromRegistry('henk'), - memoryLimitMiB: 512, + scheduledFargateTaskImageOptions: { + image: ecs.ContainerImage.fromRegistry('henk'), + memoryLimitMiB: 512, + }, schedule: events.Schedule.expression('rate(1 minute)') }); @@ -88,11 +90,13 @@ export = { new ScheduledFargateTask(stack, 'ScheduledFargateTask', { cluster, - image: ecs.ContainerImage.fromRegistry('henk'), + scheduledFargateTaskImageOptions: { + image: ecs.ContainerImage.fromRegistry('henk'), + memoryLimitMiB: 512, + cpu: 2, + environment: { TRIGGER: 'CloudWatch Events' }, + }, desiredTaskCount: 2, - memoryLimitMiB: 512, - cpu: 2, - environment: { TRIGGER: 'CloudWatch Events' }, schedule: events.Schedule.expression('rate(1 minute)') }); @@ -170,7 +174,9 @@ export = { new ScheduledFargateTask(stack, 'ScheduledFargateTask', { cluster, - image: ecs.ContainerImage.fromRegistry('henk'), + scheduledFargateTaskImageOptions: { + image: ecs.ContainerImage.fromRegistry('henk'), + }, schedule: events.Schedule.expression('rate(1 minute)') }); @@ -208,8 +214,10 @@ export = { new ScheduledFargateTask(stack, 'ScheduledFargateTask', { cluster, - image: ecs.ContainerImage.fromRegistry('henk'), - command: ["-c", "4", "amazon.com"], + scheduledFargateTaskImageOptions: { + image: ecs.ContainerImage.fromRegistry('henk'), + command: ["-c", "4", "amazon.com"], + }, schedule: events.Schedule.expression('rate(1 minute)') });