AWSTemplateFormatVersion: '2010-09-09'
Description: Creates a single EC2 instance with a pruned Bitcoin Core node, C-Lightning,
  Lightning Charge, Postgres, Ruby on Rails and Matreon.
Metadata:
  AWS::CloudFormation::Interface:
    ParameterGroups:
      - Label:
          default: Server
        Parameters:
          - KeyName
          - BugsEmail
      - Label:
          default: Bitcoin
        Parameters:
          - Network
      - Label:
          default: Matreon
        Parameters:
          - Prefix
          - Domain
          - FromEmail
          - SmtpHost
          - SmtpUser
          - SmtpPassword
          - SmtpPort
      - Label:
          default: Podcast
        Parameters:
          - Podcast
          - PodcastTitle
          - PodcastImage
          - PodcastUrl
      - Label:
          default: Developer
        Parameters:
          - Repository
          - Branch

Parameters:
  KeyName:
    Description: Name of an existing EC2 KeyPair to enable SSH access to the instances
    Type: AWS::EC2::KeyPair::KeyName
    ConstraintDescription: must be the name of an existing EC2 KeyPair.
  Network:
    Description: Real Bitcoin or testnet
    Type: String
    Default: testnet
    AllowedValues:
      - testnet
      - bitcoin
  Prefix:
    Type: String
    Description: For https:// we'll automatically request a certificate. You agree to the ACME server's Subscriber Agreement.
    Default: 'https'
    AllowedValues:
      - 'https'
      - 'http'
  Domain:
    Default: example.com
    Description: Domain without http(s), no trailing slash
    Type: String
    MinLength: '5'
    MaxLength: '100'
  FromEmail:
    Default: you@example.com
    Description: From email address. Also submitted during HTTPS certificate registration.
    Type: String
    MinLength: '5'
    MaxLength: '100'
  BugsEmail:
    Default: bugs@example.com
    Description: Bug report email address
    Type: String
    MinLength: '5'
    MaxLength: '100'
  SmtpHost:
    Default: smtp.fastmail.com
    Description: SMTP server
    Type: String
    MinLength: '5'
    MaxLength: '100'
  SmtpPort:
    Default: '587'
    Description: SMTP port
    Type: String
    MinLength: '1'
    MaxLength: '5'
  SmtpUser:
    Default: you@example.com
    Description: SMTP username
    Type: String
    MinLength: '5'
    MaxLength: '100'
  SmtpPassword:
    Default: ''
    Description: SMTP password
    Type: String
    NoEcho: 'true'
    MinLength: '0'
    MaxLength: '100'
  Podcast:
    Description: Enable podcast feature. Currently requires an existing RSS source.
    Type: String
    Default: '0'
    AllowedValues:
      - '0'
      - '1'
  PodcastTitle:
    Description: Podcast title
    Type: String
    Default: ''
    MinLength: '0'
    MaxLength: '100'
  PodcastImage:
    Description: Podcast image URL
    Type: String
    Default: ''
    MinLength: '0'
    MaxLength: '255'
  PodcastUrl:
    Description: Existing podcast RSS feed
    Type: String
    Default: ''
    MinLength: '0'
    MaxLength: '255'
  GitURL:
    Description: Git repository URL
    Type: String
    MinLength: '0'
    MaxLength: '255'
    Default: 'https://github.com/Sjors/matreon.git'
  GitBranch:
    Description: Git branch
    Type: String
    MinLength: '0'
    MaxLength: '255'
    Default: 'master'
Conditions:
  NetworkBitcoin: !Equals [!Ref 'Network', 'bitcoin']
  NetworkTestnet: !Equals [!Ref 'Network', 'testnet']
  UseDomain: !Not [!Equals [!Ref 'Domain', '']]
  SSL: !Equals [!Ref 'Prefix', 'https']

