class Shipment::Server::SSHClient

Attributes

db_name[R]
db_password[R]
db_user[R]
gh_token[R]
gh_username[R]
ip_address[R]
repo[R]
repo_name[R]
repo_original_name[R]
repo_url[R]
repo_user[R]
secret_key_base[R]

Public Class Methods

deploy(repo:, ip_address:) click to toggle source
# File lib/shipment/server/ssh_client.rb, line 18
def self.deploy(repo:, ip_address:)
  new(repo, ip_address).deploy
end
new(repo, ip_address) click to toggle source
# File lib/shipment/server/ssh_client.rb, line 26
def initialize(repo, ip_address)
  @gh_username, @gh_token = Netrc.read["shipment.gh"]
  @ip_address = ip_address
  @repo_url, @repo_name, @repo_user, @repo_original_name = repo.url, repo.name, repo.user, repo.original_name
  db_info = YAML.load(File.read('.shipment'))[:database]
  @secret_key_base = YAML.load(File.read('.shipment'))[:secret]
  @db_user, @db_password, @db_name = db_info[:username], db_info[:password], db_info[:name]
end
setup(repo:, ip_address:) click to toggle source
# File lib/shipment/server/ssh_client.rb, line 14
def self.setup(repo:, ip_address:)
  new(repo, ip_address).setup
end
tail_logs(repo:, ip_address:) click to toggle source
# File lib/shipment/server/ssh_client.rb, line 22
def self.tail_logs(repo:, ip_address:)
  new(repo, ip_address).tail_logs
end

Public Instance Methods

add_deploy_key() click to toggle source
# File lib/shipment/server/ssh_client.rb, line 150
def add_deploy_key
  puts "-----> ".green + "Adding deploy key to GitHub..."
  run_remote_command("docker run --name deploy -e ACCESS_TOKEN=#{gh_token} -e REPO=#{repo_user}/#{repo_original_name} #{repo_user}/#{repo_name} /bin/bash -c 'source /etc/profile.d/rvm.sh && wget https://gist.githubusercontent.com/loganhasson/e4791b4abe2dc75bc82f/raw/342cd7c32cec4e58947c0eb1785f45abecf714c4/add_deploy_key_shipment.rb && ruby add_deploy_key_shipment.rb && rm add_deploy_key_shipment.rb' && docker commit deploy #{repo_user}/#{repo_name} && docker rm deploy")
end
add_to_known_hosts() click to toggle source
# File lib/shipment/server/ssh_client.rb, line 82
      def add_to_known_hosts
        puts "-----> ".green + "Giving server time to prepare for SSH connections (this will take about 1 minute)..."
        sleep 60
        puts "-----> ".green + "Adding droplet to known hosts..."
        FileUtils.touch('known_host.sh')
        FileUtils.chmod('+x', 'known_host.sh')
        File.open('known_host.sh', 'w+') do |f|
          f.write <<-SCRIPT.gsub(/^ {10}/, '')
          #!/bin/bash

          /usr/bin/expect <<EOD
          spawn ssh root@#{ip_address}
          expect -re "(continue)"
          send "yes\\n"
          send "exit\\n"
          expect eof
          EOD
          SCRIPT
        end
        `./known_host.sh && rm known_host.sh`
      end
clone_and_bundle() click to toggle source
# File lib/shipment/server/ssh_client.rb, line 155
def clone_and_bundle
  puts "-----> ".green + "Cloning project into application container..."
  run_remote_command("docker run --name bundle -e REPO_URL=#{repo_url} -e REPO_NAME=#{repo_name} #{repo_user}/#{repo_name} /bin/bash -c 'source /etc/profile.d/rvm.sh && wget https://gist.githubusercontent.com/loganhasson/5db07d7b5671a9bc5fef/raw/4a28de693b2631c69a888fada82d6b462e2fe09e/clone_with_expect_shipment.sh && chmod +x clone_with_expect_shipment.sh && ./clone_with_expect_shipment.sh && rm clone_with_expect_shipment.sh && cd #{repo_name} && bundle install && cd ..' && docker commit bundle #{repo_user}/#{repo_name} && docker rm bundle")
