---
Parameters:
  AppMeshXdsEndpoint:
    Type: String
    Description: App Mesh XDS Endpoint Override (normally don't set this)
    Default: ""

  EnvoyImage:
    Type: String
    Description: Envoy container image

  ColorTellerImage:
    Type: String
    Description: Color Teller container image

  LaunchType:
    Type: String
    Description: ECS Task Launch Type
    Default: FARGATE

Resources:
  Cluster:
    Type: AWS::ECS::Cluster
    Properties:
      ClusterName: !Ref AWS::StackName

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

  LogGroup:
    Type: AWS::Logs::LogGroup
    Properties:
      LogGroupName: !Sub '${AWS::StackName}-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/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

  CloudMapNamespace:
    Type: AWS::ServiceDiscovery::PrivateDnsNamespace
    Properties:
      Name: !Sub '${AWS::StackName}-mesh.local'
      Vpc:
        Fn::ImportValue:
          !Sub "${AWS::StackName}-vpc:VPC"

  ColorTellerServiceRegistry:
    Type: AWS::ServiceDiscovery::Service
    Properties:
      Name: 'colorteller'
      DnsConfig:
        NamespaceId: !GetAtt 'CloudMapNamespace.Id'
        DnsRecords:
          - Type: A
            TTL: 300
      HealthCheckCustomConfig:
        FailureThreshold: 1

  ColorGatewayServiceRegistry:
    Type: AWS::ServiceDiscovery::Service
    Properties:
      Name: 'colorgateway'
      DnsConfig:
        NamespaceId: !GetAtt 'CloudMapNamespace.Id'
        DnsRecords:
          - Type: A
            TTL: 300
      HealthCheckCustomConfig:
        FailureThreshold: 1

  PublicLoadBalancerSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: 'Access to the public facing load balancer'
      VpcId:
        Fn::ImportValue:
          !Sub "${AWS::StackName}-vpc: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 '${AWS::StackName}-vpc:PublicSubnet1'
      - Fn::ImportValue:
          !Sub '${AWS::StackName}-vpc:PublicSubnet2'
      SecurityGroups:
      - !Ref PublicLoadBalancerSecurityGroup

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

  PublicLoadBalancerListener:
    DependsOn:
    - PublicLoadBalancer
    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

  ColorGatewayTaskDefinition:
    Type: AWS::ECS::TaskDefinition
    Properties:
      RequiresCompatibilities:
      - !Ref LaunchType
      Family: 'colorgateway'
      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: 9080
          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 '${AWS::StackName}-log-group'
            awslogs-region: !Ref AWS::Region
            awslogs-stream-prefix: 'colorgateway'
        Environment:
        - Name: 'APPMESH_RESOURCE_ARN'
          Value: !Sub 'mesh/${AWS::StackName}-mesh/virtualGateway/colorgateway'
        - Name: 'ENVOY_LOG_LEVEL'
          Value: 'debug'

  BlueColorTellerTaskDefinition:
    Type: AWS::ECS::TaskDefinition
    Properties:
      RequiresCompatibilities:
      - !Ref LaunchType
      Family: 'blue'
      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: '9080'
        - Name: 'EgressIgnoredIPs'
          Value: '169.254.170.2,169.254.169.254'
      ContainerDefinitions:
      - Name: 'app'
        Image: !Ref ColorTellerImage
        Essential: true
        DependsOn:
        - ContainerName: "envoy"
          Condition: "HEALTHY"
        LogConfiguration:
          LogDriver: 'awslogs'
          Options:
            awslogs-group: !Sub ${AWS::StackName}-log-group
            awslogs-region: !Ref AWS::Region
            awslogs-stream-prefix: 'blue'
        PortMappings:
        - ContainerPort: 9080
          Protocol: 'tcp'
        Environment:
        - Name: "COLOR"
          Value: "blue"
        - Name: "SERVER_PORT"
          Value: "9080"
        - Name: "STAGE"
          Value: ""
      - 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 '${AWS::StackName}-log-group'
            awslogs-region: !Ref AWS::Region
            awslogs-stream-prefix: 'blue'
        Environment:
        - Name: 'APPMESH_RESOURCE_ARN'
          Value: !Sub 'mesh/${AWS::StackName}-mesh/virtualNode/colorteller-blue-node'
        - Name: 'ENVOY_LOG_LEVEL'
          Value: 'debug'

  GreenColorTellerTaskDefinition:
    Type: AWS::ECS::TaskDefinition
    Properties:
      RequiresCompatibilities:
      - !Ref LaunchType
      Family: 'green'
      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: '9080'
        - Name: 'EgressIgnoredIPs'
          Value: '169.254.170.2,169.254.169.254'
      ContainerDefinitions:
      - Name: 'app'
        Image: !Ref ColorTellerImage
        Essential: true
        DependsOn:
        - ContainerName: "envoy"
          Condition: "HEALTHY"
        LogConfiguration:
          LogDriver: 'awslogs'
          Options:
            awslogs-group: !Sub '${AWS::StackName}-log-group'
            awslogs-region: !Ref AWS::Region
            awslogs-stream-prefix: 'green'
        PortMappings:
        - ContainerPort: 9080
          Protocol: 'tcp'
        Environment:
        - Name: "COLOR"
          Value: "green"
        - Name: "SERVER_PORT"
          Value: "9080"
        - Name: "STAGE"
          Value: ""
      - 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 '${AWS::StackName}-log-group'
            awslogs-region: !Ref AWS::Region
            awslogs-stream-prefix: 'green'
        Environment:
        - Name: 'APPMESH_RESOURCE_ARN'
          Value: !Sub 'mesh/${AWS::StackName}-mesh/virtualNode/colorteller-green-node'
        - Name: 'ENVOY_LOG_LEVEL'
          Value: 'debug'

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

  BlueColorTellerService:
    Type: AWS::ECS::Service
    Properties:
      Cluster: !Ref Cluster
      DeploymentConfiguration:
        MaximumPercent: 200
        MinimumHealthyPercent: 100
      DesiredCount: 1
      LaunchType: !Ref LaunchType
      ServiceRegistries:
      - RegistryArn: !GetAtt 'ColorTellerServiceRegistry.Arn'
      NetworkConfiguration:
        AwsvpcConfiguration:
          AssignPublicIp: DISABLED
          SecurityGroups:
          - !Ref SecurityGroup
          Subnets:
          - Fn::ImportValue:
              !Sub '${AWS::StackName}-vpc:PrivateSubnet1'
          - Fn::ImportValue:
              !Sub '${AWS::StackName}-vpc:PrivateSubnet2'
      TaskDefinition: !Ref BlueColorTellerTaskDefinition

  GreenColorTellerService:
    Type: AWS::ECS::Service
    Properties:
      Cluster: !Ref Cluster
      DeploymentConfiguration:
        MaximumPercent: 200
        MinimumHealthyPercent: 100
      DesiredCount: 1
      LaunchType: !Ref LaunchType
      ServiceRegistries:
      - RegistryArn: !GetAtt 'ColorTellerServiceRegistry.Arn'
      NetworkConfiguration:
        AwsvpcConfiguration:
          AssignPublicIp: DISABLED
          SecurityGroups:
          - !Ref SecurityGroup
          Subnets:
          - Fn::ImportValue:
              !Sub '${AWS::StackName}-vpc:PrivateSubnet1'
          - Fn::ImportValue:
              !Sub '${AWS::StackName}-vpc:PrivateSubnet2'
      TaskDefinition: !Ref GreenColorTellerTaskDefinition

Outputs:
  ColorGatewayEndpoint:
    Description: 'Public endpoint for Color Gateway service'
    Value: !Join ['', ['http://', !GetAtt 'PublicLoadBalancer.DNSName']]
    Export:
      Name: !Sub '${AWS::StackName}:ColorGatewayEndpoint'
