import { ICertificate } from '@aws-cdk/aws-certificatemanager' ; import { IVpc } from '@aws-cdk/aws-ec2' ; import { AwsLogDriver , BaseService , CloudMapOptions , ContainerDefinition , ContainerImage , ICluster , LogDriver , PropagatedTagSource , Protocol , Secret } from '@aws-cdk/aws-ecs' ; import { ApplicationProtocol , ApplicationTargetGroup , IApplicationListener , IApplicationLoadBalancer , Protocol as ELBProtocol , TargetType } from '@aws-cdk/aws-elasticloadbalancingv2' ; import { ARecord , IHostedZone , RecordTarget } from '@aws-cdk/aws-route53' ; import { Construct , Duration } from '@aws-cdk/core' ; import { LoadBalancerTarget } from "@aws-cdk/aws-route53-targets" ; /**
 * The properties for the base ApplicationMultipleTargetGroupsEc2Service or ApplicationMultipleTargetGroupsFargateService service.
 */ export interface ApplicationMultipleTargetGroupsServiceBaseProps { default readonly cluster : ICluster ; default readonly vpc ? : IVpc ; default readonly taskImageOptions ? : ApplicationLoadBalancedTaskImageProps [ ] ; default readonly desiredCount ? : number ; default readonly healthCheckGracePeriod ? : Duration ; default readonly serviceName ? : string ; default readonly loadBalancers : ApplicationLoadBalancerProps [ ] ; default readonly propagateTags ? : PropagatedTagSource ; default readonly enableECSManagedTags ? : boolean ; default readonly cloudMapOptions ? : CloudMapOptions ; default readonly targetGroups ? : ApplicationTargetProps [ ] ; } /**
 * Options for configuring a new container.
 */ export interface ApplicationLoadBalancedTaskImageProps { default readonly image : ContainerImage ; default readonly environment ? : { [ key ] : string ; } ; default readonly secrets ? : { [ key ] : Secret ; } ; default readonly enableLogging ? : boolean ; default readonly logDriver ? : LogDriver ; default readonly containerName ? : string ; default readonly containerPorts ? : number [ ] ; } /**
 * Properties to define an application target group.
 */ export interface ApplicationTargetProps { /**
     * The port number of the container. Only applicable when using application/network load balancers.
     */ readonly containerPort : number ; default readonly protocol ? : Protocol ; default readonly listener ? : string ; default readonly priority ? : number ; see https default readonly hostHeader ? : string ; see https default readonly pathPattern ? : string ; } /**
 * Properties to define an application load balancer.
 */ export interface ApplicationLoadBalancerProps { /**
     * Listeners (at least one listener) attached to this load balancer.
     */ readonly listeners : IApplicationListener [ ] ; readonly loadBalancer : IApplicationLoadBalancer ; /**
     * Name of the load balancer.
     */ readonly name ? : string ; default readonly publicLoadBalancer ? : boolean ; default readonly domainName ? : string ; default readonly domainZone ? : IHostedZone ; } /**
 * Properties to define an application listener.
 */ export interface ApplicationListenerProps { /**
     * Name of the listener.
     */ readonly name : string ; default readonly protocol ? : ApplicationProtocol ; default readonly port ? : number ; default readonly certificate ? : ICertificate ; } /**
 * The base class for ApplicationMultipleTargetGroupsEc2Service and ApplicationMultipleTargetGroupsFargateService classes.
 */ export abstract class ApplicationMultipleTargetGroupsServiceBase extends Construct { /**
     * The desired number of instantiations of the task definition to keep running on the service.
     */ readonly desiredCount : number ; /**
     * The default Application Load Balancer for the service (first added load balancer).
     */ readonly loadBalancer : IApplicationLoadBalancer ; /**
     * The default listener for the service (first added listener).
     */ readonly listener : IApplicationListener ; /**
     * The cluster that hosts the service.
     */ readonly cluster : ICluster ; protected logDriver ? : LogDriver ; protected listeners : IApplicationListener [ ] ; protected targetGroups : ApplicationTargetGroup [ ] ; private readonly loadBalancers : IApplicationLoadBalancer [ ] ; /**
     * Constructs a new instance of the ApplicationMultipleTargetGroupsServiceBase class.
     */ protected constructor ( scope , id , props ) { super ( scope , id ) ; this . listeners = [ ] ; this . targetGroups = [ ] ; this . loadBalancers = [ ] ; this . validateInput ( props ) ; this . cluster = props . cluster ; this . desiredCount = props . desiredCount || 1 ; if ( props . taskImageOptions ) { for ( const taskImageOptionsProps of props . taskImageOptions ) { this . logDriver = this . createLogDriver ( taskImageOptionsProps . enableLogging , taskImageOptionsProps . logDriver ) ; } } for ( const lbProps of props . loadBalancers ) { const internetFacing = lbProps . publicLoadBalancer !== undefined ? lbProps . publicLoadBalancer : true ; const { loadBalancer : lb } = lbProps ; this . loadBalancers . push ( lb ) ; for ( const listener of lbProps . listeners ) { this . listeners . push ( listener ) ; } this . createDomainName ( lb , internetFacing , lbProps . domainName , lbProps . domainZone ) ; } this . loadBalancer = this . loadBalancers [ 0 ] ; this . listener = this . listeners [ 0 ] ; } protected createAWSLogDriver ( prefix ) { return new AwsLogDriver ( { streamPrefix : prefix } ) ; } protected findListener ( name ? ) { if ( ! name ) { return this . listener ; } for ( const listener of this . listeners ) { if ( listener . node . id === name ) { return listener ; } } throw new Error ( `Listener ${ name } is not defined. Did you define listener with name ${ name }?` ) ; } protected registerECSTargets ( service , container , targets ) { targets ?. forEach ( ( targetProps , index ) => { const idSuffix = index > 0 ? index : '' ; const targetGroup = new ApplicationTargetGroup ( this , `TargetGroup${ idSuffix }` , { vpc : service . cluster . vpc , port : targetProps . containerPort , healthCheck : { path : '/lbcheck' , protocol : ELBProtocol . HTTP , interval : Duration . seconds ( 6 ) , timeout : Duration . seconds ( 5 ) , healthyThresholdCount : 2 , unhealthyThresholdCount : 2 , } , deregistrationDelay : Duration . seconds ( 10 ) , targetType : TargetType . IP , targets : [ service . loadBalancerTarget ( { containerName : container . containerName , containerPort : targetProps . containerPort , protocol : targetProps . protocol , } ) , ] , } ) ; this . findListener ( targetProps . listener ) . addTargetGroups ( `ECSTargetGroup${ idSuffix }${ container . containerName }${ targetProps . containerPort }` , { hostHeader : targetProps . hostHeader , pathPattern : targetProps . pathPattern , priority : targetProps . priority , targetGroups : [ targetGroup ] , } ) ; this . targetGroups . push ( targetGroup ) ; } ) ; if ( this . targetGroups . length === 0 ) { throw new Error ( 'At least one target group should be specified.' ) ; } return this . targetGroups [ 0 ] ; } protected addPortMappingForTargets ( container , targets ) { for ( const target of targets ) { if ( ! container . findPortMapping ( target . containerPort , target . protocol || Protocol . TCP ) ) { container . addPortMappings ( { containerPort : target . containerPort , protocol : target . protocol } ) ; } } } /**
     * Create log driver if logging is enabled.
     */ private createLogDriver ( enableLoggingProp ? , logDriverProp ? ) { const enableLogging = enableLoggingProp !== undefined ? enableLoggingProp : true ; return logDriverProp !== undefined ? logDriverProp : enableLogging ? this . createAWSLogDriver ( this . node . id ) : undefined ; } private validateInput ( props ) { if ( props . cluster && props . vpc ) { throw new Error ( 'You can only specify either vpc or cluster. Alternatively, you can leave both blank' ) ; } if ( props . desiredCount !== undefined && props . desiredCount < 1 ) { throw new Error ( 'You must specify a desiredCount greater than 0' ) ; } if ( props . loadBalancers ) { if ( props . loadBalancers . length === 0 ) { throw new Error ( 'At least one load balancer must be specified' ) ; } for ( const lbProps of props . loadBalancers ) { if ( lbProps . listeners . length === 0 ) { throw new Error ( 'At least one listener must be specified' ) ; } } } } private createDomainName ( loadBalancer , internetFacing , name ? , zone ? ) { let domainName = loadBalancer . loadBalancerDnsName ; if ( typeof name !== 'undefined' ) { if ( typeof zone === 'undefined' ) { throw new Error ( 'A Route53 hosted domain zone name is required to configure the specified domain name' ) ; } if ( internetFacing ) { const record = new ARecord ( this , `DNS${ loadBalancer . node . id }` , { zone , recordName : name , target : RecordTarget . fromAlias ( new LoadBalancerTarget ( loadBalancer ) ) , } ) ; domainName = record . domainName ; } } return domainName ; } }