AWSTemplateFormatVersion: "2010-09-09"
Description: "Xray: Deploys the EC2 Autoscaling, LaunchConfig and Instances (qs-1rtidak0g)"
Metadata:
  cfn-lint:
    config:
      ignore_checks:
        - W9006
        - W9002
        - W9003
        - W9004
        - E9101
      ignore_reasons:
        - E9101: "'master' is part of the product naming conventions for now"
Parameters:
  PrivateSubnet1Id:
    Type: AWS::EC2::Subnet::Id
  PrivateSubnet2Id:
    Type: AWS::EC2::Subnet::Id
  KeyPairName:
    Type: AWS::EC2::KeyPair::KeyName
  MinScalingNodes:
    Type: Number
  MaxScalingNodes:
    Type: Number
  DeploymentTag:
    Type: String
  QsS3BucketName:
    Type: String
  QsS3KeyPrefix:
    Type: String
  QsS3Uri:
    Type: String
  DatabaseDriver:
    Type: String
  DatabaseType:
    Type: String
  DatabaseUser:
    Type: String
  DatabasePassword:
    Type: String
    NoEcho: 'true'
  MasterKey:
    Type: String
    NoEcho: 'true'
  ExtraJavaOptions:
    Type: String
  SecurityGroups:
    Type: String
  XrayHostProfile:
    Type: String
  XrayHostRole:
    Type: String
  XrayInstanceType:
    Type: String
  JfrogInternalUrl:
    Type: String
  VolumeSize:
    Type: Number
  XrayDatabaseUser:
    Type: String
  XrayDatabasePassword:
    Type: String
    NoEcho: 'true'
  XrayMasterDatabaseUrl:
    Type: String
  XrayDatabaseUrl:
    Type: String
  XrayVersion:
    Type: String

# To populate additional mappings use following link
# https://raw.githubusercontent.com/aws-quickstart/quickstart-linux-bastion/master/templates/linux-bastion.template
# link from centos: https://www.centos.org/download/aws-images/
Mappings:
  AWSAMIRegionMap:
    ap-northeast-1:
      CentOS7HVM: "ami-06a46da680048c8ae"
    ap-northeast-2:
      CentOS7HVM: "ami-06e83aceba2cb0907"
    ap-south-1:
      CentOS7HVM: "ami-026f33d38b6410e30"
    ap-southeast-1:
      CentOS7HVM: "ami-07f65177cb990d65b"
    ap-southeast-2:
      CentOS7HVM: "ami-0b2045146eb00b617"
    ca-central-1:
      CentOS7HVM: "ami-04a25c39dc7a8aebb"
    eu-central-1:
      CentOS7HVM: "ami-0e8286b71b81c3cc1"
    me-south-1:
      CentOS7HVM: "ami-011c71a894b10f35b"
    ap-east-1:
      CentOS7HVM: "ami-0e5c29e6c87a9644f"
    eu-north-1:
      CentOS7HVM: "ami-05788af9005ef9a93"
    eu-south-1:
      CentOS7HVM: "ami-0a84267606bcea16b"
    eu-west-1:
      CentOS7HVM: "ami-0b850cf02cc00fdc8"
    eu-west-2:
      CentOS7HVM: "ami-09e5afc68eed60ef4"
    eu-west-3:
      CentOS7HVM: "ami-0cb72d2e599cffbf9"
    sa-east-1:
      CentOS7HVM: "ami-0b30f38d939dd4b54"
    us-east-1:
      CentOS7HVM: "ami-0affd4508a5d2481b"
    us-east-2:
      CentOS7HVM: "ami-01e36b7901e884a10"
      # CentOS7HVM: "ami-07f692d95b2b9c8c5" #arm64  https://www.centos.org/download/aws-images/
    us-west-1:
      CentOS7HVM: "ami-098f55b4287a885ba"
      # CentOS7HVM: "ami-09748e99ee14e3823" #arm64  https://www.centos.org/download/aws-images/
    us-west-2:
      CentOS7HVM: "ami-0bc06212a56393ee1"
    cn-north-1:
      CentOS7HVM: "ami-0e02aaefeb74c3373"
    cn-northwest-1:
      CentOS7HVM: "ami-07183a7702633260b"
    us-gov-east-1:
      CentOS7HVM: "ami-00e30c71"
    us-gov-west-1:
      CentOS7HVM: "ami-bbba86da"

