I\'ve been trying to write a small library using Thor to help assist me in quick creating new projects and sites. I wrote this small method:
def ssh(cmd)
Actually in the end, I did something very similar to Riba's suggestion (although his/her code is much better and smarter, I think)
def ssh(env, cmd, opts={})
Net::SSH.start( config[env]["ip"], config[env]["user"], :port => config[env]["port"]) do |session|
cmd = "sudo #{cmd}" if opts[:sudo]
print cmd
session.exec cmd
end
end
I cheated by doing adding this:
sudouser ALL=(ALL:ALL) NOPASSWD: ALL
to /etc/sudoers
Also, be sure to use visudo
when editing!!!
It's possible to set it up with net/ssh
, but it's really too much effort, just execute ssh
command like this:
system("ssh", "-t", "#{user}@#{host}", "sudo cp #{file_from_path} #{file_to_path}")
This -t
means to allocate a tty. Without it it will still work, but your password will be seen in clear.
(I assume you intend to type sudo password manually. If not, go the authorized keys way).
Here is a possible solution :
def ssh(cmd)
Net::SSH.start( server_ip, user, :port => port) do |session|
result = nil
session.exec!(cmd) do |channel, stream, data|
if data =~ /^\[sudo\] password for user:/
channel.send_data 'your_sudo_password'
else
result << data
end
end
result # content of 'cmd' result
end
end
But with this solution, you have your root password writen in clear in a script file somewhere ...
I hope this will help someone searching. I also needed to sudo
during deployment (restarting thin instances)
# deploy.rake
require 'net/ssh'
# INITIALIZE CONSTANTS HERE
HOST = 'yourwebsite.com'
USER = 'admin'
PASSWORD = 'your server password' # or use ENV variables?
# etc.
namespace :deploy do
namespace :staging do
task :restart do
commands = [
"cd #{PATH_TO_STAGING_APP} && git checkout master",
"git reset --hard HEAD",
"git pull origin master",
"bundle install --without test development",
"sudo thin restart -C /etc/thin/#{STAGING_APP}.yml"
]
Net::SSH.start(HOST, USER, :password => PASSWORD) do |ssh|
ssh.open_channel do |channel|
channel.request_pty do |ch, success|
if success
puts "Successfully obtained pty"
else
puts "Could not obtain pty"
end
end
channel.exec(commands.join(';')) do |ch, success|
abort "Could not execute commands!" unless success
channel.on_data do |ch, data|
puts "#{data}"
channel.send_data "#{PASSWORD}\n" if data =~ /password/
end
channel.on_extended_data do |ch, type, data|
puts "stderr: #{data}"
end
channel.on_close do |ch|
puts "Channel is closing!"
end
end
end
ssh.loop
end
end
end
end
Note that one channel can only execute one command. Hence I chained the commands together with commands.join(';')
Reference: Net::SSH::Connection::Channel
The first thing you might want to try is using public keys instead of passwords to login. Then also try running the command from the interactive shell.
For example:
(This part really depends on the server/client software you have)
$ ssh-keygen
$ scp .ssh/id-dsa.pub server:
$ ssh server
server$ cat id-dsa.pub >> .ssh/authorizedkeys
$ scp -c "ls"
this should work without any prompts, if the key sharing was successful.