require 'net/ssh' require 'pathname' namespace :exec_commands do desc "Exec commands Script" task :exec_commands,[:site_construct_id,:commands,:type,:server_name,:rails_env] => :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 = @site_construct.rails_env.blank? ? default_rails_env : @site_construct.rails_env cmd_output = exec_ssh_command_by_sudo_and_see_output(ssh,"ps -o args= -p `cat #{@site_construct.path}/#{@site_construct.site_name}/tmp/pids/unicorn.pid`",false) rescue [] enable_rails_env.each do |env| if( cmd_output[0].include?(env) rescue false) rails_env = env break elsif( cmd_output[0].include?("No such file or directory") rescue false) @site_construct.update(:status => "closed") break end end if args.rails_env.present? rails_env = args.rails_env end @site_construct.update(:rails_env=>rails_env) 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 else exec_ssh_command_by_sudo_and_see_output(ssh,"bundle exec unicorn_rails -c config/unicorn.rb -D -E #{rails_env}",false) 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=>site_server.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,update_array=false) return if @site_construct.nil? if update_last && !@site_construct.infos.empty? @site_construct.infos[-1] += info.to_s else if update_array @site_construct.infos += info else @site_construct.infos.push(info.to_s) end end @site_construct.save! return @site_construct.infos end def update_thread_infos_for_exec(info,update_last=false,update_array=false) if update_last && !@thread.status["infos"].empty? @thread.status["infos"][-1] += info.to_s else if update_array @thread.status["infos"] += info else @thread.status["infos"].push(info.to_s) end end @thread.save! return @thread.status["infos"] end def exec_ssh_command_and_see_output(session,command,update=true,output_string=false) outputs = [] @flag = (@type == "exec_all") if update if @site_construct update_thread_infos_for_exec("execing #{command} on on #{@site_construct.domain_name}") if @flag update_infos_for_exec("execing #{command}") elsif @thread update_thread_infos_for_exec("execing #{command}...") end 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| if data.to_s.include?("sudo password:") || data.to_s.include?("Password:") channel.send_data "#{@password}\n" else print "#{data}" data_str = data.to_s if data.include?("\n") || outputs.empty? output_lines = data_str.split("\n") first_output = output_lines[0] if outputs.count != 0 outputs[-1] += first_output else outputs << first_output end new_arr = output_lines[1..-1] if data_str[-1] == "\n" new_arr << "" end outputs += new_arr if update if first_output update_thread_infos_for_exec(first_output,true) if @flag update_infos_for_exec(first_output,true) end update_thread_infos_for_exec(new_arr,false,true) if @flag update_infos_for_exec(new_arr,false,true) end else if outputs.count == 0 outputs.push(data_str) else outputs[-1] += (data_str rescue "") end if update update_thread_infos_for_exec(data_str,true) if @flag update_infos_for_exec(data_str,true) end end end end channel.on_close do |ch| if update if @site_construct update_thread_infos_for_exec("finish execing #{command} on #{@site_construct.domain_name}") if @flag update_infos_for_exec("finish execing #{command}") elsif @thread update_thread_infos_for_exec("finish execing #{command}") end end end end end end session.loop if output_string return outputs.join("\n") else return outputs end end def exec_ssh_command_by_sudo_and_see_output(session,command,update=true,output_string=false) 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 if output_string return outputs.join("\n") else return outputs end end end