Resources:
  XrayScalingGroup:
    Type: AWS::AutoScaling::AutoScalingGroup
    Properties:
      LaunchConfigurationName: !Ref XrayLaunchConfiguration
      VPCZoneIdentifier:
        - !Ref PrivateSubnet1Id
        - !Ref PrivateSubnet2Id
      MinSize: !Ref MinScalingNodes
      MaxSize: !Ref MaxScalingNodes
      Cooldown: '300'
      DesiredCapacity: !Ref MinScalingNodes
      HealthCheckType: EC2
      HealthCheckGracePeriod: 1800
      Tags:
        - Key: Name
          Value: !Ref DeploymentTag
          PropagateAtLaunch: true
        - Key: XrayVersion
          Value: !Ref XrayVersion
          PropagateAtLaunch: true
      TerminationPolicies:
        - OldestInstance
        - Default
    CreationPolicy:
      ResourceSignal:
        Count: !Ref MinScalingNodes
        Timeout: PT60M
  XrayLaunchConfiguration:
    Type: AWS::AutoScaling::LaunchConfiguration
    Metadata:
      AWS::CloudFormation::Authentication:
        S3AccessCreds:
          type: S3
          roleName:
            - !Ref XrayHostRole
          buckets:
            - !Ref QsS3BucketName
      AWS::CloudFormation::Init:
        configSets:
          xray_ami_setup:
            - "config-cloudwatch"
            - "config-ansible-xray-ami"
          xray_install:
            - "config-cloudwatch"
            - "config-ansible-xray-ami"
            - "config-xray"
            - "secure-xray"
        config-cloudwatch:
          files:
            /root/cloudwatch.conf:
              content: |
                [general]
                state_file = /var/awslogs/state/agent-state

                [/var/log/messages]
                file = /var/log/messages
                log_group_name = /xray/instances/{instance_id}
                log_stream_name = /var/log/messages/
                datetime_format = %b %d %H:%M:%S

                [/var/log/xray-ami-setup.log]
                file = /var/log/messages
                log_group_name = /xray/instances/{instance_id}
                log_stream_name = /var/log/xray-ami-setup.log
                datetime_format = %b %d %H:%M:%S

                [/var/log/xray.log]
                file = /var/log/messages
                log_group_name = /xray/instances/{instance_id}
                log_stream_name = /var/log/xray.log
                datetime_format = %b %d %H:%M:%S
              mode: "0400"
        config-ansible-xray-ami:
          files:
            /root/.xray_ami/xray-ami-setup.yml:
              content: !Sub |
                  # Base install for Xray
                  - import_playbook: xray-ami.yml
                    vars:
                      ami_creation: false
                      db_type: postgresql
                      db_driver: org.postgresql.Driver
                      xray_version: ${XrayVersion}
                      xray_ha_enabled: false
              mode: "0400"
        config-xray:
          files:
            /root/create_rabbitmq_cluster.sh:
              content: !Sub |
                #!/usr/bin/env bash
                export HOME=/root
                cd
                cp ~xray/.erlang.cookie /root/
                curl http://localhost:15672/cli/rabbitmqadmin > ./rabbitmqadmin
                chmod 755 ./rabbitmqadmin
                date
                # Add a random wait up to a max of 300 secs, so all servers start at different times.
                # this helps all rabbitmq nodes to detect the same cluster
                # without this sleep, rabbit
                sleep $(( ( RANDOM % 300 )  + 100 ))
                date
                curl -s http://localhost:8046/router/api/v1/topology/health
                OTHERHOST=$(curl -s http://localhost:8046/router/api/v1/topology/health | jq -r '.nodes | keys[] as $k | "\($k), \(.[$k].effective_state), \(.[$k].health_response.services[].service_id)"' | grep -v UNHEALTHY | grep jfx | grep -v $(curl http://169.254.169.254/latest/meta-data/local-ipv4) | cut -d ',' -f 1-1 | uniq | head -1 | sed "s/.*http:\/\/\(.*\):8082.*/\1/")
                echo "OTHERHOST = $OTHERHOST"
                CLUSTER=$(./rabbitmqadmin -H $OTHERHOST list nodes | grep rabbit | head -1 | cut -s -d ' ' -f 2-2)
                echo "CLUSTER = $CLUSTER"
                if [ -z "$CLUSTER" ]
                then
                  echo "No cluster found. Treating this node as first node"
                  # no other node is found. So this must be first node.
                  # no need to connect to cluster, let it form its own cluster
                else
                  /opt/jfrog/jfrog-xray*/app/third-party/rabbitmq/sbin/rabbitmqctl stop_app
                  /opt/jfrog/jfrog-xray*/app/third-party/rabbitmq/sbin/rabbitmqctl force_reset
                  /opt/jfrog/jfrog-xray*/app/third-party/rabbitmq/sbin/rabbitmqctl join_cluster $CLUSTER
                fi
                /opt/jfrog/jfrog-xray*/app/third-party/rabbitmq/sbin/rabbitmqctl start_app
                /opt/jfrog/jfrog-xray*/app/third-party/rabbitmq/sbin/rabbitmqctl cluster_status
              mode: "0770"
            /root/.xray_ami/xray.yml:
              content: !Sub |
                # Base install for Xray
                - import_playbook: site-xray.yml
                  vars:
                    jfrog_url: ${JfrogInternalUrl}
                    master_key: ${MasterKey}
                    join_key: ${MasterKey}
                    extra_java_opts: ${ExtraJavaOptions}
                    db_type: ${DatabaseType}
                    db_driver: ${DatabaseDriver}
                    db_master_url: postgresql://${DatabaseUser}:${DatabasePassword}@${XrayMasterDatabaseUrl}
                    db_url: postgres://${XrayDatabaseUrl}
                    db_master_user: ${DatabaseUser}
                    db_user: ${XrayDatabaseUser}
                    db_password: ${XrayDatabasePassword}
                    xray_version: ${XrayVersion}
              mode: "0400"
            /root/.vault_pass.txt:
              content: !Sub |
                ${DatabasePassword}
              mode: "0400"
            /root/.secureit.sh:
              content:
                ansible-vault encrypt /root/.xray_ami/xray.yml --vault-id /root/.vault_pass.txt
              mode: "0770"
        secure-xray:
          commands:
            'secure ansible playbook':
              command: '/root/.secureit.sh'
              ignoreErrors: 'false'
    Properties:
      KeyName: !Ref KeyPairName
      IamInstanceProfile: !Ref XrayHostProfile
      ImageId: !FindInMap
        - AWSAMIRegionMap
        - !Ref AWS::Region
        - 'CentOS7HVM'
      SecurityGroups:
        - !Ref SecurityGroups
      InstanceType: !Ref XrayInstanceType
      BlockDeviceMappings:
        - DeviceName: /dev/xvda
          Ebs:
            VolumeSize: !Ref VolumeSize
            VolumeType: gp2
            DeleteOnTermination: true
            Encrypted: true
      UserData:
        Fn::Base64:
          !Sub |
            #!/bin/bash -x
            exec > >(tee /var/log/user-data.log|logger -t user-data -s 2>/dev/console) 2>&1

            #CFN Functions

            function cfn_fail

            {

            cfn-signal -e 1 --stack ${AWS::StackName} --region ${AWS::Region} --resource XrayScalingGroup

            exit 1

            }

            function cfn_success

            {

            cfn-signal -e 0 --stack ${AWS::StackName} --region ${AWS::Region} --resource XrayScalingGroup

            exit 0

            }

            S3URI=${QsS3Uri}
            yum install -y epel-release

            yum update --security -y &> /var/log/userdata.yum_security_update.log

            yum install -y git python3 libselinux-python3 jq
            yum install -y postgresql-server postgresql-devel

            echo $PATH

            PATH=/opt/aws/bin:$PATH

            echo $PATH

            # Create virtual env and activate
            python3 -m venv ~/venv --system-site-packages
            source ~/venv/bin/activate

            pip install --upgrade pip
            pip install wheel

            # Install Cloudformation helper scripts
            pip install https://s3.amazonaws.com/cloudformation-examples/aws-cfn-bootstrap-py3-latest.tar.gz 2>&1 | tee /var/log/userdata.aws_cfn_bootstrap_install.log

            pip install awscli &> /var/log/userdata.awscli_install.log

            pip install ansible &> /var/log/userdata.ansible_install.log

            mkdir ~/.xray_ami

            aws s3 --region ${AWS::Region} sync s3://${QsS3BucketName}/${QsS3KeyPrefix}cloudInstallerScripts/ ~/.xray_ami/

            setsebool httpd_can_network_connect 1 -P

            # CentOS cloned virtual machines do not create a new machine id
            # https://www.thegeekdiary.com/centos-rhel-7-how-to-change-the-machine-id/
            rm -f /etc/machine-id
            systemd-machine-id-setup

            cfn-init -v --stack ${AWS::StackName} --resource XrayLaunchConfiguration --configsets xray_install --region ${AWS::Region} || cfn_fail

            # Setup CloudWatch Agent
            curl https://s3.amazonaws.com/aws-cloudwatch/downloads/latest/awslogs-agent-setup.py -O
            chmod +x ./awslogs-agent-setup.py
            ./awslogs-agent-setup.py -n -r ${AWS::Region} -c /root/cloudwatch.conf

            lsblk # debug

            ansible-galaxy collection install community.general ansible.posix

            ansible-playbook /root/.xray_ami/xray-ami-setup.yml --vault-id /root/.vault_pass.txt 2>&1 | tee /var/log/xray-ami.log || cfn_fail
            ansible-playbook /root/.xray_ami/xray.yml --vault-id /root/.vault_pass.txt 2>&1 | tee /var/log/xray.log || cfn_fail

            rm -rf /root/.secureit.sh

            cfn_success &> /var/log/cfn_success.log
            cfn_success || cfn_fail
