inf 0.0.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/bin/inf +166 -0
- data/bootstrap-scripts/default-app.sh +127 -0
- data/lib/aws.rb +131 -0
- data/lib/domains.rb +91 -0
- data/lib/env.rb +27 -0
- data/lib/fleet.rb +167 -0
- data/lib/inf.rb +73 -0
- data/lib/load_balancer.rb +53 -0
- data/lib/migrations.rb +18 -0
- metadata +123 -0
    
        checksums.yaml
    ADDED
    
    | @@ -0,0 +1,7 @@ | |
| 1 | 
            +
            ---
         | 
| 2 | 
            +
            SHA1:
         | 
| 3 | 
            +
              metadata.gz: 575f2c3299b818ecc71262fcc870d0c37c650ff1
         | 
| 4 | 
            +
              data.tar.gz: ee6999576321334813dfa5a7d50395b3e25ac64a
         | 
| 5 | 
            +
            SHA512:
         | 
| 6 | 
            +
              metadata.gz: c0d28adc8da0b26881ead0ddc7041def45d54e01ab61d51ca73ccf515ae13bb296c33a5f913866d53fa8cd7730cce22ab79a4ab9b4dfb973d91f8b47dc58a8b5
         | 
| 7 | 
            +
              data.tar.gz: 6aeaa6eaf7ec2f03fb9198ebb22fb40603416b4c934a704aa6d56f79bd7f55ca5616a2e5d3555419a9df6ee911314c3e812269c85d516e11a6afb98b8b39cec8
         | 
    
        data/bin/inf
    ADDED
    
    | @@ -0,0 +1,166 @@ | |
| 1 | 
            +
            #!/usr/bin/env ruby
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require 'thor'
         | 
| 4 | 
            +
            require 'aws-sdk'
         | 
| 5 | 
            +
            require 'time'
         | 
| 6 | 
            +
            require 'pry'
         | 
| 7 | 
            +
            require 'awesome_print'
         | 
| 8 | 
            +
            require 'base64'
         | 
| 9 | 
            +
            require 'colored'
         | 
| 10 | 
            +
            require 'dotenv'
         | 
| 11 | 
            +
             | 
| 12 | 
            +
            require_relative '../lib/inf.rb'
         | 
| 13 | 
            +
             | 
| 14 | 
            +
            Dotenv.load('inf.env')
         | 
| 15 | 
            +
             | 
| 16 | 
            +
            def require_env_vars(vars)
         | 
| 17 | 
            +
              vars.each do |v|
         | 
| 18 | 
            +
                raise ArgumentError.new("Missing env var #{v}") unless ENV[v]
         | 
| 19 | 
            +
              end
         | 
| 20 | 
            +
            end
         | 
| 21 | 
            +
             | 
| 22 | 
            +
            require_env_vars %w(
         | 
| 23 | 
            +
              STATE_BUCKET
         | 
| 24 | 
            +
              APP_NAME
         | 
| 25 | 
            +
              AWS_REGION
         | 
| 26 | 
            +
              SPOT_PRICE
         | 
| 27 | 
            +
              TARGET_CAPACITY
         | 
| 28 | 
            +
              AMI
         | 
| 29 | 
            +
              FLEET_ROLE
         | 
| 30 | 
            +
              INSTANCE_PROFILE_NAME
         | 
| 31 | 
            +
              SG
         | 
| 32 | 
            +
              ELB_SG
         | 
| 33 | 
            +
              SUBNET_IDS
         | 
| 34 | 
            +
              KEY_NAME
         | 
| 35 | 
            +
              CERT_ARN
         | 
| 36 | 
            +
              JENKINS_PUBLIC_KEY
         | 
| 37 | 
            +
            )
         | 
| 38 | 
            +
             | 
| 39 | 
            +
            ENV['TERMINATE_INSTANCES'] ||= "true"
         | 
| 40 | 
            +
            ENV['INSTANCE_TYPES'] = 'm1.small,m1.medium'
         | 
| 41 | 
            +
            ENV['DNS_TTL'] ||= "30"
         | 
| 42 | 
            +
             | 
| 43 | 
            +
            SSH_CMD='ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no'
         | 
| 44 | 
            +
             | 
| 45 | 
            +
            class CLI < Thor
         | 
| 46 | 
            +
              register Inf::Fleet, :fleet, 'fleet', 'operate on the fleet'
         | 
| 47 | 
            +
              register Inf::LoadBalancer, :lb, 'lb', 'operate on load balancer'
         | 
| 48 | 
            +
              register Inf::Env, :env, 'env', "operate on app environment"
         | 
| 49 | 
            +
              # register Inf::Domains, :domains, 'domains', 'operate on domains'
         | 
| 50 | 
            +
             | 
| 51 | 
            +
              def method_missing(m, *args, &block)
         | 
| 52 | 
            +
                Inf.send(m, *args, &block)
         | 
| 53 | 
            +
              end
         | 
| 54 | 
            +
             | 
| 55 | 
            +
              desc 'release [TARBALL_PATH]', 'create release from slug (default: ./slug.tgz)'
         | 
| 56 | 
            +
              def release(tarball_path='./slug.tgz')
         | 
| 57 | 
            +
                release_id = latest_release + 1
         | 
| 58 | 
            +
             | 
| 59 | 
            +
                tarball_path = File.expand_path tarball_path
         | 
| 60 | 
            +
             | 
| 61 | 
            +
                puts "Uploading slug to s3 (could take a moment)...".yellow
         | 
| 62 | 
            +
                put_state("apps/#{app_name}/releases/v#{release_id}.tgz", File.read(tarball_path))
         | 
| 63 | 
            +
                put_state("apps/#{app_name}/latest_release_id", release_id)
         | 
| 64 | 
            +
              end
         | 
| 65 | 
            +
             | 
| 66 | 
            +
              desc 'migrate', 'migrate state'
         | 
| 67 | 
            +
              def migrate
         | 
| 68 | 
            +
                Inf::Migrations.maybe_migrate!
         | 
| 69 | 
            +
              end
         | 
| 70 | 
            +
             | 
| 71 | 
            +
              desc 'deploy [RELEASE_ID]', 'deploy release_id (default: currently deployed release)'
         | 
| 72 | 
            +
              def deploy(release_id=nil)
         | 
| 73 | 
            +
                release_id ||= current_release || latest_release
         | 
| 74 | 
            +
             | 
| 75 | 
            +
                unless release_id
         | 
| 76 | 
            +
                  puts "No current or latest release; please specify a release"
         | 
| 77 | 
            +
                  return
         | 
| 78 | 
            +
                end
         | 
| 79 | 
            +
             | 
| 80 | 
            +
             | 
| 81 | 
            +
                # set the current release
         | 
| 82 | 
            +
                # app servers will pull this and use
         | 
| 83 | 
            +
                # it to deploy
         | 
| 84 | 
            +
                put_state("apps/#{app_name}/current_release_id", release_id)
         | 
| 85 | 
            +
             | 
| 86 | 
            +
                # loop over app servers and tell them
         | 
| 87 | 
            +
                # they should deploy
         | 
| 88 | 
            +
                Inf::Fleet.each_fleet do |name|
         | 
| 89 | 
            +
                  puts "Deploying release v#{release_id} to fleet #{name}...".blue
         | 
| 90 | 
            +
             | 
| 91 | 
            +
                  Inf::Fleet.ips(name).each do |ip|
         | 
| 92 | 
            +
                    puts "Deploying to #{ip}...".gray
         | 
| 93 | 
            +
                    `#{SSH_CMD} -t ubuntu@#{ip} fork-and-deploy-app`
         | 
| 94 | 
            +
                    $?.success? or exit 1
         | 