end
deploy() click to toggle source
# File lib/shipment/server/ssh_client.rb, line 42
def deploy
  puts "-----> ".green + "Deploying..."
  kill_and_commit_old_server
  start_new_server
  puts "-----> ".green + "Done.\nYour application is accessable at: #{ip_address}"
end
generate_ssh_key() click to toggle source
# File lib/shipment/server/ssh_client.rb, line 145
def generate_ssh_key
  puts "-----> ".green + "Generating deploy key..."
  run_remote_command("docker run --name setup loganhasson/ruby_image /bin/bash -c 'ssh-keygen -t rsa -N \"\" -C \"#{repo_name}@shipment\" -f \"/root/.ssh/id_rsa\"' && docker commit setup #{repo_user}/#{repo_name} && docker rm setup && docker rmi loganhasson/ruby_image")
end
kill_and_commit_old_server() click to toggle source
# File lib/shipment/server/ssh_client.rb, line 58
def kill_and_commit_old_server
  puts "-----> ".green + "Stopping existing server..."
  run_remote_command("if sudo docker ps -a | grep application > /dev/null; then docker kill application && docker commit application #{repo_user}/#{repo_name} && docker rm application; fi")
end
migrate() click to toggle source
# File lib/shipment/server/ssh_client.rb, line 160
def migrate
  puts "-----> ".green + "Migrating database..."
  run_remote_command("docker run --name migrate #{repo_user}/#{repo_name} /bin/bash -c 'source /etc/profile.d/rvm.sh && mkdir -p /var/lib/docker/volumes/log && touch /var/lib/docker/volumes/log/production.log && cd #{repo_name} && RAILS_ENV=production bundle exec rake db:migrate && cd ..' && docker commit migrate #{repo_user}/#{repo_name} && docker rm migrate")
end
parse_redis_and_sidekiq() click to toggle source
# File lib/shipment/server/ssh_client.rb, line 69
def parse_redis_and_sidekiq
  # Code smell...
  commands = []
  if YAML.load(File.read('.shipment'))[:sidekiq]
    commands << ' && redis-server /etc/redis/redis.conf'
    commands << ' && bundle exec sidekiq -d -L log/sidekiq.log'
  elsif YAML.load(File.read('.shipment'))[:redis]
    commands << ' && redis-server /etc/redis/redis.conf'
  end

  return commands
end
pull_postgres_image() click to toggle source
# File lib/shipment/server/ssh_client.rb, line 111
def pull_postgres_image
  puts "-----> ".green + "Pulling Postgres image (this may take a few minutes)..."
  run_remote_command("docker pull loganhasson/postgres_image")
end
pull_ruby_image() click to toggle source
# File lib/shipment/server/ssh_client.rb, line 140
def pull_ruby_image
  puts "-----> ".green + "Pulling Ruby image (this may take a few minutes)..."
  run_remote_command("docker pull loganhasson/ruby_image")
end
run_remote_command(command, silent=false) click to toggle source

