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 = default_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 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) 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