| 95 | 
            +
                  end
         | 
| 96 | 
            +
                end
         | 
| 97 | 
            +
              end
         | 
| 98 | 
            +
             | 
| 99 | 
            +
              desc 'deploy_latest', 'deploy newest release to app'
         | 
| 100 | 
            +
              def deploy_latest
         | 
| 101 | 
            +
                deploy latest_release
         | 
| 102 | 
            +
              end
         | 
| 103 | 
            +
             | 
| 104 | 
            +
              desc 'latest_release', 'show version number of latest release'
         | 
| 105 | 
            +
              def latest_release
         | 
| 106 | 
            +
                get_state("apps/#{app_name}/latest_release_id").tap do |id|
         | 
| 107 | 
            +
                  puts "The latest release is v#{id} (it may not be deployed at the moment)".blue
         | 
| 108 | 
            +
                end.to_i
         | 
| 109 | 
            +
              end
         | 
| 110 | 
            +
             | 
| 111 | 
            +
              desc 'current_release', 'show version number of current release'
         | 
| 112 | 
            +
              def current_release
         | 
| 113 | 
            +
                get_state("apps/#{app_name}/current_release_id").tap do |id|
         | 
| 114 | 
            +
                  puts "The current release is v#{id}".blue
         | 
| 115 | 
            +
                end.to_i
         | 
| 116 | 
            +
              end
         | 
| 117 | 
            +
             | 
| 118 | 
            +
              desc 'releases', 'list releases'
         | 
| 119 | 
            +
              def releases
         | 
| 120 | 
            +
                list_state "apps/#{app_name}/releases" do |key|
         | 
| 121 | 
            +
                  ap key
         | 
| 122 | 
            +
                end
         | 
| 123 | 
            +
              end
         | 
| 124 | 
            +
             | 
| 125 | 
            +
              desc 'restart', 'restart servers'
         | 
| 126 | 
            +
              def restart
         | 
| 127 | 
            +
                Inf::Fleet.each_fleet do |name|
         | 
| 128 | 
            +
                  puts "Restarting fleet #{name}...".blue
         | 
| 129 | 
            +
             | 
| 130 | 
            +
                  Inf::Fleet.ips(name).each do |ip|
         | 
| 131 | 
            +
                    puts "Restarting #{ip}...".gray
         | 
| 132 | 
            +
                    `#{SSH_CMD} -t ubuntu@#{ip} fork-and-restart-app`
         | 
| 133 | 
            +
                    $?.success? or exit 1
         | 
| 134 | 
            +
                  end
         | 
| 135 | 
            +
                end
         | 
| 136 | 
            +
              end
         | 
| 137 | 
            +
             | 
| 138 | 
            +
              desc 'clear_state', 'resets the s3 state'
         | 
| 139 | 
            +
              def clear_state
         | 
| 140 | 
            +
                delete_spec = s3.list_objects_v2(
         | 
| 141 | 
            +
                  bucket: state_bucket
         | 
| 142 | 
            +
                ).contents.map { |object| {key: object.key} }
         | 
| 143 | 
            +
             | 
| 144 | 
            +
                if delete_spec.empty?
         | 
| 145 | 
            +
                  puts "Had no state".yellow
         | 
| 146 | 
            +
                else
         | 
| 147 | 
            +
                  s3.delete_objects(
         | 
| 148 | 
            +
                    bucket: state_bucket,
         | 
| 149 | 
            +
                    delete: {
         | 
| 150 | 
            +
                      objects: delete_spec
         | 
| 151 | 
            +
                    }
         | 
| 152 | 
            +
                  )
         | 
| 153 | 
            +
             | 
| 154 | 
            +
                  puts "State cleared".blue
         | 
| 155 | 
            +
                end
         | 
| 156 | 
            +
              end
         | 
| 157 | 
            +
             | 
| 158 | 
            +
              desc 'open [BROWSER]', 'opens app in browser'
         | 
| 159 | 
            +
              def open(browser='google-chrome')
         | 
| 160 | 
            +
                url = get_state("apps/#{app_name}/lb-dns")
         | 
| 161 | 
            +
                puts "Opening #{url} in #{browser}".blue
         | 
| 162 | 
            +
                `#{browser} #{url}`
         | 
| 163 | 
            +
              end
         | 
| 164 | 
            +
            end
         | 
| 165 | 
            +
             | 
| 166 | 
            +
            CLI.start(ARGV)
         | 
