class EY::ServersideRunner

Public Class Methods

new(options) click to toggle source
# File lib/engineyard/serverside_runner.rb, line 7
def initialize(options)
  @verbose        = options[:verbose] || !!ENV['DEBUG']
  @hostname       = options[:bridge]
  env             = options[:environment]
  @adapter        = load_adapter(@hostname, options[:app], env, @verbose, options[:serverside_version])
  @username       = env.username
  @hierarchy_name = env.hierarchy_name
  @command        = nil
end

Public Instance Methods

call(out, err) click to toggle source
# File lib/engineyard/serverside_runner.rb, line 42
def call(out, err)
  raise "No command!" unless @command
  @command.call do |cmd|
    run cmd, out, err
  end
end
deploy(&block) click to toggle source
# File lib/engineyard/serverside_runner.rb, line 17
def deploy(&block)
  @command = @adapter.deploy(&block)
  self
end
put_up_maintenance_page(&block) click to toggle source
# File lib/engineyard/serverside_runner.rb, line 32
def put_up_maintenance_page(&block)
  @command = @adapter.enable_maintenance(&block)
  self
end
restart(&block) click to toggle source
# File lib/engineyard/serverside_runner.rb, line 27
def restart(&block)
  @command = @adapter.restart(&block)
  self
end
rollback(&block) click to toggle source
# File lib/engineyard/serverside_runner.rb, line 22
def rollback(&block)
  @command = @adapter.rollback(&block)
  self
end
take_down_maintenance_page(&block) click to toggle source
# File lib/engineyard/serverside_runner.rb, line 37
def take_down_maintenance_page(&block)
  @command = @adapter.disable_maintenance(&block)
  self
end

Private Instance Methods

instances_data(instances, bridge) click to toggle source

If we tell engineyard-serverside to use 'localhost', it'll run commands on the instance directly (system). If we give it the instance's actual hostname, it'll SSH to itself.

Using 'localhost' instead of its EC2 hostname speeds up deploys on solos and single-app-server clusters significantly.

# File lib/engineyard/serverside_runner.rb, line 71
def instances_data(instances, bridge)
  instances.map do |i|
    {
      hostname: i.hostname == bridge ? 'localhost' : i.hostname,
      roles:    [i.role],
      name:     i.name,
    }
  end
end
load_adapter(bridge, app, environment, verbose, serverside_version) click to toggle source
# File lib/engineyard/serverside_runner.rb, line 51
def load_adapter(bridge, app, environment, verbose, serverside_version)
  EY::Serverside::Adapter.new("/usr/local/ey_resin/ruby/bin") do |args|
    args.serverside_version = serverside_version
    args.app              = app.name
    args.git              = app.repository_uri
    args.instances        = instances_data(environment.deploy_to_instances, bridge)
    args.stack            = environment.app_server_stack_name
    args.framework_env    = environment.framework_env
    args.environment_name = environment.name
    args.account_name     = app.account.name
    args.verbose          = verbose
  end
end
net_ssh_options() click to toggle source
# File lib/engineyard/serverside_runner.rb, line 110
def net_ssh_options
  level = :fatal # default in Net::SSH
  if debug = ENV["DEBUG"]
    level = :info
    if %w[debug info warn error fatal].include?(debug.downcase)
      level = debug.downcase.to_sym
    end
  end

  {paranoid: false, verbose: level, keepalive: true, keepalive_interval: 60}
end
run(remote_command, out, err) click to toggle source
# File lib/engineyard/serverside_runner.rb, line 81
    def run(remote_command, out, err)
      cmd = Escape.shell_command(['bash', '-lc', remote_command])

      if cmd.respond_to?(:encoding) && cmd.respond_to?(:force_encoding)
        out << "Encoding: #{cmd.encoding.name}" if @verbose
        cmd.force_encoding('binary')
        out << " => #{cmd.encoding.name}; __ENCODING__: #{__ENCODING__.name}; LANG: #{ENV['LANG']}; LC_CTYPE: #{ENV['LC_CTYPE']}\n" if @verbose
      end

      out << "Running command on #{@username}@#{@hostname}.\n"
      out << cmd << "\n" if @verbose || ENV['PRINT_CMD']

      if ENV["NO_SSH"]
        out << "NO_SSH is set. No output.\n"
        true
      else
        begin
          ssh(cmd, @hostname, @username, out, err)
        rescue Net::SSH::AuthenticationFailed
          raise EY::Error, <<-ERROR
Authentication Failed. Things to fix:
  1. Add your SSH key to your local SSH agent with `ssh-add path/to/key`.
  2. Add your SSH key to #{@hierarchy_name} on Engine Yard Cloud and apply the changes.
  (https://support.cloud.engineyard.com/entries/20996846-set-up-ssh-keys)
          ERROR
        end
      end
    end
ssh(cmd, hostname, username, out, err) click to toggle source
# File lib/engineyard/serverside_runner.rb, line 122
def ssh(cmd, hostname, username, out, err)
  exit_code = 1
  Net::SSH.start(hostname, username, net_ssh_options) do |net_ssh|
    net_ssh.open_channel do |channel|
      channel.exec cmd do |_, success|
        unless success
          err << "Remote command execution failed"
          return false
        end

        channel.on_data do |_, data|
          out << data
        end

        channel.on_extended_data do |_, _, data|
          err << data
        end

        channel.on_request("exit-status") do |_, data|
          exit_code = data.read_long
        end

        channel.on_request("exit-signal") do |_, data|
          exit_code = 255
        end

        # sending eof declares no more data coming from this end (close stdin)
        channel.eof!
      end
    end

    net_ssh.loop
  end
  exit_code.zero?
end