class Slugforge::Host
Attributes
pattern[R]
server[R]
slug_name[R]
status[R]
Public Class Methods
new(pattern, server=nil)
click to toggle source
# File lib/slugforge/models/host.rb, line 8 def initialize(pattern, server=nil) @pattern = pattern @server = server @deploy_results = [] @timeline = [] @start_time = Time.now @actions = [] self end
Public Instance Methods
add_action(action)
click to toggle source
# File lib/slugforge/models/host.rb, line 76 def add_action(action) @actions << action end
complete?()
click to toggle source
# File lib/slugforge/models/host.rb, line 64 def complete? success? || failed? end
deploy(slug_name, logger, opts)
click to toggle source
# File lib/slugforge/models/host.rb, line 109 def deploy(slug_name, logger, opts) begin record_event :started if opts[:pretend] logger.log "not actually #{effective_action} slug (#{name})", {:color => :yellow, :status => :pretend} else logger.say_status :deploy, "#{effective_action} to host #{name} as #{username(opts)}", :green Net::SSH.start(ssh_host, username(opts), ssh_opts(opts)) do |ssh| host_slug = detect_slug(ssh, slug_name, logger) unless opts[:force] host_slug ||= copy_slug(ssh, slug_name, logger, opts) explode_slug(ssh, host_slug, logger, opts) if stage? install_slug(ssh, host_slug, logger, opts) if install? end end record_event :deployed rescue => e record_event :failed message = "#{e.class.to_s}: #{e.to_s}" logger.log "#{message} (#{ip}: #{name})", {:color => :red, :status => :fail} @deploy_results << {:output => message} end logger.say_status :deploy, "#{effective_action} complete for host: #{name}", success? ? :green : :red @deploy_results end
effective_action()
click to toggle source
# File lib/slugforge/models/host.rb, line 96 def effective_action return "installing" if @actions.include?(:install) "staging" end
elapsed_time()
click to toggle source
# File lib/slugforge/models/host.rb, line 51 def elapsed_time Time.at(Time.now - @start_time).strftime('%M:%S') end
failed?()
click to toggle source
# File lib/slugforge/models/host.rb, line 72 def failed? @status == :failed? end
has_action?(action)
click to toggle source
# File lib/slugforge/models/host.rb, line 80 def has_action?(action) @actions.include?(action) end
id()
click to toggle source
# File lib/slugforge/models/host.rb, line 30 def id nil end
install?()
click to toggle source
# File lib/slugforge/models/host.rb, line 92 def install? @actions.include?(:install) end
ip()
click to toggle source
# File lib/slugforge/models/host.rb, line 22 def ip @pattern end
is_autoscaled?()
click to toggle source
# File lib/slugforge/models/host.rb, line 34 def is_autoscaled? false end
name()
click to toggle source
# File lib/slugforge/models/host.rb, line 18 def name "name:#{@pattern}" end
output()
click to toggle source
# File lib/slugforge/models/host.rb, line 105 def output @deploy_results.map { |result| result[:output] unless result[:exit_code] == 0 }.compact end
record_event(status)
click to toggle source
# File lib/slugforge/models/host.rb, line 55 def record_event(status) @status = status @timeline << {:status => status, :elapsed_time => elapsed_time} end
remove_action(action)
click to toggle source
# File lib/slugforge/models/host.rb, line 84 def remove_action(action) @actions.delete(action) end
ssh_host()
click to toggle source
# File lib/slugforge/models/host.rb, line 26 def ssh_host @pattern end
stage?()
click to toggle source
# File lib/slugforge/models/host.rb, line 88 def stage? @actions.include?(:stage) end
success?()
click to toggle source
# File lib/slugforge/models/host.rb, line 68 def success? [:deployed, :terminated].include?(@status) && output.empty? end
terminated?()
click to toggle source
# File lib/slugforge/models/host.rb, line 101 def terminated? @status == :terminated? end
timeline()
click to toggle source
# File lib/slugforge/models/host.rb, line 60 def timeline @timeline.map { |event| "#{event[:status]} @ #{event[:elapsed_time]}" }.join ', ' end
to_status()
click to toggle source
# File lib/slugforge/models/host.rb, line 38 def to_status { :name => name, :ip => ip, :pattern => @pattern, :slug_name => @slug_name, :status => @status.to_s, :output => @deploy_results, :start_time => @start_time, :timeline => timeline, } end
Private Instance Methods
copy_slug(ssh, slug_name, logger, opts)
click to toggle source
# File lib/slugforge/models/host.rb, line 155 def copy_slug(ssh, slug_name, logger, opts) slug_name_with_path = "/mnt/#{slug_name}" case opts[:copy_type] when :ssh logger.log "interactive mode enabled (be sure to set slug_name)", {:color => :yellow, :status => :copy, :log_level => :verbose} binding.pry when :scp logger.log "copying slug to host via SCP (#{name})", {:color => :green, :status => :copy, :log_level => :verbose} scp_upload ip, username(opts), opts[:filename], "#{slug_name}", logger, :ssh => ssh_opts logger.log "moving slug from ~ to /mnt as root", {:color => :green, :status => :copy, :log_level => :verbose} @deploy_results << ssh_command(ssh, "sudo mv #{slug_name} #{slug_name_with_path}", logger) else # AWS S3 command line by default logger.log "copying slug to host via aws s3 command (#{name})", {:color => :green, :status => :copy, :log_level => :verbose} @deploy_results << ssh_command(ssh, "sudo -- sh -c 'export AWS_ACCESS_KEY_ID=#{opts[:aws_session][:aws_access_key_id]}; export AWS_SECRET_ACCESS_KEY=#{opts[:aws_session][:aws_secret_access_key]}; export AWS_SECURITY_TOKEN=#{opts[:aws_session][:aws_session_token]}; export AWS_DEFAULT_REGION=#{opts[:aws_session][:aws_region]}; aws s3 cp #{opts[:s3_url]} #{slug_name_with_path}'; echo \"SSH_COMMAND_EXIT_CODE=$?\"", logger) end record_event :copied slug_name_with_path end
detect_slug(ssh, slug_name, logger)
click to toggle source
# File lib/slugforge/models/host.rb, line 144 def detect_slug(ssh, slug_name, logger) found_path = ['/tmp', '/mnt'].select do |path| file_count(ssh, path, slug_name) > 0 end.compact return nil if found_path.empty? slug_name_with_path = "#{found_path.first}/#{slug_name}" logger.log "found existing slug (#{slug_name_with_path}) on host (#{name}); use --force to overwrite slug", {:color => :yellow, :status => :detect, :log_level => :vervose} record_event :detected slug_name_with_path end
explode_slug(ssh, slug_name_with_path, logger, opts)
click to toggle source
# File lib/slugforge/models/host.rb, line 178 def explode_slug(ssh, slug_name_with_path, logger, opts) logger.log "exploding package as root #{"for user " + opts[:owner] if opts[:owner]}", {:color => :green, :status => :stage, :log_level => :verbose} @deploy_results << ssh_command(ssh, slug_install_command(slug_name_with_path, opts[:deploy_dir], {:unpack => true, :owner => opts[:owner], :env => opts[:env]}), logger) @slug_name = slug_name end
file_count(ssh, path, file)
click to toggle source
# File lib/slugforge/models/host.rb, line 191 def file_count(ssh, path, file) ssh.exec!("find #{path} -maxdepth 1 -name '#{file}' -type f -size +0 | wc -l").to_i end
install_slug(ssh, slug_name_with_path, logger, opts)
click to toggle source
# File lib/slugforge/models/host.rb, line 184 def install_slug(ssh, slug_name_with_path, logger, opts) logger.log "installing package as root #{"for user " + opts[:owner] if opts[:owner]}", {:color => :green, :status => :install, :log_level => :verbose} @deploy_results << ssh_command(ssh, slug_install_command(slug_name_with_path, opts[:deploy_dir], {:owner => opts[:owner], :env => opts[:env], :force => opts[:force]}), logger) @slug_name = slug_name record_event :installed end
scp_upload(host, user, source, dest, logger, opts)
click to toggle source
# File lib/slugforge/models/host.rb, line 195 def scp_upload(host, user, source, dest, logger, opts) logger.log "SCP: #{source} to #{host}:#{dest}" Net::SCP.upload!(host, user, source, dest, opts) do | ch, name, sent, total | logger.log "\r#{name}: #{(sent * 100.0 / total).to_i}% " end logger.log end
slug_install_command(slug_name_with_path, deploy_dir, opts = {})
click to toggle source
# File lib/slugforge/models/host.rb, line 217 def slug_install_command(slug_name_with_path, deploy_dir, opts = {}) [ opts[:prefix], "TERM=dumb sudo bash -l -c 'date >> /var/log/slug_deploy.log ; ", "chmod +x #{slug_name_with_path} ", "&& #{opts[:env]} #{slug_name_with_path} ", '-y ', #always clobber existing installs "-i #{deploy_dir} ", opts[:owner] ? "-o #{opts[:owner]} " : '', opts[:force] ? '-f ' : '', opts[:unpack] ? '-u ' : '', '-v ', #verbose '| tee -a /var/log/slug_deploy.log ', "; echo \"SSH_COMMAND_EXIT_CODE=$?\"'" ].join('') end
ssh_command(ssh, command, logger)
click to toggle source
# File lib/slugforge/models/host.rb, line 203 def ssh_command(ssh, command, logger) output = ssh.exec!("#{command}") exit_code_matches = /^SSH_COMMAND_EXIT_CODE=(\d+)$/.match(output) exit_code = exit_code_matches ? exit_code_matches[1].to_i : 0 logger_opts = if exit_code == 0 {:color => :green, :log_level => :verbose} else {:color => :red} end.merge({:status => :command}) logger.log "#{command}", logger_opts logger.log "Output:\n#{output}", logger_opts {:exit_code => exit_code, :output => output, :command => command, :username => ssh.options[:user]} end
ssh_opts(opts = {})
click to toggle source
# File lib/slugforge/models/host.rb, line 135 def ssh_opts(opts = {}) ssh_opts = { :forward_agent => true } if opts[:identity] ssh_opts[:key_data] = File.read(opts[:identity]) ssh_opts[:keys_only] = true end ssh_opts end
username(opts)
click to toggle source
# File lib/slugforge/models/host.rb, line 174 def username(opts) opts[:username] || Net::SSH.configuration_for(ip)[:user] || `whoami`.chomp end