| @@ -0,0 +1,127 @@ | |
| 1 | 
            +
            #!/bin/bash
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            . ./bootstrap-env
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            INSTANCE_ID=$(curl http://169.254.169.254/latest/meta-data/instance-id)
         | 
| 6 | 
            +
            LB_NAME=$(aws s3 cp s3://$STATE_BUCKET/apps/$APP_NAME/lb-name -)
         | 
| 7 | 
            +
             | 
| 8 | 
            +
            cat <<EOF | logger -t bootstrap
         | 
| 9 | 
            +
              beginning instance bootstrap for $INSTANCE_ID
         | 
| 10 | 
            +
                app: $APP_NAME
         | 
| 11 | 
            +
                state_bucket: $STATE_BUCKET
         | 
| 12 | 
            +
                lb_name: $LB_NAME
         | 
| 13 | 
            +
                fleet_name: $FLEET_NAME
         | 
| 14 | 
            +
            EOF
         | 
| 15 | 
            +
             | 
| 16 | 
            +
            if [ $FLEET_NAME == "web" ]; then
         | 
| 17 | 
            +
              echo "Registering with load balancer $LB_NAME" | logger -t bootstrap
         | 
| 18 | 
            +
             | 
| 19 | 
            +
              aws elb register-instances-with-load-balancer \
         | 
| 20 | 
            +
                --load-balancer-name $LB_NAME \
         | 
| 21 | 
            +
                --instances $INSTANCE_ID \
         | 
| 22 | 
            +
                --region $AWS_REGION
         | 
| 23 | 
            +
            fi
         | 
| 24 | 
            +
             | 
| 25 | 
            +
            mkdir -p /var/app
         | 
| 26 | 
            +
             | 
| 27 | 
            +
            cat > /var/app/env <<EOF
         | 
| 28 | 
            +
            APP_NAME=$APP_NAME
         | 
| 29 | 
            +
            STATE_BUCKET=$STATE_BUCKET
         | 
| 30 | 
            +
            FLEET_NAME=$FLEET_NAME
         | 
| 31 | 
            +
            EOF
         | 
| 32 | 
            +
             | 
| 33 | 
            +
            echo "Wrote /var/app/env" | logger -t bootstrap
         | 
| 34 | 
            +
             | 
| 35 | 
            +
            chown -R ubuntu:ubuntu /var/app
         | 
| 36 | 
            +
             | 
| 37 | 
            +
            mkdir -p /home/ubuntu/.ssh
         | 
| 38 | 
            +
            echo "$JENKINS_PUBLIC_KEY" >> /home/ubuntu/.ssh/authorized_keys
         | 
| 39 | 
            +
            chown -R ubuntu:ubuntu /home/ubuntu/.ssh
         | 
| 40 | 
            +
             | 
| 41 | 
            +
            echo "Wrote jenkins public key" | logger -t bootstrap
         | 
| 42 | 
            +
             | 
| 43 | 
            +
            cat > /usr/local/bin/deploy-app <<EOF
         | 
| 44 | 
            +
            #!/bin/bash
         | 
| 45 | 
            +
             | 
| 46 | 
            +
            cd /var/app
         | 
| 47 | 
            +
            . ./env
         | 
| 48 | 
            +
             | 
| 49 | 
            +
            DEPLOYED_VERSION=`cat /var/app/release_id`
         | 
| 50 | 
            +
            VERSION=\$1
         | 
| 51 | 
            +
             | 
| 52 | 
            +
            if [ -z \$VERSION ]; then
         | 
| 53 | 
            +
              VERSION=\$(aws s3 cp s3://\$STATE_BUCKET/apps/\$APP_NAME/current_release_id -)
         | 
| 54 | 
            +
            fi
         | 
| 55 | 
            +
             | 
| 56 | 
            +
            if [ "\$VERSION" == "\$DEPLOYED_VERSION" ]; then
         | 
| 57 | 
            +
              echo "The desired version (\$VERSION) is currently deployed; exiting"
         | 
| 58 | 
            +
              exit 0
         | 
| 59 | 
            +
            elif [ -z \$DEPLOYED_VERSION ]; then
         | 
| 60 | 
            +
              echo "Bootstrapping instance with version \$VERSION"
         | 
| 61 | 
            +
            else
         | 
| 62 | 
            +
              echo "Had version \$DEPLOYED_VERSION; deploying \$VERSION"
         | 
| 63 | 
            +
            fi
         | 
| 64 | 
            +
             | 
| 65 | 
            +
            aws s3 cp s3://\$STATE_BUCKET/apps/\$APP_NAME/releases/v\$VERSION.tgz /tmp/slug.tgz
         | 
| 66 | 
            +
             | 
| 67 | 
            +
            rm -rf staging || true
         | 
| 68 | 
            +
            mkdir staging
         | 
| 69 | 
            +
            cd staging
         | 
| 70 | 
            +
            tar -xzf /tmp/slug.tgz
         | 
| 71 | 
            +
             | 
| 72 | 
            +
            . bin/pre-deploy
         | 
| 73 | 
            +
             | 
| 74 | 
            +
            cd /var/app
         | 
| 75 | 
            +
             | 
| 76 | 
            +
            rm -rf live
         | 
| 77 | 
            +
            mv staging live
         | 
| 78 | 
            +
             | 
| 79 | 
            +
            cd live
         | 
| 80 | 
            +
             | 
| 81 | 
            +
            . bin/restart
         | 
| 82 | 
            +
             | 
| 83 | 
            +
            echo \$VERSION > /var/app/release_id
         | 
| 84 | 
            +
            EOF
         | 
| 85 | 
            +
             | 
| 86 | 
            +
            cat > /usr/local/bin/restart-app <<EOF
         | 
| 87 | 
            +
            #!/bin/bash
         | 
| 88 | 
            +
             | 
| 89 | 
            +
            cd /var/app
         | 
| 90 | 
            +
            . ./env
         | 
| 91 | 
            +
             | 
| 92 | 
            +
            cd live
         | 
| 93 | 
            +
             | 
| 94 | 
            +
            . bin/restart
         | 
| 95 | 
            +
            EOF
         | 
| 96 | 
            +
             | 
| 97 | 
            +
             | 
| 98 | 
            +
            cat > /usr/local/bin/fork-and-deploy-app <<EOF
         | 
| 99 | 
            +
            #!/bin/bash -l
         | 
| 100 | 
            +
             | 
| 101 | 
            +
            ( setsid deploy-app 2>&1 | setsid logger -t deploy ) & disown
         | 
| 102 | 
            +
             | 
| 103 | 
            +
            sleep 1
         | 
| 104 | 
            +
            echo "Forked... bye"
         | 
| 105 | 
            +
            EOF
         | 
| 106 | 
            +
             | 
| 107 | 
            +
            cat > /usr/local/bin/fork-and-restart-app <<EOF
         | 
| 108 | 
            +
            #!/bin/bash -l
         | 
| 109 | 
            +
             | 
| 110 | 
            +
            ( setsid restart-app 2>&1 | setsid logger -t deploy ) & disown
         | 
| 111 | 
            +
             | 
| 112 | 
            +
            sleep 1
         | 
| 113 | 
            +
            echo "Forked... bye"
         | 
| 114 | 
            +
            EOF
         | 
| 115 | 
            +
             | 
| 116 | 
            +
             | 
| 117 | 
            +
            chmod +x /usr/local/bin/deploy-app
         | 
| 118 | 
            +
            chmod +x /usr/local/bin/fork-and-deploy-app
         | 
| 119 | 
            +
            chmod +x /usr/local/bin/restart-app
         | 
| 120 | 
            +
            chmod +x /usr/local/bin/fork-and-restart-app
         | 
| 121 | 
            +
             | 
| 122 | 
            +
            echo "Wrote deploy scripts" | logger -t bootstrap
         | 
| 123 | 
            +
             | 
| 124 | 
            +
            echo "Running initial deploy as user 'ubuntu'..."
         | 
| 125 | 
            +
            nohup sudo -u ubuntu fork-and-deploy-app & disown
         | 
| 126 | 
            +
             | 
| 127 | 
            +
            echo "That's it for bootstrap!"
         | 
    
        data/lib/aws.rb
    ADDED
    
    | @@ -0,0 +1,131 @@ | |
| 1 | 
            +
            module Inf::AWS
         | 
| 2 | 
            +
              def self.method_missing(m, *args, &block)
         | 
| 3 | 
            +
                Inf.send(m, *args, &block)
         | 
| 4 | 
            +
              end
         | 
| 5 | 
            +
             | 
| 6 | 
            +
              def self.ec2
         | 
| 7 | 
            +
                @ec2 ||= Aws::EC2::Client.new
         | 
| 8 | 
            +
              end
         | 
| 9 | 
            +
             | 
| 10 | 
            +
              def self.elb
         | 
| 11 | 
            +
                @elb ||= Aws::ElasticLoadBalancing::Client.new
         | 
| 12 | 
            +
              end
         | 
| 13 | 
            +
             | 
| 14 | 
            +
              def self.s3
         | 
| 15 | 
            +
                @s3 ||= Aws::S3::Client.new
         | 
| 16 | 
            +
              end
         | 
| 17 | 
            +
             | 
| 18 | 
            +
              def self.route53
         | 
| 19 | 
            +
                @route53 ||= Aws::Route53::Client.new
         | 
| 20 | 
            +
              end
         | 
| 21 | 
            +
             | 
| 22 | 
            +
              def self.bootstrap_script(app_name, state_bucket, fleet_name)
         | 
| 23 | 
            +
                <<BOOTSTRAP_SCRIPT
         | 
| 24 | 
            +
            #!/bin/bash
         | 
| 25 | 
            +
             | 
| 26 | 
            +
            cd /tmp
         | 
| 27 | 
            +
             | 
| 28 | 
            +
            cat > bootstrap-env <<EOF
         | 
| 29 | 
            +
            export APP_NAME=#{app_name}
         | 
| 30 | 
            +
            export STATE_BUCKET=#{state_bucket}
         | 
| 31 | 
            +
            export FLEET_NAME=#{fleet_name}
         | 
| 32 | 
            +
            export AWS_REGION=#{ENV['AWS_REGION']}
         | 
| 33 | 
            +
            export JENKINS_PUBLIC_KEY="#{ENV['JENKINS_PUBLIC_KEY']}"
         | 
| 34 | 
            +
            EOF
         | 
| 35 | 
            +
             | 
| 36 | 
            +
            . ./bootstrap-env
         | 
| 37 | 
            +
             | 
| 38 | 
            +
            aws s3 cp s3://$STATE_BUCKET/apps/$APP_NAME/bootstrap-instance.sh ./bootstrap.sh
         | 
| 39 | 
            +
            chmod +x bootstrap.sh
         | 
| 40 | 
            +
             | 
| 41 | 
            +
            ( setsid ./bootstrap.sh 2>&1 | setsid logger -t bootstrap ) & disown
         | 
| 42 | 
            +
             | 
| 43 | 
            +
            BOOTSTRAP_SCRIPT
         | 
| 44 | 
            +
              end
         | 
| 45 | 
            +
             | 
| 46 | 
            +
              def self.launch_load_balancer
         | 
| 47 | 
            +
                lb_name = "app-lb-#{app_name}"
         | 
| 48 | 
            +
                lb_dns = elb.create_load_balancer(
         | 
| 49 | 
            +
                  load_balancer_name: lb_name,
         | 
| 50 | 
            +
                  subnets: ENV['SUBNET_IDS'].split(','),
         | 
| 51 | 
            +
                  security_groups: [ENV['ELB_SG']],
         | 
| 52 | 
            +
                  scheme: "internet-facing",
         | 
| 53 | 
            +
                  listeners: [{
         | 
| 54 | 
            +
                    instance_port: 5000,
         | 
| 55 | 
            +
                    instance_protocol: 'HTTP',
         | 
| 56 | 
            +
                    load_balancer_port: 80,
         | 
| 57 | 
            +
                    protocol: 'HTTP'
         | 
| 58 | 
            +
                  }, {
         | 
| 59 | 
            +
                    instance_port: 5000,
         | 
| 60 | 
            +
                    instance_protocol: "HTTP",
         | 
| 61 | 
            +
                    load_balancer_port: 443,
         | 
| 62 | 
            +
                    protocol: "HTTPS",
         | 
| 63 | 
            +
                    ssl_certificate_id: ENV['CERT_ARN'],
         | 
| 64 | 
            +
                  }]
         | 
| 65 | 
            +
                ).dns_name
         | 
| 66 | 
            +
             | 
| 67 | 
            +
                elb.configure_health_check(
         | 
| 68 | 
            +
                  load_balancer_name: lb_name,
         | 
| 69 | 
            +
                  health_check: {
         | 
| 70 | 
            +
                    target: 'HTTP:80/',
         | 
| 71 | 
            +
                    interval: 10, # seconds
         | 
| 72 | 
            +
                    timeout: 5, # seconds to respond
         | 
| 73 | 
            +
                    unhealthy_threshold: 2, # two consecutive failures
         | 
| 74 | 
            +
                    healthy_threshold: 2
         | 
| 75 | 
            +
                  }
         | 
| 76 | 
            +
                )
         | 
| 77 | 
            +
             | 
| 78 | 
            +
                put_state("apps/#{app_name}/lb-name", lb_name)
         | 
| 79 | 
            +
                put_state("apps/#{app_name}/lb-dns", lb_dns)
         | 
| 80 | 
            +
             | 
| 81 | 
            +
                puts "Launched load balancer (or it was running)".blue
         | 
| 82 | 
            +
              end
         | 
| 83 | 
            +
             | 
| 84 | 
            +
              def self.request_spot_fleet(fleet_name='default')
         | 
| 85 | 
            +
                launch_specifications = ENV['SUBNET_IDS'].split(',').map do |subnet_id|
         | 
| 86 | 
            +
                  ENV['INSTANCE_TYPES'].split(',').map do |type|
         | 
| 87 | 
            +
                    {
         | 
| 88 | 
            +
                      image_id: ENV['AMI'],
         | 
| 89 | 
            +
                      key_name: ENV['KEY_NAME'],
         | 
| 90 | 
            +
                      instance_type: type,
         | 
| 91 | 
            +
                      monitoring: {
         | 
| 92 | 
            +
                        enabled: false,
         | 
| 93 | 
            +
                      },
         | 
| 94 | 
            +
                      network_interfaces: [{
         | 
| 95 | 
            +
                        associate_public_ip_address: true,
         | 
| 96 | 
            +
                        device_index: 0,
         | 
| 97 | 
            +
                        groups: [ENV['SG']],
         | 
| 98 | 
            +
                        subnet_id: subnet_id
         | 
| 99 | 
            +
                      }],
         | 
| 100 | 
            +
                      iam_instance_profile: {
         | 
| 101 | 
            +
                        name: ENV['INSTANCE_PROFILE_NAME'],
         | 
| 102 | 
            +
                      },
         | 
| 103 | 
            +
                      # ebs_optimized: type == 'm1.small' ? false : false,
         | 
| 104 | 
            +
                      weighted_capacity: 1.0,
         | 
| 105 | 
            +
                      user_data: Base64.encode64(bootstrap_script(app_name, state_bucket, fleet_name))
         | 
| 106 | 
            +
                    }
         | 
| 107 | 
            +
                  end
         | 
| 108 | 
            +
                end.flatten
         | 
| 109 | 
            +
             | 
| 110 | 
            +
                resp = ec2.request_spot_fleet(
         | 
| 111 | 
            +
                  spot_fleet_request_config: {
         | 
| 112 | 
            +
                    spot_price: ENV['SPOT_PRICE'],
         | 
| 113 | 
            +
                    target_capacity: ENV['TARGET_CAPACITY'],
         | 
| 114 | 
            +
                    valid_from: Time.now,
         | 
| 115 | 
            +
                    valid_until: Time.parse('1/1/2050'),
         | 
| 116 | 
            +
                    terminate_instances_with_expiration: ENV['TERMINATE_INSTANCES'],
         | 
| 117 | 
            +
                    iam_fleet_role: ENV['FLEET_ROLE'],
         | 
| 118 | 
            +
                    launch_specifications: launch_specifications,
         | 
| 119 | 
            +
                    excess_capacity_termination_policy: "default", # accepts noTermination, default
         | 
| 120 | 
            +
                    allocation_strategy: "diversified", # accepts lowestPrice, diversified
         | 
| 121 | 
            +
                    type: "maintain", # accepts request, maintain
         | 
| 122 | 
            +
                    replace_unhealthy_instances: true,
         | 
| 123 | 
            +
                  },
         | 
| 124 | 
            +
                )
         | 
| 125 | 
            +
             | 
| 126 | 
            +
                resp.spot_fleet_request_id.tap do |sfr|
         | 
| 127 | 
            +
                  puts "Launched spot fleet #{fleet_name} (#{sfr})".blue
         | 
| 128 | 
            +
                  put_state("apps/#{app_name}/fleets/#{fleet_name}/sfr", sfr)
         | 
| 129 | 
            +
                end
         | 
| 130 | 
            +
              end
         | 
| 131 | 
            +
            end
         | 
    
        data/lib/domains.rb
    ADDED
    
    | @@ -0,0 +1,91 @@ | |
| 1 | 
            +
            class Inf::Domains < Thor
         | 
| 2 | 
            +
              def method_missing(m, *args, &block)
         | 
| 3 | 
            +
                Inf.send(m, *args, &block)
         | 
| 4 | 
            +
              end
         | 
| 5 | 
            +
             | 
| 6 | 
            +
              def self.method_missing(m, *args, &block)
         | 
| 7 | 
            +
                Inf.send(m, *args, &block)
         | 
| 8 | 
            +
              end
         | 
| 9 | 
            +
             | 
| 10 | 
            +
              def self.list
         | 
| 11 | 
            +
                get_state("apps/#{app_name}/domains").lines rescue []
         | 
| 12 | 
            +
              end
         | 
| 13 | 
            +
             | 
| 14 | 
            +
              def self.hosted_zones
         | 
| 15 | 
            +
                @hosted_zones ||= route53.list_hosted_zones_by_name.hosted_zones.reduce
         | 
| 16 | 
            +
                  binding.pry
         | 
| 17 | 
            +
                end
         | 
| 18 | 
            +
              end
         | 
| 19 | 
            +
             | 
| 20 | 
            +
              def self.sync_to_route53
         | 
| 21 | 
            +
                lb_dns = get_state("apps/#{app_name}/lb-dns")
         | 
| 22 | 
            +
             | 
| 23 | 
            +
                hosted_zones
         | 
| 24 | 
            +
             | 
| 25 | 
            +
                list.each do |domain|
         | 
| 26 | 
            +
                  route53.change_resource_record_sets({
         | 
| 27 | 
            +
                    change_batch: {
         | 
| 28 | 
            +
                      changes: [
         | 
| 29 | 
            +
                        {
         | 
| 30 | 
            +
                          action: "UPSERT",
         | 
| 31 | 
            +
                          resource_record_set: {
         | 
| 32 | 
            +
                            name: domain,
         | 
| 33 | 
            +
                            resource_records: [
         | 
| 34 | 
            +
                              {
         | 
| 35 | 
            +
                                value: lb_dns,
         | 
| 36 | 
            +
                              },
         | 
| 37 | 
            +
                            ],
         | 
| 38 | 
            +
                            ttl: ENV['DNS_TTL'],
         | 
| 39 | 
            +
                            type: "CNAME",
         | 
| 40 | 
            +
                          },
         | 
| 41 | 
            +
                        },
         | 
| 42 | 
            +
                      ],
         | 
| 43 | 
            +
                      comment: "load balancer for #{app_name}",
         | 
| 44 | 
            +
                    },
         | 
| 45 | 
            +
                    hosted_zone_id: zone_id,
         | 
| 46 | 
            +
                  })
         | 
| 47 | 
            +
                end
         | 
| 48 | 
            +
              end
         | 
| 49 | 
            +
             | 
| 50 | 
            +
              desc 'domains', 'list domains'
         | 
| 51 | 
            +
              def list
         | 
| 52 | 
            +
                Inf::Domains.list.each do |domain|
         | 
| 53 | 
            +
                  puts domain
         | 
| 54 | 
            +
                end
         | 
| 55 | 
            +
              end
         | 
| 56 | 
            +
             | 
| 57 | 
            +
              desc 'sync', 'sync to route53'
         | 
| 58 | 
            +
              def sync
         | 
| 59 | 
            +
                Inf::Domains.sync_to_route53
         | 
| 60 | 
            +
              end
         | 
| 61 | 
            +
             | 
| 62 | 
            +
              desc 'add DOMAIN', 'cname DOMAIN to the load balancer'
         | 
| 63 | 
            +
              def add(domain)
         | 
| 64 | 
            +
                domains = Inf::Domains.list
         | 
| 65 | 
            +
             | 
| 66 | 
            +
                if domains.include? domain
         | 
| 67 | 
            +
                  puts "Domain #{domain} is already configured".blue
         | 
| 68 | 
            +
                  return
         | 
| 69 | 
            +
                end
         | 
| 70 | 
            +
             | 
| 71 | 
            +
                domains += [domain]
         | 
| 72 | 
            +
                put_state("apps/#{app_name}/domains", domains.join("\n"))
         | 
| 73 | 
            +
             | 
| 74 | 
            +
                Inf::Domains.sync_to_route53
         | 
| 75 | 
            +
              end
         | 
| 76 | 
            +
             | 
| 77 | 
            +
              desc 'remove DOMAIN', 'un-cname DOMAIN'
         | 
| 78 | 
            +
              def remove(domain)
         | 
| 79 | 
            +
                domains = Inf::Domains.list
         | 
| 80 | 
            +
             | 
| 81 | 
            +
                unless domains.include? domain
         | 
| 82 | 
            +
                  puts "Domain #{domain} is not configured".red
         | 
| 83 | 
            +
                  return
         | 
| 84 | 
            +
                end
         | 
| 85 | 
            +
             | 
| 86 | 
            +
                domains = domains.reject { |d| d == domain }
         | 
| 87 | 
            +
                put_state("apps/#{app_name}/domains", domains.join("\n"))
         | 
| 88 | 
            +
             | 
| 89 | 
            +
                Inf::Domains.sync_to_route53
         | 
| 90 | 
            +
              end
         | 
| 91 | 
            +
            end
         | 
    
        data/lib/env.rb
    ADDED
    
    | @@ -0,0 +1,27 @@ | |
| 1 | 
            +
            class Inf::Env < Thor
         | 
| 2 | 
            +
              def method_missing(m, *args, &block)
         | 
| 3 | 
            +
                Inf.send(m, *args, &block)
         | 
| 4 | 
            +
              end
         | 
| 5 | 
            +
             | 
| 6 | 
            +
              def self.method_missing(m, *args, &block)
         | 
| 7 | 
            +
                Inf.send(m, *args, &block)
         | 
| 8 | 
            +
              end
         | 
| 9 | 
            +
             | 
| 10 | 
            +
              desc 'push [FILE]', 'sets the .env file for the app (default: ./.env; pass "-" for stdin)'
         | 
| 11 | 
            +
              def push(file='.env')
         | 
| 12 | 
            +
                file = File.expand_path(file)
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                if file == '-'
         | 
| 15 | 
            +
                  env = STDIN.read
         | 
| 16 | 
            +
                else
         | 
| 17 | 
            +
                  env = File.read(file)
         | 
| 18 | 
            +
                end
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                put_state("apps/#{app_name}/env", env)
         | 
| 21 | 
            +
              end
         | 
| 22 | 
            +
             | 
| 23 | 
            +
              desc 'pull', 'gets env vars'
         | 
| 24 | 
            +
              def pull
         | 
| 25 | 
            +
                puts get_state("apps/#{app_name}/env")
         | 
| 26 | 
            +
              end
         | 
| 27 | 
            +
            end
         | 
    
        data/lib/fleet.rb
    ADDED
    
    | @@ -0,0 +1,167 @@ | |
| 1 | 
            +
            class Inf::Fleet < Thor
         | 
| 2 | 
            +
              def method_missing(m, *args, &block)
         | 
| 3 | 
            +
                Inf.send(m, *args, &block)
         | 
| 4 | 
            +
              end
         | 
| 5 | 
            +
             | 
| 6 | 
            +
              def self.method_missing(m, *args, &block)
         | 
| 7 | 
            +
                Inf.send(m, *args, &block)
         | 
| 8 | 
            +
              end
         | 
| 9 | 
            +
             | 
| 10 | 
            +
              no_commands do
         | 
| 11 | 
            +
                def get_sfr(fleet_name='web')
         | 
| 12 | 
            +
                  @sfrs ||= {}
         | 
| 13 | 
            +
                  @sfrs[fleet_name] ||= get_state("apps/#{app_name}/fleets/#{fleet_name}/sfr")
         | 
| 14 | 
            +
                end
         | 
| 15 | 
            +
              end
         | 
| 16 | 
            +
             | 
| 17 | 
            +
              def self.each_fleet(&block)
         | 
| 18 | 
            +
                list_state("apps/#{app_name}/fleets") do |key|
         | 
| 19 | 
            +
                  key.match(/fleets\/([^\/]+)/)[1]
         | 
| 20 | 
            +
                end.uniq.each { |name| block.call name }
         | 
| 21 | 
            +
              end
         | 
| 22 | 
            +
             | 
| 23 | 
            +
              def self.ips(fleet_name='web')
         | 
| 24 | 
            +
                sfr = new.get_sfr fleet_name
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                unless sfr
         | 
| 27 | 
            +
                  puts "No sfr for #{app_name}/#{fleet_name}".red
         | 
| 28 | 
            +
                  puts "Is a fleet launched?"
         | 
| 29 | 
            +
                  exit 1
         | 
| 30 | 
            +
                end
         | 
| 31 | 
            +
             | 
| 32 | 
            +
                instance_ids = ec2.describe_spot_fleet_instances(
         | 
| 33 | 
            +
                  spot_fleet_request_id: sfr
         | 
| 34 | 
            +
                ).active_instances.map(&:instance_id)
         | 
| 35 | 
            +
             | 
| 36 | 
            +
                if instance_ids.empty?
         | 
| 37 | 
            +
                  []
         | 
| 38 | 
            +
                else
         | 
| 39 | 
            +
                  ec2.describe_instances(
         | 
| 40 | 
            +
                    instance_ids: instance_ids
         | 
| 41 | 
            +
                  ).reservations.map do |it|
         | 
| 42 | 
            +
                    it.instances.first.public_ip_address
         | 
| 43 | 
            +
                  end
         | 
| 44 | 
            +
                end
         | 
| 45 | 
            +
              end
         | 
| 46 | 
            +
             | 
| 47 | 
            +
              desc 'launch', 'launch app fleet; check env vars in code'
         | 
| 48 | 
            +
              def launch(fleet_name='web')
         | 
| 49 | 
            +
                sfr = get_sfr fleet_name
         | 
| 50 | 
            +
             | 
| 51 | 
            +
                if sfr
         | 
| 52 | 
            +
                  puts "Kill existing sfr first".red
         | 
| 53 | 
            +
                  exit 1
         | 
| 54 | 
            +
                end
         | 
| 55 | 
            +
             | 
| 56 | 
            +
                Inf::AWS.request_spot_fleet fleet_name
         | 
| 57 | 
            +
             | 
| 58 | 
            +
                script = File.expand_path "../../bootstrap-scripts/default-app.sh", __FILE__
         | 
| 59 | 
            +
             | 
| 60 | 
            +
                put_state "apps/#{app_name}/bootstrap-instance.sh", File.read(script)
         | 
| 61 | 
            +
                puts "Don't forget to deploy something".yellow
         | 
| 62 | 
            +
              end
         | 
| 63 | 
            +
             | 
| 64 | 
            +
              desc 'kill', 'kill fleet'
         | 
| 65 | 
            +
              def kill(fleet_name='web')
         | 
| 66 | 
            +
                sfr = get_sfr fleet_name
         | 
| 67 | 
            +
             | 
| 68 | 
            +
                if sfr
         | 
| 69 | 
            +
                  ec2.cancel_spot_fleet_requests(
         | 
| 70 | 
            +
                    spot_fleet_request_ids: [sfr],
         | 
| 71 | 
            +
                    terminate_instances: true
         | 
| 72 | 
            +
                  )
         | 
| 73 | 
            +
                  puts "Killed #{sfr}".blue
         | 
| 74 | 
            +
                else
         | 
| 75 | 
            +
                  puts "Did not find a sfr".yellow
         | 
| 76 | 
            +
                end
         | 
| 77 | 
            +
             | 
| 78 | 
            +
                delete_state "apps/#{app_name}/fleets/#{fleet_name}/sfr"
         | 
| 79 | 
            +
              end
         | 
| 80 | 
            +
             | 
| 81 | 
            +
              desc 'rename [NAME] [NEW_NAME]', 'rename fleet'
         | 
| 82 | 
            +
              def rename(name, new_name)
         | 
| 83 | 
            +
                sfr = get_sfr new_name
         | 
| 84 | 
            +
             | 
| 85 | 
            +
                if sfr
         | 
| 86 | 
            +
                  puts "Fleet #{new_name} already exists".red
         | 
| 87 | 
            +
                  exit 1
         | 
| 88 | 
            +
                end
         | 
| 89 | 
            +
             | 
| 90 | 
            +
                list_state("apps/#{app_name}/fleets/#{name}") do |key|
         | 
| 91 | 
            +
                  leaf = key.split('/').last
         | 
| 92 | 
            +
             | 
| 93 | 
            +
                  s3.copy_object(
         | 
| 94 | 
            +
                    bucket: state_bucket,
         | 
| 95 | 
            +
                    key: "apps/#{app_name}/fleets/#{new_name}/#{leaf}",
         | 
| 96 | 
            +
                    copy_source: "#{state_bucket}/#{key}"
         | 
| 97 | 
            +
                  )
         | 
| 98 | 
            +
             | 
| 99 | 
            +
                  delete_state key
         | 
| 100 | 
            +
                end
         | 
| 101 | 
            +
              end
         | 
| 102 | 
            +
             | 
| 103 | 
            +
              desc 'nodes', 'list public ips of nodes in fleet'
         | 
| 104 | 
            +
              def nodes(fleet_name='web')
         | 
| 105 | 
            +
                ips = Inf::Fleet.ips fleet_name
         | 
| 106 | 
            +
             | 
| 107 | 
            +
                if ips.any?
         | 
| 108 | 
            +
                  puts ips.join("\n")
         | 
| 109 | 
            +
                else
         | 
| 110 | 
            +
                  puts "No active instances in fleet"
         | 
| 111 | 
            +
                end
         | 
| 112 | 
            +
              end
         | 
| 113 | 
            +
             | 
| 114 | 
            +
              # TODO make tmux optional
         | 
| 115 | 
            +
              desc 'ssh', 'ssh to a node in the fleet'
         | 
| 116 | 
            +
              def ssh(fleet_name='web')
         | 
| 117 | 
            +
                ips = Inf::Fleet.ips fleet_name
         | 
| 118 | 
            +
             | 
| 119 | 
            +
                if ips.empty?
         | 
| 120 | 
            +
                  puts "No instances in fleet #{fleet_name}".red
         | 
| 121 | 
            +
                else
         | 
| 122 | 
            +
                  ip = ips.first
         | 
| 123 | 
            +
             | 
| 124 | 
            +
                  puts "Opening ssh to #{ip} in a tmux split".blue
         | 
| 125 | 
            +
                  `tmux split-window "#{SSH_CMD} ubuntu@#{ips.first}"`
         | 
| 126 | 
            +
                end
         | 
| 127 | 
            +
              end
         | 
| 128 | 
            +
             | 
| 129 | 
            +
              desc 'scale FLEET_NAME COUNT', 'scales the fleet'
         | 
| 130 | 
            +
              def scale(fleet_name, count)
         | 
| 131 | 
            +
                sfr = get_sfr fleet_name
         | 
| 132 | 
            +
             | 
| 133 | 
            +
                ec2.modify_spot_fleet_request({
         | 
| 134 | 
            +
                  spot_fleet_request_id: sfr,
         | 
| 135 | 
            +
                  target_capacity: count.to_i,
         | 
| 136 | 
            +
                  excess_capacity_termination_policy: "default"
         | 
| 137 | 
            +
                })
         | 
| 138 | 
            +
              end
         | 
| 139 | 
            +
             | 
| 140 | 
            +
              desc 'status', 'describes the fleet'
         | 
| 141 | 
            +
              def status(fleet_name='web', indent='')
         | 
| 142 | 
            +
                nodes = Inf::Fleet.ips fleet_name
         | 
| 143 | 
            +
                sfr = get_state("apps/#{app_name}/fleets/#{fleet_name}/sfr")
         | 
| 144 | 
            +
             | 
| 145 | 
            +
                sfr_status = ec2.describe_spot_fleet_requests(
         | 
| 146 | 
            +
                  spot_fleet_request_ids: [sfr]
         | 
| 147 | 
            +
                ).spot_fleet_request_configs[0].spot_fleet_request_state
         | 
| 148 | 
            +
             | 
| 149 | 
            +
                if nodes.any?
         | 
| 150 | 
            +
                  puts "Fleet #{fleet_name} has #{nodes.count} nodes (#{nodes.join ', '})".blue
         | 
| 151 | 
            +
                elsif sfr
         | 
| 152 | 
            +
                  puts "Fleet #{fleet_name} (#{sfr}: #{sfr_status}) has no nodes!".red
         | 
| 153 | 
            +
                else
         | 
| 154 | 
            +
                  puts "No fleet #{fleet_name} is launched".yelow
         | 
| 155 | 
            +
                end
         | 
| 156 | 
            +
              end
         | 
| 157 | 
            +
             | 
| 158 | 
            +
              desc 'list', 'lists fleets'
         | 
| 159 | 
            +
              def list
         | 
| 160 | 
            +
                Inf::Fleet.each_fleet { |name| puts name }
         | 
| 161 | 
            +
              end
         | 
| 162 | 
            +
             | 
| 163 | 
            +
              desc 'status_all', 'runs `status` for all fleets'
         | 
| 164 | 
            +
              def status_all
         | 
| 165 | 
            +
                Inf::Fleet.each_fleet { |name| status name }
         | 
| 166 | 
            +
              end
         | 
| 167 | 
            +
            end
         | 
    
        data/lib/inf.rb
    ADDED
    
    | @@ -0,0 +1,73 @@ | |
| 1 | 
            +
            require 'mini_cache'
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module Inf
         | 
| 4 | 
            +
              def self.cache
         | 
| 5 | 
            +
                @cache ||= MiniCache::Store.new
         | 
| 6 | 
            +
              end
         | 
| 7 | 
            +
             | 
| 8 | 
            +
              def self.state_bucket
         | 
| 9 | 
            +
                ENV['STATE_BUCKET'].downcase
         | 
| 10 | 
            +
              end
         | 
| 11 | 
            +
             | 
| 12 | 
            +
              def self.app_name
         | 
| 13 | 
            +
                ENV['APP_NAME'].downcase
         | 
| 14 | 
            +
              end
         | 
| 15 | 
            +
             | 
| 16 | 
            +
              def self.put_state(k, v)
         | 
| 17 | 
            +
                v = v.to_s
         | 
| 18 | 
            +
                s3.put_object(
         | 
| 19 | 
            +
                  bucket: state_bucket,
         | 
| 20 | 
            +
                  key: k,
         | 
| 21 | 
            +
                  body: v
         | 
| 22 | 
            +
                )
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                summary = v.lines.count > 1 ? '[multiline]' : v
         | 
| 25 | 
            +
                cache.unset k
         | 
| 26 | 
            +
                puts "#{k} -> #{summary}".gray
         | 
| 27 | 
            +
              end
         | 
| 28 | 
            +
             | 
| 29 | 
            +
              def self.list_state(prefix)
         | 
| 30 | 
            +
                s3.list_objects_v2(
         | 
| 31 | 
            +
                  bucket: state_bucket,
         | 
| 32 | 
            +
                  prefix: prefix,
         | 
| 33 | 
            +
                ).contents.map do |it|
         | 
| 34 | 
            +
                  yield it.key
         | 
| 35 | 
            +
                end
         | 
| 36 | 
            +
              end
         | 
| 37 | 
            +
             | 
| 38 | 
            +
              def self.get_state(k, opts={})
         | 
| 39 | 
            +
                cache.get_or_set(k) do
         | 
| 40 | 
            +
                  begin
         | 
| 41 | 
            +
                    object = s3.get_object(bucket: state_bucket, key: k)
         | 
| 42 | 
            +
             | 
| 43 | 
            +
                    if opts[:raw]
         | 
| 44 | 
            +
                      object
         | 
| 45 | 
            +
                    else
         | 
| 46 | 
            +
                      object.body.read
         | 
| 47 | 
            +
                    end
         | 
| 48 | 
            +
                  rescue Aws::S3::Errors::NoSuchKey
         | 
| 49 | 
            +
                  end
         | 
| 50 | 
            +
                end
         | 
| 51 | 
            +
              end
         | 
| 52 | 
            +
             | 
| 53 | 
            +
              def self.delete_state(k)
         | 
| 54 | 
            +
                s3.delete_object(bucket: state_bucket, key: k) rescue nil
         | 
| 55 | 
            +
              end
         | 
| 56 | 
            +
             | 
| 57 | 
            +
              class << self
         | 
| 58 | 
            +
                %i(s3 ec2 elb route53).each do |m|
         | 
| 59 | 
            +
                  define_method m do
         | 
| 60 | 
            +
                    AWS.send m
         | 
| 61 | 
            +
                  end
         | 
| 62 | 
            +
                end
         | 
| 63 | 
            +
              end
         | 
| 64 | 
            +
            end
         | 
| 65 | 
            +
             | 
| 66 | 
            +
            require 'thor'
         | 
| 67 | 
            +
             | 
| 68 | 
            +
            require_relative 'migrations.rb'
         | 
| 69 | 
            +
            require_relative 'aws.rb'
         | 
| 70 | 
            +
            require_relative 'fleet.rb'
         | 
| 71 | 
            +
            require_relative 'env.rb'
         | 
| 72 | 
            +
            require_relative 'load_balancer.rb'
         | 
| 73 | 
            +
            # require_relative 'domains.rb'
         | 
| @@ -0,0 +1,53 @@ | |
| 1 | 
            +
            class Inf::LoadBalancer < Thor
         | 
| 2 | 
            +
              def method_missing(m, *args, &block)
         | 
| 3 | 
            +
                Inf.send(m, *args, &block)
         | 
| 4 | 
            +
              end
         | 
| 5 | 
            +
             | 
| 6 | 
            +
              def self.method_missing(m, *args, &block)
         | 
| 7 | 
            +
                Inf.send(m, *args, &block)
         | 
| 8 | 
            +
              end
         | 
| 9 | 
            +
             | 
| 10 | 
            +
              desc 'launch', 'launch load balancer for app'
         | 
| 11 | 
            +
              def launch
         | 
| 12 | 
            +
                Inf::AWS.launch_load_balancer
         | 
| 13 | 
            +
              end
         | 
| 14 | 
            +
             | 
| 15 | 
            +
              desc 'kill', 'kill lb for app'
         | 
| 16 | 
            +
              def kill
         | 
| 17 | 
            +
                lb_name  = get_state "apps/#{app_name}/lb-name"
         | 
| 18 | 
            +
                delete_state "apps/#{app_name}/lb-name"
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                if lb_name
         | 
| 21 | 
            +
                  elb.delete_load_balancer(
         | 
| 22 | 
            +
                    load_balancer_name: lb_name
         | 
| 23 | 
            +
                  )
         | 
| 24 | 
            +
             | 
| 25 | 
            +
                  delete_state "apps/#{app_name}/lb-dns"
         | 
| 26 | 
            +
                  puts "Deleted lb #{lb_name}".blue
         | 
| 27 | 
            +
                else
         | 
| 28 | 
            +
                  puts "Did not find an lb".yellow
         | 
| 29 | 
            +
                end
         | 
| 30 | 
            +
              end
         | 
| 31 | 
            +
             | 
| 32 | 
            +
              desc 'status', 'lb status'
         | 
| 33 | 
            +
              def status
         | 
| 34 | 
            +
                lb_name  = get_state "apps/#{app_name}/lb-name"
         | 
| 35 | 
            +
             | 
| 36 | 
            +
                unless lb_name
         | 
| 37 | 
            +
                  puts "Load balancer is not configured".blue
         | 
| 38 | 
            +
                  return
         | 
| 39 | 
            +
                end
         | 
| 40 | 
            +
             | 
| 41 | 
            +
                lb = elb.
         | 
| 42 | 
            +
                  describe_load_balancers(load_balancer_names: [lb_name]).
         | 
| 43 | 
            +
                  load_balancer_descriptions[0]
         | 
| 44 | 
            +
             | 
| 45 | 
            +
                unless lb
         | 
| 46 | 
            +
                  puts "Load balancer expected but not found!".red
         | 
| 47 | 
            +
                  return
         | 
| 48 | 
            +
                end
         | 
| 49 | 
            +
             | 
| 50 | 
            +
                puts "Load balancer is at #{lb.dns_name} with ".blue +
         | 
| 51 | 
            +
                  "#{lb.instances.count} instances".blue
         | 
| 52 | 
            +
              end
         | 
| 53 | 
            +
            end
         | 
    
        data/lib/migrations.rb
    ADDED
    
    | @@ -0,0 +1,18 @@ | |
| 1 | 
            +
            module Inf::Migrations
         | 
| 2 | 
            +
              def self.method_missing(m, *args, &block)
         | 
| 3 | 
            +
                Inf.send(m, *args, &block)
         | 
| 4 | 
            +
              end
         | 
| 5 | 
            +
             | 
| 6 | 
            +
              def self.maybe_migrate!
         | 
| 7 | 
            +
                if single_sfr = get_state("apps/#{app_name}/sfr")
         | 
| 8 | 
            +
                  migrate_to_multi_fleets single_sfr
         | 
| 9 | 
            +
                end
         | 
| 10 | 
            +
              end
         | 
| 11 | 
            +
             | 
| 12 | 
            +
              # before: apps/:name/sfr is the only fleet
         | 
| 13 | 
            +
              # after: apps/:name/fleets/web/sfr
         | 
| 14 | 
            +
              def self.migrate_to_multi_fleets(sfr)
         | 
| 15 | 
            +
                Inf.put_state "apps/#{app_name}/fleets/web/sfr", sfr
         | 
| 16 | 
            +
                Inf.delete_state "apps/#{app_name}/sfr"
         | 
| 17 | 
            +
              end
         | 
| 18 | 
            +
            end
         | 
    
        metadata
    ADDED
    
    | @@ -0,0 +1,123 @@ | |
| 1 | 
            +
            --- !ruby/object:Gem::Specification
         | 
| 2 | 
            +
            name: inf
         | 
| 3 | 
            +
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            +
              version: 0.0.9
         | 
| 5 | 
            +
            platform: ruby
         | 
| 6 | 
            +
            authors:
         | 
| 7 | 
            +
            - Kyle Brett <kyle@kylebrett.com>
         | 
| 8 | 
            +
            autorequire: 
         | 
| 9 | 
            +
            bindir: bin
         | 
| 10 | 
            +
            cert_chain: []
         | 
| 11 | 
            +
            date: 2017-05-03 00:00:00.000000000 Z
         | 
| 12 | 
            +
            dependencies:
         | 
| 13 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 14 | 
            +
              name: thor
         | 
| 15 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 16 | 
            +
                requirements:
         | 
| 17 | 
            +
                - - "~>"
         | 
| 18 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 19 | 
            +
                    version: '0.19'
         | 
| 20 | 
            +
              type: :runtime
         | 
| 21 | 
            +
              prerelease: false
         | 
| 22 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 23 | 
            +
                requirements:
         | 
| 24 | 
            +
                - - "~>"
         | 
| 25 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 26 | 
            +
                    version: '0.19'
         | 
| 27 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 28 | 
            +
              name: aws-sdk
         | 
| 29 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 30 | 
            +
                requirements:
         | 
| 31 | 
            +
                - - "~>"
         | 
| 32 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 33 | 
            +
                    version: '2.9'
         | 
| 34 | 
            +
              type: :runtime
         | 
| 35 | 
            +
              prerelease: false
         | 
| 36 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 37 | 
            +
                requirements:
         | 
| 38 | 
            +
                - - "~>"
         | 
| 39 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 40 | 
            +
                    version: '2.9'
         | 
| 41 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 42 | 
            +
              name: colored
         | 
| 43 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 44 | 
            +
                requirements:
         | 
| 45 | 
            +
                - - ">="
         | 
| 46 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 47 | 
            +
                    version: '1'
         | 
| 48 | 
            +
              type: :runtime
         | 
| 49 | 
            +
              prerelease: false
         | 
| 50 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 51 | 
            +
                requirements:
         | 
| 52 | 
            +
                - - ">="
         | 
| 53 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 54 | 
            +
                    version: '1'
         | 
| 55 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 56 | 
            +
              name: dotenv
         | 
| 57 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 58 | 
            +
                requirements:
         | 
| 59 | 
            +
                - - ">="
         | 
| 60 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 61 | 
            +
                    version: '2'
         | 
| 62 | 
            +
              type: :runtime
         | 
| 63 | 
            +
              prerelease: false
         | 
| 64 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 65 | 
            +
                requirements:
         | 
| 66 | 
            +
                - - ">="
         | 
| 67 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 68 | 
            +
                    version: '2'
         | 
| 69 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 70 | 
            +
              name: mini_cache
         | 
| 71 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 72 | 
            +
                requirements:
         | 
| 73 | 
            +
                - - ">="
         | 
| 74 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 75 | 
            +
                    version: '1'
         | 
| 76 | 
            +
              type: :runtime
         | 
| 77 | 
            +
              prerelease: false
         | 
| 78 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 79 | 
            +
                requirements:
         | 
| 80 | 
            +
                - - ">="
         | 
| 81 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 82 | 
            +
                    version: '1'
         | 
| 83 | 
            +
            description: 
         | 
| 84 | 
            +
            email: kyle@kylebrett.com
         | 
| 85 | 
            +
            executables:
         | 
| 86 | 
            +
            - inf
         | 
| 87 | 
            +
            extensions: []
         | 
| 88 | 
            +
            extra_rdoc_files: []
         | 
| 89 | 
            +
            files:
         | 
| 90 | 
            +
            - bin/inf
         | 
| 91 | 
            +
            - bootstrap-scripts/default-app.sh
         | 
| 92 | 
            +
            - lib/aws.rb
         | 
| 93 | 
            +
            - lib/domains.rb
         | 
| 94 | 
            +
            - lib/env.rb
         | 
| 95 | 
            +
            - lib/fleet.rb
         | 
| 96 | 
            +
            - lib/inf.rb
         | 
| 97 | 
            +
            - lib/load_balancer.rb
         | 
| 98 | 
            +
            - lib/migrations.rb
         | 
| 99 | 
            +
            homepage: http://www.kylebrett.com
         | 
| 100 | 
            +
            licenses:
         | 
| 101 | 
            +
            - Nonstandard
         | 
| 102 | 
            +
            metadata: {}
         | 
| 103 | 
            +
            post_install_message: 
         | 
| 104 | 
            +
            rdoc_options: []
         | 
| 105 | 
            +
            require_paths:
         | 
| 106 | 
            +
            - lib
         | 
| 107 | 
            +
            required_ruby_version: !ruby/object:Gem::Requirement
         | 
| 108 | 
            +
              requirements:
         | 
| 109 | 
            +
              - - ">="
         | 
| 110 | 
            +
                - !ruby/object:Gem::Version
         | 
| 111 | 
            +
                  version: '0'
         | 
| 112 | 
            +
            required_rubygems_version: !ruby/object:Gem::Requirement
         | 
| 113 | 
            +
              requirements:
         | 
| 114 | 
            +
              - - ">="
         | 
| 115 | 
            +
                - !ruby/object:Gem::Version
         | 
| 116 | 
            +
                  version: '0'
         | 
| 117 | 
            +
            requirements: []
         | 
| 118 | 
            +
            rubyforge_project: 
         | 
| 119 | 
            +
            rubygems_version: 2.6.8
         | 
| 120 | 
            +
            signing_key: 
         | 
| 121 | 
            +
            specification_version: 4
         | 
| 122 | 
            +
            summary: inf
         | 
| 123 | 
            +
            test_files: []
         |