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