Parameters:
  ProjectName:
    Type: String
    Description: Project name to link stacks

  EnvoyImage:
    Type: String
    Description: Envoy container image

  BackendImage:
    Type: String
    Description: Color client app container image

  ContainerPort:
    Type: Number
    Description: Port number to use for applications
    Default: 80

Resources:
  PublicLoadBalancerSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: 'Access to the public facing load balancer'
      VpcId:
        Fn::ImportValue: !Sub '${ProjectName}:VPC'
      SecurityGroupIngress:
      - CidrIp: 0.0.0.0/0
        IpProtocol: tcp
        FromPort: 80
        ToPort: 80

  PublicLoadBalancer:
    Type: AWS::ElasticLoadBalancingV2::LoadBalancer
    Properties:
      Scheme: internet-facing
      LoadBalancerAttributes:
      - Key: idle_timeout.timeout_seconds
        Value: '30'
      Subnets:
      - Fn::ImportValue: !Sub '${ProjectName}:PublicSubnet1'
      - Fn::ImportValue: !Sub '${ProjectName}:PublicSubnet2'
      SecurityGroups:
      - !Ref PublicLoadBalancerSecurityGroup

  WebTargetGroup:
    Type: AWS::ElasticLoadBalancingV2::TargetGroup
    Properties:
      HealthCheckIntervalSeconds: 6
      HealthCheckPath: '/ready'
      HealthCheckPort: 9901
      HealthCheckProtocol: HTTP
      HealthCheckTimeoutSeconds: 5
      HealthyThresholdCount: 2
      TargetType: ip
      Name: !Sub '${ProjectName}-webtarget'
      Port: 80
      Protocol: HTTP
      UnhealthyThresholdCount: 2
      TargetGroupAttributes:
      - Key: deregistration_delay.timeout_seconds
        Value: 120
      VpcId:
        Fn::ImportValue: !Sub '${ProjectName}:VPC'

  PublicLoadBalancerListener:
    Type: AWS::ElasticLoadBalancingV2::Listener
    Properties:
      DefaultActions:
      - TargetGroupArn: !Ref WebTargetGroup
        Type: 'forward'
      LoadBalancerArn: !Ref PublicLoadBalancer
      Port: 80
      Protocol: HTTP

  WebLoadBalancerRule:
    Type: AWS::ElasticLoadBalancingV2::ListenerRule
    Properties:
      Actions:
      - TargetGroupArn: !Ref WebTargetGroup
        Type: 'forward'
      Conditions:
      - Field: path-pattern
        Values:
        - '*'
      ListenerArn: !Ref PublicLoadBalancerListener
      Priority: 1

  TaskSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: "Security group for the tasks"
      VpcId:
        Fn::ImportValue: !Sub '${ProjectName}:VPC'
      SecurityGroupIngress:
      - CidrIp:
          Fn::ImportValue: !Sub '${ProjectName}:VpcCIDR'
        IpProtocol: -1

  LogGroup:
    Type: AWS::Logs::LogGroup
    Properties:
      LogGroupName: !Sub '${ProjectName}-log-group'
      RetentionInDays: 30

  TaskIamRole:
    Type: AWS::IAM::Role
    Properties:
      Path: /
      AssumeRolePolicyDocument: |
        {
          "Statement": [{
            "Effect": "Allow",
            "Principal": { "Service": [ "ecs-tasks.amazonaws.com" ]},
            "Action": [ "sts:AssumeRole" ]
          }]
        }
      ManagedPolicyArns:
      - arn:aws:iam::aws:policy/CloudWatchFullAccess
      - arn:aws:iam::aws:policy/AWSXRayDaemonWriteAccess
      - arn:aws:iam::aws:policy/AWSAppMeshEnvoyAccess

  TaskExecutionIamRole:
    Type: AWS::IAM::Role
    Properties:
      Path: /
      AssumeRolePolicyDocument: |
        {
          "Statement": [{
            "Effect": "Allow",
            "Principal": { "Service": [ "ecs-tasks.amazonaws.com" ]},
            "Action": [ "sts:AssumeRole" ]
          }]
        }
      ManagedPolicyArns:
      - arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly
      - arn:aws:iam::aws:policy/CloudWatchLogsFullAccess

  SecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: "Security group for the instances"
      VpcId:
        Fn::ImportValue: !Sub '${ProjectName}:VPC'
      SecurityGroupIngress:
      - CidrIp:
          Fn::ImportValue: !Sub '${ProjectName}:VpcCIDR'
        IpProtocol: -1
 
  GatewayRegistry:
    Type: AWS::ServiceDiscovery::Service
    Properties:
      Name: 'gateway'
      DnsConfig:
        NamespaceId:
          Fn::ImportValue: !Sub '${ProjectName}:ECSServiceDiscoveryNamespace'
        DnsRecords:
        - Type: A
          TTL: 300
      HealthCheckCustomConfig:
        FailureThreshold: 1

  GatewayTaskDef:
    Type: AWS::ECS::TaskDefinition
    Properties:
      RequiresCompatibilities:
      - 'FARGATE'
      Family: !Sub '${ProjectName}-gateway'
      NetworkMode: 'awsvpc'
      Cpu: 256
      Memory: 512
      TaskRoleArn: !Ref TaskIamRole
      ExecutionRoleArn: !Ref TaskExecutionIamRole
      ContainerDefinitions:
      - Name: envoy
        Image: !Ref EnvoyImage
        Essential: true
        Ulimits:
        - Name: "nofile"
          HardLimit: 15000
          SoftLimit: 15000
        PortMappings:
        - ContainerPort: 9901
          Protocol: 'tcp'
        - ContainerPort: 15000
          Protocol: 'tcp'
        - ContainerPort: 15001
          Protocol: 'tcp'
        - ContainerPort: 80
          Protocol: 'tcp'
        HealthCheck:
          Command:
          - 'CMD-SHELL'
          - 'curl -s http://localhost:9901/server_info | grep state | grep -q LIVE'
          Interval: 5
          Timeout: 2
          Retries: 3
        LogConfiguration:
          LogDriver: 'awslogs'
          Options:
            awslogs-group: !Sub '${ProjectName}-log-group'
            awslogs-region: !Ref AWS::Region
            awslogs-stream-prefix: 'gateway-envoy'
        Environment:
        - Name: 'APPMESH_RESOURCE_ARN'
          Value: !Sub 'mesh/${ProjectName}-mesh/virtualGateway/gateway-vg'
        - Name: 'ENVOY_LOG_LEVEL'
          Value: 'debug'

  GatewayService:
    Type: AWS::ECS::Service
    DependsOn:
    - WebLoadBalancerRule
    Properties:
      Cluster:
        Fn::ImportValue: !Sub '${ProjectName}:ECSCluster'
      DeploymentConfiguration:
        MaximumPercent: 200
        MinimumHealthyPercent: 100
      DesiredCount: 1
      LaunchType: 'FARGATE'
      ServiceRegistries:
      - RegistryArn: !GetAtt 'GatewayRegistry.Arn'
      NetworkConfiguration:
        AwsvpcConfiguration:
          AssignPublicIp: DISABLED
          SecurityGroups:
          - !Ref SecurityGroup
          Subnets:
          - Fn::ImportValue: !Sub '${ProjectName}:PrivateSubnet1'
          - Fn::ImportValue: !Sub '${ProjectName}:PrivateSubnet2'
      TaskDefinition: !Ref GatewayTaskDef
      LoadBalancers:
      - ContainerName: envoy
        ContainerPort: !Ref ContainerPort
        TargetGroupArn: !Ref WebTargetGroup

  BackendRegistry:
    Type: AWS::ServiceDiscovery::Service
    Properties:
      Name: 'backend'
      DnsConfig:
        NamespaceId:
          Fn::ImportValue: !Sub '${ProjectName}:ECSServiceDiscoveryNamespace'
        DnsRecords:
        - Type: A
          TTL: 300
      HealthCheckCustomConfig:
        FailureThreshold: 1

  BackendTaskDef:
    Type: AWS::ECS::TaskDefinition
    Properties:
      RequiresCompatibilities:
      - 'FARGATE'
      Family: !Sub '${ProjectName}-backend'
      NetworkMode: 'awsvpc'
      Cpu: 256
      Memory: 512
      TaskRoleArn: !Ref TaskIamRole
      ExecutionRoleArn: !Ref TaskExecutionIamRole
      ProxyConfiguration:
        Type: 'APPMESH'
        ContainerName: 'envoy'
        ProxyConfigurationProperties:
        - Name: 'IgnoredUID'
          Value: '1337'
        - Name: 'ProxyIngressPort'
          Value: '15000'
        - Name: 'ProxyEgressPort'
          Value: '15001'
        - Name: 'AppPorts'
          Value: !Sub '${ContainerPort}'
        - Name: 'EgressIgnoredIPs'
          Value: '169.254.170.2,169.254.169.254'
      ContainerDefinitions:
      - Name: 'app'
        Image: !Ref BackendImage
        Essential: true
        LogConfiguration:
          LogDriver: 'awslogs'
          Options:
            awslogs-group: !Sub '${ProjectName}-log-group'
            awslogs-region: !Ref AWS::Region
            awslogs-stream-prefix: 'backend-1'
        PortMappings:
        - ContainerPort: !Ref ContainerPort
          Protocol: 'tcp'
      - Name: envoy
        Image: !Ref EnvoyImage
        Essential: true
        User: '1337'
        Ulimits:
        - Name: "nofile"
          HardLimit: 15000
          SoftLimit: 15000
        PortMappings:
        - ContainerPort: 9901
          Protocol: 'tcp'
        - ContainerPort: 15000
          Protocol: 'tcp'
        - ContainerPort: 15001
          Protocol: 'tcp'
        HealthCheck:
          Command:
          - 'CMD-SHELL'
          - 'curl -s http://localhost:9901/server_info | grep state | grep -q LIVE'
          Interval: 5
          Timeout: 2
          Retries: 3
        LogConfiguration:
          LogDriver: 'awslogs'
          Options:
            awslogs-group: !Sub '${ProjectName}-log-group'
            awslogs-region: !Ref AWS::Region
            awslogs-stream-prefix: 'backend-envoy'
        Environment:
        - Name: 'APPMESH_RESOURCE_ARN'
          Value: !Sub 'mesh/${ProjectName}-mesh/virtualNode/backend-1-vn'
        - Name: 'ENVOY_LOG_LEVEL'
          Value: 'debug'

  BackendService:
    Type: AWS::ECS::Service
    Properties:
      Cluster:
        Fn::ImportValue: !Sub '${ProjectName}:ECSCluster'
      DeploymentConfiguration:
        MaximumPercent: 200
        MinimumHealthyPercent: 100
      DesiredCount: 1
      LaunchType: 'FARGATE'
      ServiceRegistries:
      - RegistryArn: !GetAtt 'BackendRegistry.Arn'
      NetworkConfiguration:
        AwsvpcConfiguration:
          AssignPublicIp: DISABLED
          SecurityGroups:
          - !Ref SecurityGroup
          Subnets:
          - Fn::ImportValue: !Sub '${ProjectName}:PrivateSubnet1'
          - Fn::ImportValue: !Sub '${ProjectName}:PrivateSubnet2'
      TaskDefinition: !Ref BackendTaskDef

Outputs:
  PublicEndpoint:
    Description: 'Public endpoint for the color client service'
    Value: !Join ['', ['http://', !GetAtt 'PublicLoadBalancer.DNSName']]
    Export:
      Name: !Sub '${ProjectName}:PublicEndpoint'