Resources:
  WebServer:
    Type: AWS::EC2::Instance
    DependsOn: IPAddress
    Metadata:
      AWS::CloudFormation::Init:
        configSets:
          full_install:
            - install_cfn
            - users_groups
            - env_files
            - storage
            - shared_packages_managers_languages
            - clone_repo
            - install_bitcoind
            - prepare_initial_blockchain_download
            - install_lightningd
            - install_lightning_charge
            - install_postgres
            - install_rails
            - install_nginx
            - install_certbot
            - prepare_cron_and_services
        install_cfn:
          files:
            /etc/cfn/cfn-hup.conf:
              content: !Sub |
                [main]
                stack=${AWS::StackId}
                region=${AWS::Region}
              mode: '000400'
              owner: root
              group: root
            /etc/cfn/hooks.d/cfn-auto-reloader.conf:
              content: !Sub |
                [cfn-auto-reloader-hook]
                triggers=post.update
                path=Resources.WebServer.Metadata.AWS::CloudFormation::Init
                action=/opt/aws/bin/cfn-init -v          --stack ${AWS::StackName}         --resource WebServer          --configsets full_install          --region ${AWS::Region}
                runas=root
              mode: '000400'
              owner: root
              group: root
          services:
            sysvinit:
              cfn-hup:
                enabled: 'true'
                ensureRunning: 'true'
                files:
                  - /etc/cfn/cfn-hup.conf
                  - /etc/cfn/hooks.d/cfn-auto-reloader.conf

        users_groups:
          commands:
            01_bitcoin:
              command: groupadd -r bitcoin && useradd -r -m -g bitcoin bitcoin
            02_charge:
              command: groupadd -r charge && useradd -r -m -g charge charge
            03_matreon:
              command: groupadd -r matreon && useradd -r -m -g matreon matreon
            04_certbot:
              command: groupadd -r certbot && useradd -r -m -g certbot certbot
            10_lightningrpc_group:
              command: groupadd lightningrpc
                       && usermod -a -G lightningrpc bitcoin
                       && usermod -a -G lightningrpc charge

        env_files:
          files:
            /root/.env:
              content: !Sub |
                DOMAIN=${Domain}
                IP=${IPAddress}
                EMAIL=${FromEmail}

            /home/matreon/.env:
              content: !Sub |
                RAILS_ENV=production
                NODE_ENV=production
                DATABASE_URL=postgres://matreon@localhost:5432
                HOSTNAME=${Prefix}://${Domain}
                FROM_EMAIL=${FromEmail}
                BUGS_TO=${BugsEmail}
                SMTP_HOST=${SmtpHost}
                SMTP_PORT=${SmtpPort}
                SMTP_USERNAME=${SmtpUser}
                SMTP_PASSWORD=${SmtpPassword}

                PODCAST=${Podcast}
                PODCAST_TITLE=${PodcastTitle}
                PODCAST_URL=${PodcastUrl}
                PODCAST_IMAGE=${PodcastImage}
              mode: '000400'
              owner: matreon
              group: matreon

            /home/charge/.env:
              content: "\n"
              mode: '000400'
              owner: charge
              group: charge

          commands:
            20_generate_lightning_charge_api_token:
              command: export API_TOKEN=`hexdump -n 64 -e '16/4 "%08x" 1 "\n"' /dev/random`
                       && echo "LIGHTNING_CHARGE_API_TOKEN=$API_TOKEN" >> /home/matreon/.env
                       && echo "API_TOKEN=$API_TOKEN" >> /home/charge/.env

        storage:
          commands:
            01_format_magnetic_volume:
              command: mkfs -t ext4 /dev/xvdb
            02_mount_magnetic_volume:
              command: mkdir /mnt/magnetic
                      && echo "/dev/xvdb /mnt/magnetic ext4 defaults,nofail 0 2" >> /etc/fstab
                      && mount -a
            10_format_ssd:
              command: mkfs.ext4 -E nodiscard /dev/nvme0n1
            11_mount_ssd:
              command: mkdir /mnt/ssd && mount -o discard /dev/nvme0n1 /mnt/ssd

        shared_packages_managers_languages:
          packages:
            yum:
              git: []
              jq: []
              patch: []
              gcc: []
              gcc-c++: []
              automake: []
              libtool: []
              gmp-devel: []
              sqlite-devel: []
              python3: []
              net-tools: []

          commands:
            01_upgrade:
              command: yum update -y
            10_add_npm_repo:
              command: curl --silent --location https://rpm.nodesource.com/setup_8.x | bash -
            11_install_node:
              command: yum -y install nodejs
            12_install_yarn:
              command: npm install -g yarn
            20_install_ruby:
              command: amazon-linux-extras install ruby2.4
            21_install_bundler:
              command: yum install -y redhat-rpm-config ruby-devel
                       && gem install bundler --no-document

        clone_repo:
          commands:
            01_clone_repo:
              command: !Sub
                git clone ${GitURL} /usr/local/src/matreon && cd /usr/local/src/matreon && git checkout ${GitBranch}

        install_bitcoind:
          commands:
            01_install:
              command: sh /usr/local/src/matreon/vendor/bitcoin/install.sh
            02_copy_bitcoind_config:
              command: mkdir /etc/bitcoin && cp /usr/local/src/matreon/vendor/bitcoin/bitcoin.conf /etc/bitcoin/bitcoin.conf
                       && chmod 444 /etc/bitcoin/bitcoin.conf
            03_set_bitcoin_network:
              command:
                !If
                  - NetworkBitcoin
                  - echo
                  - echo "testnet=1" >> /etc/bitcoin/bitcoin.conf
            04_create_datadir:
              command: mkdir /home/bitcoin/.bitcoin && chown -R bitcoin:bitcoin /home/bitcoin/.bitcoin
            05_create_blocks_dir:
              command:
                !If
                  - NetworkBitcoin
                  - su - bitcoin --command "mkdir -p ~/.bitcoin/blocks-index"
                    && mkdir /mnt/magnetic/blocks && chown -R bitcoin:bitcoin /mnt/magnetic/blocks
                    && su - bitcoin --command "ln -s /home/bitcoin/.bitcoin/blocks-index /mnt/magnetic/blocks/index"
                    && su - bitcoin --command "ln -s /mnt/magnetic/blocks /home/bitcoin/.bitcoin/blocks"
                  - su - bitcoin --command "mkdir -p ~/.bitcoin/testnet3/blocks-index"
                    && mkdir /mnt/magnetic/blocks-testnet3 && chown -R bitcoin:bitcoin /mnt/magnetic/blocks-testnet3
                    && su - bitcoin --command "ln -s /home/bitcoin/.bitcoin/testnet3/blocks-index /mnt/magnetic/blocks-testnet3/index"
                    && su - bitcoin --command "ln -s /mnt/magnetic/blocks-testnet3 /home/bitcoin/.bitcoin/testnet3/blocks"

        prepare_initial_blockchain_download:
          commands:
            01_add_ssd_bitcoin_dir:
              command: mkdir /mnt/ssd/bitcoin && chown -R bitcoin:bitcoin /mnt/ssd/bitcoin
                       && ln -s /mnt/ssd/bitcoin /home/bitcoin/big-disk && chown -h bitcoin:bitcoin /home/bitcoin/big-disk

        install_lightningd:
          packages:
            yum:
              zlib-devel: []
          commands:
            01_clone_repo:
              command: git clone https://github.com/ElementsProject/lightning /usr/local/src/lightning
            02_checkout_release:
              command: cd /usr/local/src/lightning && git checkout v0.6rc1
            03_configure:
              command: cd /usr/local/src/lightning && ./configure
            04_make:
              command: cd /usr/local/src/lightning && make && make install
            10_create_datadir_and_copy_config:
              command: mkdir /home/bitcoin/.lightning && cp /usr/local/src/matreon/vendor/lightning/config /home/bitcoin/.lightning
                       && chown -R bitcoin:bitcoin /home/bitcoin/.lightning
            11_set_lightning_network:
              command:
                !If
                  - NetworkBitcoin
                  - echo "network=bitcoin" >> /home/bitcoin/.lightning/config
                  - echo "network=testnet" >> /home/bitcoin/.lightning/config
            12_set_lightning_announce_addr:
              command: !Sub
                echo "announce-addr=${IPAddress}" >> /home/bitcoin/.lightning/config
            13_share_rpc:
              command: mkdir /etc/lightning && chown bitcoin:lightningrpc /etc/lightning


        install_lightning_charge:
          commands:
            01_allow_global_npm_packages:
              command:    su - charge --command "mkdir ~/.npm-global && npm config set prefix '~/.npm-global'"
                       && su - charge --command "echo 'export PATH=~/.npm-global/bin:$PATH' >> ~/.bashrc"
            02_clone_repo:
              command:    su - charge --command  "git clone https://github.com/Sjors/lightning-charge"
                       && su - charge --command  "cd lightning-charge && git checkout 2018/05/node-uri"
            03_npm_install:
              command:    su - charge --command "cd lightning-charge && npm link"

        install_postgres:
          commands:
            01_install_postgress:
              command: yum install -y https://download.postgresql.org/pub/repos/yum/10/redhat/rhel-7-x86_64/pgdg-redhat10-10-2.noarch.rpm
                       && sed -i "s/rhel-\$releasever-\$basearch/rhel-latest-x86_64/g" "/etc/yum.repos.d/pgdg-10-redhat.repo"
                       && yum install -y postgresql10-devel postgresql10-server
            02_configure_postgres:
              command: su - postgres --command "/usr/pgsql-10/bin/initdb -D /var/lib/pgsql/10/data"
                       && systemctl enable postgresql-10.service
            03_start_postgres:
              command: systemctl start postgresql-10.service
            04_create_db_for_matreon:
              command: su - postgres --command "createuser matreon && createdb -O matreon matreon"
            05_ruby_gem:
              command: gem install pg --no-document -- --with-pg-config=/usr/pgsql-10/bin/pg_config

        install_rails:
          packages:
            yum:
              libxml2: []
              libxml2-devel: []
              libxslt: []
              libxslt-devel: []

          commands:
            01_gems_with_native_extensions:
              command: gem install nokogiri --no-document
            10_clone_repo_again:
              command: !Sub
                mkdir -p /var/www/matreon
                && git clone ${GitURL} /var/www/matreon && cd /var/www/matreon && git checkout ${GitBranch}
                && chown -R matreon:matreon /var/www/matreon
            21_set_secret_key_base:
              command: echo "SECRET_KEY_BASE=`hexdump -n 64 -e '16/4 \"%08x\" 1 \"\n\"' /dev/random`" >> /home/matreon/.env
            22_set_devise_secret_key_key:
              command: echo "DEVISE_SECRET_KEY=`hexdump -n 64 -e '16/4 \"%08x\" 1 \"\n\"' /dev/random`" >> /home/matreon/.env

        install_nginx:
          files:
            /etc/nginx/conf.d/matreon/server_name:
              content:
                !If
                  - UseDomain
                  - !Sub |
                      server_name ${Domain};
                  - !Sub |
                      server_name _;

            /etc/nginx/conf.d/redirect_domain.conf.disabled:
              content: !Sub |
                server {
                  server_name *.amazonaws.com;
                  listen 80;
                  return 301 http://${Domain}$request_uri;
                }
          commands:
            01_install:
              command: amazon-linux-extras install nginx1.12
            02_copy_conf:
              command:    cp /usr/local/src/matreon/vendor/www/nginx.conf /etc/nginx/nginx.conf
                       && cp /usr/local/src/matreon/vendor/www/matreon.conf /etc/nginx/conf.d
                       && cp /usr/local/src/matreon/vendor/www/matreon/listen /etc/nginx/conf.d/matreon

        install_certbot:
          commands:
            01_add_EPEL7:
              command: yum install https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm -y
            02_install:
              command: yum install -y python2-certbot-nginx
            03_copy_https_upgrade_conf:
              command: cp /usr/local/src/matreon/vendor/www/https_upgrade.conf /etc/nginx/conf.d/https_upgrade.conf.disabled
            04_add_cron_daily_at_random_time:
              command: echo "`shuf -i 00-59 -n 1` `shuf -i 00-23 -n 1` * * * /usr/bin/certbot renew --quiet" >> /usr/local/src/matreon/vendor/AWS/crontab-matreon


        prepare_cron_and_services:
          commands:
            01_install_crontab:
              command:
                crontab -u matreon /usr/local/src/matreon/vendor/AWS/crontab-matreon
            10_copy_systemd_files:
              command:    cp /usr/local/src/matreon/vendor/**/*.service /lib/systemd/system
                       && cp /usr/local/src/matreon/vendor/**/*.path    /lib/systemd/system
            11_prep_bitcoind_service:
              command:    systemctl enable bitcoind.service
                       && systemctl enable bitcoind.path
                       && systemctl start bitcoind.path
            12_start_ibd_and_prune_service:
              command:    systemctl enable initial-blockchain-download.service
                       && systemctl start initial-blockchain-download.service
                       && systemctl enable ibd-shutdown.service
                       && systemctl enable ibd-shutdown.path
                       && systemctl start ibd-shutdown.path
            13_prep__lightningd_service:
              command:    systemctl enable lightningd.service
                       && systemctl enable lightningd.path
                       && systemctl start lightningd.path
            14_prep__lightning_charge_service:
              command:    systemctl enable lightning-charge.service
                       && systemctl enable lightning-charge.path
                       && systemctl start lightning-charge.path
            15_run_rails_service:
              command:     systemctl enable rails.service
                        && systemctl start rails.service
            16_run_nginx_service:
              command:    systemctl enable nginx
                       && systemctl start nginx
            17_run_redirect_domain_service:
              command:
                !If
                  - UseDomain
                  - systemctl enable redirect-domain.service && systemctl start redirect-domain.service
                  - echo
            17_run_first_certificate_service:
              command:
                !If
                  - SSL
                  - systemctl enable first-certificate.service && systemctl start first-certificate.service
                  - echo

    Properties:
      ImageId: ami-43eec3a8
      InstanceType: i3.2xlarge
      BlockDeviceMappings:
        - DeviceName: /dev/xvda
          Ebs:
            VolumeSize: '25'
            VolumeType: 'gp2'
        - DeviceName: /dev/xvdb
          Ebs:
            VolumeSize: '40'
            VolumeType: 'standard'
      SecurityGroups:
        - !Ref 'WebServerSecurityGroup'
      KeyName: !Ref 'KeyName'
      Tags:
        - Key: Name
          Value: Matreon
      UserData: !Base64
        Fn::Sub: |
          #!/bin/bash -xe
          yum update -y aws-cfn-bootstrap
          /opt/aws/bin/cfn-init -v          --stack ${AWS::StackId}         --resource WebServer          --configsets full_install          --region ${AWS::Region}
          /opt/aws/bin/cfn-signal -e $?          --stack ${AWS::StackId}         --resource WebServer          --region ${AWS::Region}
    CreationPolicy:
      ResourceSignal:
        Timeout: PT60M
  IPAddress:
    Type: AWS::EC2::EIP
  IPAssoc:
    Type: AWS::EC2::EIPAssociation
    Properties:
      InstanceId: !Ref 'WebServer'
      EIP: !Ref 'IPAddress'
  WebServerSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: SSH, HTTP, Bitcoin & Lightning P2P
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: '22'
          ToPort: '22'
          CidrIp: 0.0.0.0/0
        - IpProtocol: tcp
          FromPort: '80'
          ToPort: '80'
          CidrIp: 0.0.0.0/0
        - IpProtocol: tcp
          FromPort: '443'
          ToPort: '443'
          CidrIp: 0.0.0.0/0
        - IpProtocol: tcp
          FromPort:
            !If
              - NetworkBitcoin
              - '8883'
              - '18883'
          ToPort:
            !If
              - NetworkBitcoin
              - '8883'
              - '18883'
          CidrIp: 0.0.0.0/0
        - IpProtocol: tcp
          FromPort: '9735'
          ToPort: '9735'
          CidrIp: 0.0.0.0/0

Outputs:
  IP:
    Value: !Sub '${WebServer.PublicIp}'
    Description: IP address to use for DNS A-Record
  WebsiteURL:
    Value: !Sub 'http://${WebServer.PublicDnsName}/'
    Description: URL for your Matreon