require 'net/ssh'
require 'pathname'
namespace :exec_commands do
desc "Exec commands Script"
task :exec_commands,[:site_construct_id,:commands,:type,:server_name] => :environment do |task,args|
@type = args.type
if !args.site_construct_id.blank?
@site_construct = SiteConstruct.find(args.site_construct_id)
site_server = SiteServer.where(:server_name=>@site_construct.server_type).first
site_servers = [site_server]
else
@site_construct = nil
site_servers = SiteServer.where(:server_name.in=>args.server_name.split("////")).to_a
end
if args.type == "exec_all"
Multithread.where(:key=>'execing_commands').each{|thread| thread.destroy if (thread.status["status"] == "error" || thread.status["status"] == "finish")}
@thread = Multithread.where(:key=>'execing_commands').first
else
@thread = nil
end
if @thread.nil?
begin
if @thread.nil? && args.type == "exec_all"
@thread = Multithread.create(:key=>'execing_commands',:status=>{"infos"=>[],"status"=>"execing"})
end
site_servers.each do |site_server|
ip = site_server.ip
user = site_server.account
password = site_server.password
@password = password
begin
Net::SSH.start(ip , user , password: password) do |ssh|
end
rescue Net::SSH::HostKeyMismatch
system("ssh-keygen -f \"$HOME/.ssh/known_hosts\" -R #{ip}")
rescue Errno::ENOTTY
system("ssh-add \"$HOME/.ssh/id_rsa\"")
end
Net::SSH.start(ip , user , password: password) do |ssh|
@site_construct.update!(:infos=>[]) rescue nil
if args.type == 'close_site'
exec_ssh_command_by_sudo_and_see_output(ssh,"chmod 777 #{@site_construct.path}/#{@site_construct.site_name} -R",false)
exec_ssh_command_by_sudo_and_see_output(ssh,"bash -l -c 'kill -s TERM `fuser #{@site_construct.path}/#{@site_construct.site_name}/tmp/unicorn.sock`'",false)
update_infos_for_exec("finish closing #{@site_construct.site_name}")
@site_construct.update(:status =>"closed")
elsif args.type == 'open_site'
default_rails_env = 'development'
enable_rails_env = ['development','production']
rails_env = default_rails_env
log_files = exec_ssh_command_by_sudo_and_see_output(ssh,"ls -t '#{@site_construct.path}/#{@site_construct.site_name}/log'",false).flat_map{|output| output.split(/(\r\n|\t|\n|\s+)/)}.select{|output| output.present? && /(\r\n|\t|\n|\s+)/.match(output).nil?} rescue []
log_files.each do |log_file|
if( enable_rails_env.include?(log_file.sub('.log','')) rescue false)
rails_env = log_file.sub('.log','')
break
end
end
update_infos_for_exec("starting site to #{rails_env}")
outputs = exec_ssh_command_by_sudo_and_see_output(ssh,"sudo -p 'sudo password:' chmod 777 #{@site_construct.path}/#{@site_construct.site_name} -R",false)
output = exec_ssh_command_by_sudo_and_see_output(ssh,"bash -l -c 'cd #{@site_construct.path}/#{@site_construct.site_name}\nkill -s TERM `fuser tmp/unicorn.sock`\nsudo -p \"sudo password:\" kill -s TERM `sudo -p \"sudo password:\" fuser tmp/unicorn.sock`\nsudo -p \"sudo password:\" rm -f tmp/pids/unicorn.pid\nbundle exec unicorn_rails -c config/unicorn.rb -D -E #{rails_env}\n'",false)
if output.include? "bundle install"
update_infos_for_exec("not yet bundle install")
outputs = exec_ssh_command_by_sudo_and_see_output(ssh,"bash -l -c 'cd #{@site_construct.path}/#{@site_construct.site_name};bundle install'")
if outputs.join("\n").include? "Username for"
update_infos_for_exec("Cannot finish bundle install.")
update_infos_for_exec("This site include an private git project.")
@site_construct.update(:status =>"error")
break
end
end
update_infos_for_exec("finish starting #{@site_construct.site_name}")
@site_construct.update(:status =>"finish")
elsif args.type == 'exec_all'
sites = SiteConstruct.where(:server_type=>args.server_name).to_a
commands = args.commands.split("////").select{|c| c != ""} rescue [args.commands]
sites.each do |site_construct|
@site_construct = site_construct
@site_construct.update!(:infos=>[])
exec_ssh_command_by_sudo(ssh,"chmod 777 #{@site_construct.path}/#{@site_construct.site_name} -R")
commands.each do |command|
exec_ssh_command_by_sudo_and_see_output(ssh,"bash -l -c 'cd #{@site_construct.path}/#{@site_construct.site_name};#{command}'")
end
end
else
exec_ssh_command_by_sudo(ssh,"chmod 777 #{@site_construct.path}/#{@site_construct.site_name} -R")
commands = args.commands.split("////").select{|c| c != ""} rescue [args.commands]
commands.each do |command|
exec_ssh_command_by_sudo_and_see_output(ssh,"bash -l -c 'cd #{@site_construct.path}/#{@site_construct.site_name};#{command}'")
end
end
end
end
if !@thread.nil?
@thread.update(:status=>@thread.status.merge({"status"=>"finish"}))
end
rescue => e
if !@thread.nil?
@thread.update(:status=>{"infos"=>@thread.status["infos"].push(e.message),"status"=>"error"})
@thread.update(:status=>{"infos"=>@thread.status["infos"].push(e.backtrace.join("\n")),"status"=>"error"})
end
if !@site_construct.nil?
@site_construct.update(:status =>"error",:infos=>@site_construct.infos.push("#{e.message}"))
@site_construct.update(:status =>"error",:infos=>@site_construct.infos.push("#{e.backtrace.join("\n")}"))
end
end
end
end
def exec_ssh_command_by_sudo(session,command)
output = session.exec!("echo '#{@password}' | sudo -S #{command}")
# output = session.exec!("echo '#{@password}' | sudo -S -s #{command}")
if output.include?("sudo:") && output.include?("command not found")
output = session.exec!(command)
end
return output
end
def update_infos_for_exec(info,update_last=false)
if update_last && !@site_construct.infos.empty?
@site_construct.infos[-1] += info.to_s
else
@site_construct.infos = @site_construct.infos.push(info.to_s)
end
@site_construct.save!
return @site_construct.infos
end
def update_thread_infos_for_exec(info,update_last=false)
if update_last && !@thread.status["infos"].empty?
@thread.status["infos"][-1] += info.to_s
else
@thread.status["infos"] = @thread.status["infos"].push(info.to_s)
end
@thread.save!
return @thread.status["infos"]
end
def exec_ssh_command_and_see_output(session,command,update=true)
outputs = []
@flag = (@type == "exec_all")
if update
update_thread_infos_for_exec("execing #{command} on on #{@site_construct.domain_name}") if @flag
update_infos_for_exec("execing #{command}")
end
session.open_channel do |channel|
channel.request_pty do |channel, success|
channel.exec(command) do |ch, success|
abort "could not execute command: #{command}" unless success
channel.on_data do |ch, data|
print "#{data}"
if data.include? "\n" || outputs.empty?
outputs.push(data.to_s)
if update
update_thread_infos_for_exec(data) if @flag
update_infos_for_exec(data)
end
else
outputs[-1] += data.to_s rescue ""
if update
update_thread_infos_for_exec(data,true) if @flag
update_infos_for_exec(data,true)
end
end
if data.to_s.include?("sudo password:") || data.to_s.include?("Password:")
channel.send_data "#{@password}\n"
end
end
channel.on_close do |ch|
if update
update_thread_infos_for_exec("finish execing #{command} on #{@site_construct.domain_name}") if @flag
update_infos_for_exec("finish execing #{command}")
end
end
end
end
end
session.loop
return outputs
end
def exec_ssh_command_by_sudo_and_see_output(session,command,update=true)
outputs = exec_ssh_command_and_see_output(session,command,update)
if outputs.join("\n").include?("Permission denied") || outputs.join("\n").include?("Operation not permitted")
outputs = exec_ssh_command_and_see_output(session,"sudo -p 'sudo password:' #{command}",update)
end
return outputs
end
end