run_remote_command(<<-SETUP /usr/bin/expect <<EOD spawn docker login expect “Username:” send “#{docker_username}n” expect “Password:” send “#{docker_password}n” expect “Email:” send “#{docker_email}n” expect eof EOD SETUP ) run_remote_command(“docker push #{repo_user}/#{repo_name} && docker push #{repo_user}/#{repo_name}_db”) end

# File lib/shipment/server/ssh_client.rb, line 190
def run_remote_command(command, silent=false)
  puts "-----> ".blue + "#{command}"

  begin
    Net::SSH.start(ip_address, 'root', timeout: 500) do |ssh|
      ssh.open_channel do |channel|
        channel.exec "#{command}" do |ch, success|
          raise "problem executing command: #{command}".red unless success

          ch.on_data do |c, data|
            if !silent
              if !data.empty? && !(data == " ") && !(data == "\n") && !data.match(/ojbects|deltas/) && !(data == ".")
                $stdout.puts "       #{data.strip.chomp}"
              end
            end
          end

          ch.on_extended_data do |c, type, data|
            if !data.empty? && !(data == " ") && !(data == "\n") && !(data == ".")
              $stderr.puts "       #{data.strip.chomp}".red

              if data.strip.chomp.match(/Error pulling image/) || data.strip.chomp.match(/connection timed out/)
                $stderr.puts "-----> ".red + "CONNECTION TO DOCKER TIMED OUT: ".red + "Trying again..."
                run_remote_command(command, silent)
              end
            end
          end

          #ch.on_close { puts "-----> ".green + "Done." }
        end
      end
    end
  rescue Errno::ETIMEDOUT
    $stderr.puts "-----> ".red + "CONNECTION TO SERVER TIMED OUT: ".red + "Reconnecting..."
    run_remote_command(command, silent)
  end
end
setup() click to toggle source
# File lib/shipment/server/ssh_client.rb, line 35
def setup
  add_to_known_hosts
  puts "-----> ".green + "Preparing server..."
  setup_database_container
  setup_application_container
end
setup_application_container() click to toggle source
# File lib/shipment/server/ssh_client.rb, line 126
def setup_application_container
  puts "-----> ".green + "Setting up application container..."
  pull_ruby_image
  generate_ssh_key
  add_deploy_key
  clone_and_bundle
  migrate
  #save_docker_image

  puts "-----> ".green + "Done.\nReady to deploy."
  puts "Your server's IP Address is: #{ip_address}"
  puts "Deploy with: ship out"
end
setup_database_container() click to toggle source
# File lib/shipment/server/ssh_client.rb, line 104
def setup_database_container
  puts "-----> ".green + "Setting up database container..."
  pull_postgres_image
  setup_database_user
  start_database_container
end
setup_database_user() click to toggle source
# File lib/shipment/server/ssh_client.rb, line 116
def setup_database_user
  puts "-----> ".green + "Setting up database user..."
  run_remote_command("docker run --name database -e USER=#{db_user} -e PASSWORD=#{db_password} -e NAME=#{db_name} loganhasson/postgres_image /bin/bash -c 'wget https://gist.githubusercontent.com/loganhasson/d8f8a91875087407ea6a/raw/21bdbd6134e470397cef1ab0feeefdecee3fb6f0/database_setup_shipment.sql && service postgresql start && psql --set \"user=$USER\" --set \"password=$PASSWORD\" --file=database_setup_shipment.sql && createdb -O $USER $NAME && service postgresql stop && rm database_setup_shipment.sql && wget https://gist.githubusercontent.com/loganhasson/680f4b02c0f295dd7961/raw/18af1b0ccb2bb485dfc51e2fde169fce91393bca/update_postgres_config_shipment.sh && chmod +x update_postgres_config_shipment.sh && ./update_postgres_config_shipment.sh && rm update_postgres_config_shipment.sh' && docker commit database #{repo_user}/#{repo_name}_db && docker rm database && docker rmi loganhasson/postgres_image")
end
start_database_container() click to toggle source
# File lib/shipment/server/ssh_client.rb, line 121
def start_database_container
  puts "-----> ".green + "Starting postgres server..."
  run_remote_command("docker run --name database -d -p 5432:5432 #{repo_user}/#{repo_name}_db su postgres -c '/usr/lib/postgresql/9.3/bin/postgres -D /var/lib/postgresql/9.3/main -c config_file=/etc/postgresql/9.3/main/postgresql.conf'")
end
start_new_server() click to toggle source
# File lib/shipment/server/ssh_client.rb, line 63
def start_new_server
  puts "-----> ".green + "Restarting server..."
  redis_command, sidekiq_command = parse_redis_and_sidekiq
  run_remote_command("docker run -d -p 80:3000 --name application -e SECRET_KEY_BASE=#{secret_key_base} -v /root/log:/var/lib/docker/volumes/log #{repo_user}/#{repo_name} /bin/bash -c 'kill -9 $(pgrep -f sidekiq) > /dev/null 2>&1 && kill -9 $(pgrep -f redis-server) > /dev/null 2>&1 && kill -9 $(pgrep -f rails) > /dev/null 2>&1 && source /etc/profile.d/rvm.sh && rm -rf #{repo_name} && git clone #{repo_url} #{repo_name} && cd #{repo_name} && bundle install && RAILS_ENV=production bundle exec rake db:migrate#{redis_command}#{sidekiq_command} && rails server -p 3000 -e production'")
end
tail_logs() click to toggle source
# File lib/shipment/server/ssh_client.rb, line 49
def tail_logs
  puts "-----> ".green + "Accessing application logs..."
  begin
    run_remote_command("tail -f log/production.log")
  rescue Interrupt
    puts "\nDone."
  end
end