require 'net/ssh' require 'pathname' namespace :create_site do desc "Copy Site from another site" task :copy_site,[:ip,:user,:password,:site_name,:domain_name,:port,:db_name,:path,:site_construct_id,:template_site_construct_id] => :environment do |task,args| @password = args.password template_site = SiteConstruct.find(args.template_site_construct_id) if args.site_construct_id.blank? @site_construct = SiteConstruct.new @site_construct.server_type = (SiteServer.where(:ip=>args.ip).first.server_name rescue args.ip) @site_construct.site_name = args.site_name @site_construct.domain_name = args.domain_name @site_construct.db_name = args.db_name @site_construct.port = args.port @site_construct.path = args.path @site_construct.school_name = args.site_name.split(/[-_]/) @site_construct.user_id = User.first.id.to_s @site_construct.save else @site_construct = SiteConstruct.find(args.site_construct_id) end begin @site_construct.update(:status=>"creating") @site_construct.update!(:infos=>[]) begin Net::SSH.start(args.ip , args.user , password: args.password) do |ssh| end rescue Net::SSH::HostKeyMismatch system("ssh-keygen -f \"$HOME/.ssh/known_hosts\" -R #{args.ip}") rescue Errno::ENOTTY system("ssh-add \"$HOME/.ssh/id_rsa\"") end Net::SSH.start(args.ip , args.user , password: args.password) do |ssh| update_infos("setting nginx for #{args.site_name}") nginx_setting_texts = ('upstream '+args.site_name+'_sock {\n'+ ' server unix:'+args.path+'/'+args.site_name+'/tmp/unicorn.sock;\n'+ '}\n'+ 'server {\n'+ ' listen '+args.port+';\n\n'+ ' root '+args.path+'/'+args.site_name+'/public;\n\n'+ 'server_name '+args.domain_name+';\n\n'+ ' client_max_body_size 500m;\n\n'+ ' location / {\n'+ ' try_files \$uri \$uri/index.html \$uri.html @app;\n'+ ' }\n\n'+ ' location @app {\n'+ ' proxy_redirect off;\n'+ ' proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;\n'+ ' proxy_set_header Host \$http_host;\n'+ ' proxy_connect_timeout 360;\n'+ 'proxy_pass http://'+args.site_name+'_sock;\n'+ ' }\n'+ '}') exec_ssh_command_by_sudo_for_create(ssh,"touch /etc/nginx/orbit_sites/#{args.site_name}") exec_ssh_command_by_sudo_for_create(ssh,"sh -c \"echo '#{nginx_setting_texts}' > /etc/nginx/orbit_sites/#{args.site_name}\"") update_infos("restarting nginx") exec_ssh_command_by_sudo_for_create(ssh,"sudo -p 'sudo password:' service nginx restart") update_infos("finish restarting nginx") exec_ssh_command_by_sudo_for_create(ssh,"mkdir -p #{args.path}") update_infos("copying site's files for #{args.site_name}") exec_ssh_command_for_create(ssh,"sudo -p 'sudo password:' apt-get install -y rsync") if @site_construct.server_type == template_site.server_type exec_ssh_command_for_create(ssh,"rsync -arv --delete --exclude={tmp/cache/,tmp/unicorn.sock,tmp/pids/*} #{template_site.path}/#{template_site.site_name}/ #{args.path}/#{args.site_name}/",true) else server = SiteServer.where(:server_name => template_site.server_type).first exec_ssh_command_for_create(ssh,"sudo -p 'sudo password:' apt-get install -y sshpass") outputs = exec_ssh_command_for_create(ssh,"sshpass -p \"#{server.password}\" rsync -arv --delete --exclude={tmp/cache/,tmp/unicorn.sock,tmp/pids/*} #{server.account}@#{server.ip}:#{template_site.path}/#{template_site.site_name}/ #{args.path}/#{args.site_name}/",true) if outputs.join("\n").include?("Host key verification failed") exec_ssh_command_by_sudo_for_create(session,"ssh-keygen -f \"$HOME/.ssh/known_hosts\" -R #{server.ip}") exec_ssh_command_by_sudo_for_create(session,"ssh-keyscan -H #{server.ip} >> ~/.ssh/known_hosts") exec_ssh_command_for_create(ssh,"sshpass -p \"#{server.password}\" rsync -arv --delete --exclude={tmp/cache/,tmp/unicorn.sock,tmp/pids/*} #{server.account}@#{server.ip}:#{template_site.path}/#{template_site.site_name}/ #{args.path}/#{args.site_name}/",true) end end update_infos("Finish copying site's files for #{args.site_name}!") exec_ssh_command_by_sudo_for_create(ssh,"sudo -p 'sudo password:' chown #{args.user}:#{args.user} #{args.path}/#{args.site_name} -R") exec_ssh_command_by_sudo_for_create(ssh,"sudo -p 'sudo password:' chmod 777 #{args.path}/#{args.site_name} -R") db_setting_text = ssh.exec!("cat #{args.path}/#{args.site_name}/config/mongoid.yml") update_infos("setting dbname to #{args.db_name}") exec_ssh_command_by_sudo_for_create(ssh,"sh -c \"echo '#{db_setting_text.gsub(template_site.db_name,args.db_name)}' > #{args.path}/#{args.site_name}/config/mongoid.yml\"",true) update_infos("copying db from #{template_site.db_name} to #{args.db_name}") if @site_construct.server_type == template_site.server_type exec_ssh_command_by_sudo_for_create(ssh,"mongodump -d #{template_site.db_name} -o #{args.path}/#{args.site_name}/dump_xxxx") exec_ssh_command_by_sudo_for_create(ssh,"mongorestore --drop -d #{args.db_name} #{args.path}/#{args.site_name}/dump_xxxx/#{template_site.db_name}") exec_ssh_command_by_sudo_for_create(ssh,"rm -rf #{args.path}/#{args.site_name}/dump_xxxx") else exec_ssh_command_by_sudo_for_create(ssh,"sshpass -p '#{server.password}' ssh #{server.account}@#{server.ip} 'mongodump -d #{template_site.db_name} -o #{template_site.path}/#{template_site.site_name}/dump_xxxx'") exec_ssh_command_for_create(ssh,"sshpass -p \"#{server.password}\" rsync -arv --delete #{server.account}@#{server.ip}:#{template_site.path}/#{template_site.site_name}/dump_xxxx/ #{args.path}/#{args.site_name}/dump_xxxx/") exec_ssh_command_by_sudo_for_create(ssh,"mongorestore --drop -d #{args.db_name} #{args.path}/#{args.site_name}/dump_xxxx/#{template_site.db_name}") exec_ssh_command_by_sudo_for_create(ssh,"rm -rf #{args.path}/#{args.site_name}/dump_xxxx") exec_ssh_command_by_sudo_for_create(ssh,"sshpass -p '#{server.password}' ssh #{server.account}@#{server.ip} 'rm -rf #{template_site.path}/#{template_site.site_name}/dump_xxxx'") end update_infos("Finish copying database!") update_infos("execing bundle install...") exec_ssh_command_by_sudo_for_create(ssh,"bash -l -c 'cd #{args.path}/#{args.site_name}\nbundle install'",true) exec_ssh_command_by_sudo_for_create(ssh,"bash -l -c 'cd #{args.path}/#{args.site_name}\nbundle install'") update_infos("finish execing bundle install") update_infos("starting #{args.site_name} web server to development") outputs = exec_ssh_command_by_sudo_for_create(ssh,"bash -l -c 'cd #{args.path}/#{args.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 development\n'") if outputs.include? "not writable" exec_ssh_command_by_sudo_for_create(ssh,"sudo -p 'sudo password:' chown #{args.user}:#{args.user} #{args.path}/#{args.site_name} -R") exec_ssh_command_by_sudo_for_create(ssh,"sudo -p 'sudo password:' chmod 777 #{args.path}/#{args.site_name} -R") end update_infos("finish creating #{args.site_name}") exec_ssh_command_by_sudo_for_create(ssh,"chmod 777 #{args.path}/#{args.site_name} -R") @site_construct.update(:status =>"finish") puts "finish creating #{args.site_name} on #{args.ip}" end rescue =>e @site_construct.update(:status =>"error",:infos=>@site_construct.infos.push("#{e}")) 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_thread_infos_for_exec(info,update_last=false) if update_last @thread.status["infos"][-1] += info else @thread.status["infos"] = @thread.status["infos"].push(info) end @thread.save! return @thread.status["infos"] end def exec_ssh_command_for_create(session,command,update=false) outputs = [] 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| outputs.push(data) print "#{data}" if update if data.include? "\n" update_infos_for_exec(data) else 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 end end end session.loop return outputs end def exec_ssh_command_by_sudo_for_create(session,command,update=false) outputs = exec_ssh_command_for_create(session,command,update) if outputs.join("\n").include?("Permission denied") || outputs.join("\n").include?("Operation not permitted") outputs = exec_ssh_command_for_create(session,"sudo -p 'sudo password:' #{command}",update) end return outputs.join("\n") end def update_infos_for_exec(info,update_last=false) if update_last && !@site_construct.infos.empty? @site_construct.infos[-1] += info else @site_construct.infos = @site_construct.infos.push(info) end @site_construct.save! return @site_construct.infos end def update_infos(info) puts info @site_construct.infos = @site_construct.infos.push(info) @site_construct.save! return @site_construct.infos end end