2020-04-28 13:26:49 +00:00
require 'net/ssh'
require 'pathname'
2022-10-16 08:43:14 +00:00
require 'json'
require 'base64'
2021-01-19 04:48:03 +00:00
namespace :exec_commands do
desc " Exec commands Script "
2022-11-14 04:46:26 +00:00
task :exec_commands , [ :base64_args , :site_construct_id , :commands , :type , :server_name , :rails_env , :commands_i18n ] = > :environment do | task , args |
2022-10-16 08:43:14 +00:00
if args . base64_args . present?
tmp = JSON . parse ( Base64 . decode64 ( args . base64_args ) ) rescue { }
args = Rake :: TaskArguments . new ( tmp . keys , tmp . values )
end
2021-01-19 04:48:03 +00:00
@type = args . type
2022-11-14 04:46:26 +00:00
if args . site_construct_id . present?
2021-01-19 04:48:03 +00:00
@site_construct = SiteConstruct . find ( args . site_construct_id )
site_server = SiteServer . where ( :server_name = > @site_construct . server_type ) . first
site_servers = [ site_server ]
2022-11-14 04:46:26 +00:00
@site_construct . update ( :status = > " execing " , :infos = > [ " " ] )
2021-01-19 04:48:03 +00:00
else
@site_construct = nil
site_servers = SiteServer . where ( :server_name . in = > args . server_name . split ( " //// " ) ) . to_a
end
if args . type == " exec_all "
2022-11-14 04:46:26 +00:00
Multithread . where ( :key = > 'execing_commands' ) . each do | thread |
if thread . status [ " status " ] == " error " || thread . status [ " status " ] == " finish "
thread . destroy
elsif thread . updated_at < ( Time . now - 5 . minute )
thread . destroy
end
end
2021-01-19 04:48:03 +00:00
@thread = Multithread . where ( :key = > 'execing_commands' ) . first
else
@thread = nil
end
if @thread . nil?
2021-12-30 13:46:50 +00:00
@command_trans = { }
@command_relations = { " upgrade_site " = > 'git fetch origin && rails runner "a=Admin::SitesController.new;a.git_reset(%w(origin)[0],%w(update)[0]);while(1) do (Multithread.where(key: %w(update_manager)[0],status: %w(finish)[0]).count!=0 ? break : nil) end;sleep(5)"' ,
" bundle_update " = > " bundle update && kill -s USR2 `cat tmp/pids/unicorn.pid` " ,
2022-12-09 06:44:26 +00:00
" restart_site " = > " kill -s TERM `fuser tmp/pids/unicorn.sock tmp/sockets/unicorn.sock tmp/unicorn.sock`;bundle exec unicorn_rails -c config/unicorn.rb -D -E {{rails_env}} " ,
" get_template " = > " mongo --eval 'db.sites.find().pretty()[0][ \" template \" ]' {{db_name}} --quiet " }
@preserve_output = [ " get_template " ]
2021-12-30 13:46:50 +00:00
@command_relations . each do | k , v |
@command_trans [ k ] = I18n . t ( " client_management. #{ k } " )
end
rails_envs = [ " production " , " development " ]
rails_envs . each do | env |
2022-01-14 01:41:44 +00:00
@command_relations [ " start_site_in_env, #{ env } " ] = " kill -s TERM `fuser tmp/pids/unicorn.sock tmp/sockets/unicorn.sock tmp/unicorn.sock`;bundle exec unicorn_rails -c config/unicorn.rb -D -E #{ env } "
2021-12-30 13:46:50 +00:00
@command_trans [ " start_site_in_env, #{ env } " ] = I18n . t ( " client_management.start_site_in_env " , { :env = > env } )
end
2021-01-19 04:48:03 +00:00
begin
2022-11-14 04:46:26 +00:00
if args . type == " exec_all "
if @thread . nil?
@thread = Multithread . create ( :key = > 'execing_commands' , :status = > { " infos " = > [ ] , " status " = > " execing " } )
else
@thread . update ( :status = > { " infos " = > [ ] , " status " = > " execing " } )
end
2021-01-19 04:48:03 +00:00
end
site_servers . each do | site_server |
ip = site_server . ip
2023-02-23 04:52:58 +00:00
server_port = site_server . port
2021-01-19 04:48:03 +00:00
user = site_server . account
password = site_server . password
@password = password
begin
2023-02-23 04:52:58 +00:00
Net :: SSH . start ( ip , user , { password : password , port : server_port } ) do | ssh |
2021-01-19 04:48:03 +00:00
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
2023-02-23 04:52:58 +00:00
Net :: SSH . start ( ip , user , { password : password , port : server_port } ) do | ssh |
2021-01-19 04:48:03 +00:00
@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' ]
2021-12-17 01:36:01 +00:00
rails_env = @site_construct . rails_env . blank? ? default_rails_env : @site_construct . rails_env
2021-04-14 10:55:07 +00:00
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 )
2021-12-17 01:36:01 +00:00
@site_construct . update ( :status = > " closed " )
2021-01-19 04:48:03 +00:00
break
end
end
2021-03-17 11:19:18 +00:00
if args . rails_env . present?
rails_env = args . rails_env
end
2021-04-14 11:57:06 +00:00
@site_construct . update ( :rails_env = > rails_env )
2021-01-19 04:48:03 +00:00
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 } \n kill -s TERM `fuser tmp/unicorn.sock` \n sudo -p \" sudo password: \" kill -s TERM `sudo -p \" sudo password: \" fuser tmp/unicorn.sock` \n sudo -p \" sudo password: \" rm -f tmp/pids/unicorn.pid \n bundle 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
2021-12-17 01:36:01 +00:00
else
exec_ssh_command_by_sudo_and_see_output ( ssh , " bundle exec unicorn_rails -c config/unicorn.rb -D -E #{ rails_env } " , false )
2021-01-19 04:48:03 +00:00
end
end
update_infos_for_exec ( " finish starting #{ @site_construct . site_name } " )
@site_construct . update ( :status = > " finish " )
2021-12-30 13:46:50 +00:00
else
if args . type == 'exec_all'
sites = SiteConstruct . where ( :server_type = > site_server . server_name ) . to_a
else
sites = [ @site_construct ]
end
2022-11-14 04:46:26 +00:00
commands = args . commands
if commands . class == String
commands = commands . split ( " //// " ) . select { | c | c != " " }
elsif commands . nil?
commands = [ ]
end
if commands . include? ( " {{create_users}} " )
2022-01-14 03:20:39 +00:00
commands = [ 'sudo -p "sudo password:" chmod 777 {{full_site_path}} -R' ,
'sudo -p "sudo password:" chmod 777 {{full_site_path}}/public -R' ,
'sudo -p "sudo password:" chmod 777 {{full_site_path}}/app/templates -R' ,
'sudo -p "sudo password:" useradd --home-dir {{full_site_path}}/app/templates {{site_name}} --no-create-home --badnames' ,
2022-01-14 02:46:44 +00:00
'sudo -p "sudo password:" bash -l -c \'echo -e "{{site_name}}\n{{site_name}}"|passwd {{site_name}}\'' ]
end
2022-11-14 04:46:26 +00:00
@commands_i18n = args . commands_i18n # command 客製化翻譯
2021-01-19 04:48:03 +00:00
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 " )
2021-12-30 13:46:50 +00:00
rails_env = @site_construct . rails_env . blank? ? " development " : @site_construct . rails_env
2022-11-14 04:46:26 +00:00
commands . each_with_index do | command , idx |
2021-12-30 13:46:50 +00:00
@command_i18n = command
update_flag = 1
@command_relations . each do | k , v |
if command . include? ( " {{ #{ k } }} " )
2022-12-09 06:44:26 +00:00
if @preserve_output . include? ( k )
update_flag = 1
else
update_flag = 2 #Not logging commands result
end
2021-12-30 13:46:50 +00:00
end
command = command . gsub ( " {{ #{ k } }} " , v )
@command_i18n = @command_i18n . gsub ( " {{ #{ k } }} " , @command_trans [ k ] )
end
2022-11-14 04:46:26 +00:00
if @commands_i18n
tmp = @commands_i18n [ idx ]
@command_i18n = tmp if tmp
2022-10-01 13:12:14 +00:00
end
2021-12-30 13:46:50 +00:00
command = command . gsub ( " {{rails_env}} " , rails_env )
2022-01-14 01:08:06 +00:00
command = command . gsub ( " {{full_site_path}} " , @site_construct . full_site_path )
command = command . gsub ( " {{site_name}} " , @site_construct . site_name )
2022-10-01 13:12:14 +00:00
command = command . gsub ( " {{db_name}} " , @site_construct . db_name )
2022-01-14 01:17:10 +00:00
@command_i18n = @command_i18n . gsub ( " {{rails_env}} " , rails_env )
@command_i18n = @command_i18n . gsub ( " {{full_site_path}} " , @site_construct . full_site_path )
@command_i18n = @command_i18n . gsub ( " {{site_name}} " , @site_construct . site_name )
2022-11-14 04:46:26 +00:00
command = command . gsub ( " ' " , " ' \" ' \" ' " )
2021-12-30 13:46:50 +00:00
exec_ssh_command_by_sudo_and_see_output ( ssh , " bash -l -c 'cd #{ @site_construct . path } / #{ @site_construct . site_name } ; #{ command } ' " , update_flag )
2022-01-14 01:17:10 +00:00
@command_i18n = nil
2021-01-19 04:48:03 +00:00
end
2020-04-28 13:26:49 +00:00
end
end
end
end
2021-01-19 04:48:03 +00:00
if ! @thread . nil?
@thread . update ( :status = > @thread . status . merge ( { " status " = > " finish " } ) )
2022-11-14 04:46:26 +00:00
elsif @site_construct
@site_construct . update ( :status = > " finish " )
2021-01-19 04:48:03 +00:00
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
2020-04-28 13:26:49 +00:00
end
2021-01-19 04:48:03 +00:00
end
2020-04-28 13:26:49 +00:00
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
2022-07-21 16:10:04 +00:00
return output . encode! ( " UTF-8 " , :invalid = > :replace , :undef = > :replace , :replace = > '' )
2020-04-28 13:26:49 +00:00
end
2021-08-11 10:25:03 +00:00
def update_infos_for_exec ( info , update_last = false , update_array = false )
2021-08-11 08:16:14 +00:00
return if @site_construct . nil?
2021-01-19 04:48:03 +00:00
if update_last && ! @site_construct . infos . empty?
@site_construct . infos [ - 1 ] += info . to_s
else
2021-08-11 10:25:03 +00:00
if update_array
@site_construct . infos += info
else
@site_construct . infos . push ( info . to_s )
end
2021-01-19 04:48:03 +00:00
end
2020-04-28 13:26:49 +00:00
@site_construct . save!
return @site_construct . infos
end
2021-08-11 10:25:03 +00:00
def update_thread_infos_for_exec ( info , update_last = false , update_array = false )
2021-01-19 04:48:03 +00:00
if update_last && ! @thread . status [ " infos " ] . empty?
@thread . status [ " infos " ] [ - 1 ] += info . to_s
else
2021-08-11 10:25:03 +00:00
if update_array
@thread . status [ " infos " ] += info
else
@thread . status [ " infos " ] . push ( info . to_s )
end
2021-01-19 04:48:03 +00:00
end
@thread . save!
return @thread . status [ " infos " ]
end
2021-12-30 13:46:50 +00:00
def exec_ssh_command_and_see_output ( session , command , update_flag = 1 , output_string = false )
2021-01-19 04:48:03 +00:00
outputs = [ ]
@flag = ( @type == " exec_all " )
2021-12-30 13:46:50 +00:00
command_i18n = command
update = ( update_flag == true || update_flag == 1 || update_flag == 2 )
update_outputs = ( update_flag == true || update_flag == 1 )
2021-01-19 04:48:03 +00:00
if update
2021-12-30 13:46:50 +00:00
command_i18n = @command_i18n if @command_i18n
2021-08-11 08:16:14 +00:00
if @site_construct
2021-12-30 13:46:50 +00:00
update_thread_infos_for_exec ( [ " execing #{ command_i18n } on on <a href=' #{ ( ( @site_construct . get_port == " 443 " ) ? " https " : " http " ) } :// #{ @site_construct . domain_name } #{ ( ( @site_construct . get_port == " 80 " || @site_construct . get_port == " 443 " || @site_construct . get_port . blank? ) ? " " : ( ':' + @site_construct . get_port ) ) } '> #{ @site_construct . domain_name } </a> " , " " ] , false , true ) if @flag
update_infos_for_exec ( [ " execing #{ command_i18n } " , " " ] , false , true )
2021-08-11 08:16:14 +00:00
elsif @thread
2021-12-30 13:46:50 +00:00
update_thread_infos_for_exec ( [ " execing #{ command_i18n } ... " , " " ] , false , true )
2021-08-11 08:16:14 +00:00
end
2021-01-19 04:48:03 +00:00
end
session . open_channel do | channel |
channel . request_pty do | channel , success |
2022-09-19 04:07:27 +00:00
channel . exec ( " LANG=en.UTF8 #{ command } " ) do | ch , success |
2021-01-19 04:48:03 +00:00
abort " could not execute command: #{ command } " unless success
channel . on_data do | ch , data |
2022-12-02 05:09:43 +00:00
data_str = data . to_s
data_str . encode! ( " UTF-8 " , :invalid = > :replace , :undef = > :replace , :replace = > '' )
if data_str . include? ( " sudo password: " ) || data_str . include? ( " Password: " )
2021-08-11 10:25:03 +00:00
channel . send_data " #{ @password } \n "
2021-01-19 04:48:03 +00:00
else
2022-12-02 05:09:43 +00:00
print data_str unless @no_stdout
data_str . gsub! ( " \r \n " , " \n " )
data_str . sub! ( / \ r$ / , '' )
next if data_str . length == 0
if data_str . include? ( " \n " ) || outputs . empty?
2021-12-01 12:59:18 +00:00
output_lines = data_str . split ( " \n " )
2021-12-01 13:11:18 +00:00
first_output = output_lines [ 0 ]
2022-01-04 02:23:28 +00:00
if first_output
if outputs . count != 0
outputs [ - 1 ] += first_output
else
outputs << first_output
end
2021-12-01 12:59:18 +00:00
end
new_arr = output_lines [ 1 .. - 1 ]
2022-01-26 01:37:27 +00:00
new_arr = [ ] if new_arr . nil?
2021-12-01 12:59:18 +00:00
if data_str [ - 1 ] == " \n "
new_arr << " "
end
outputs += new_arr
2021-12-30 13:46:50 +00:00
if update_outputs
2021-12-01 12:59:18 +00:00
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 )
2021-08-11 10:25:03 +00:00
end
2021-05-31 09:58:24 +00:00
else
2021-08-11 10:25:03 +00:00
if outputs . count == 0
2021-12-01 12:59:18 +00:00
outputs . push ( data_str )
2021-08-11 10:25:03 +00:00
else
2021-12-01 12:59:18 +00:00
outputs [ - 1 ] += ( data_str rescue " " )
2021-08-11 10:25:03 +00:00
end
2021-12-30 13:46:50 +00:00
if update_outputs
2021-12-01 12:59:18 +00:00
update_thread_infos_for_exec ( data_str , true ) if @flag
update_infos_for_exec ( data_str , true )
2021-08-11 10:25:03 +00:00
end
2021-01-19 04:48:03 +00:00
end
end
end
channel . on_close do | ch |
if update
2021-08-11 08:16:14 +00:00
if @site_construct
2021-12-30 13:46:50 +00:00
update_thread_infos_for_exec ( " finish execing #{ command_i18n } on <a href=' #{ ( ( @site_construct . get_port == " 443 " ) ? " https " : " http " ) } :// #{ @site_construct . domain_name } #{ ( ( @site_construct . get_port == " 80 " || @site_construct . get_port == " 443 " || @site_construct . get_port . blank? ) ? " " : ( ':' + @site_construct . get_port ) ) } '> #{ @site_construct . domain_name } </a> " ) if @flag
update_infos_for_exec ( " finish execing #{ command_i18n } " )
2021-08-11 08:16:14 +00:00
elsif @thread
2021-12-30 13:46:50 +00:00
update_thread_infos_for_exec ( " finish execing #{ command_i18n } " )
2021-08-11 08:16:14 +00:00
end
2021-01-19 04:48:03 +00:00
end
end
end
end
end
session . loop
2021-08-11 08:16:14 +00:00
if output_string
return outputs . join ( " \n " )
else
return outputs
end
2021-01-19 04:48:03 +00:00
end
2021-12-30 13:46:50 +00:00
def exec_ssh_command_by_sudo_and_see_output ( session , command , update = 1 , output_string = false )
2021-01-19 04:48:03 +00:00
outputs = exec_ssh_command_and_see_output ( session , command , update )
2022-09-19 04:07:27 +00:00
tmp = outputs . join ( " \n " )
if tmp . include? ( " Permission denied " ) || tmp . include? ( " Operation not permitted " )
2021-01-19 04:48:03 +00:00
outputs = exec_ssh_command_and_see_output ( session , " sudo -p 'sudo password:' #{ command } " , update )
2022-09-19 04:07:27 +00:00
tmp = outputs . join ( " \n " )
2021-01-19 04:48:03 +00:00
end
2021-08-11 08:16:14 +00:00
if output_string
2022-09-19 04:07:27 +00:00
return tmp
2021-08-11 08:16:14 +00:00
else
return outputs
end
2021-01-19 04:48:03 +00:00
end
2020-04-28 13:26:49 +00:00
end