Part of a Chef cookbook I'm writing is configuring perforce, which requires the user to enter their password (lest they save this in plaintext in an "attributes" file). Is it possible to interrupt the provisioning with an interactive prompt?
We would prompt the user from the Vagrantfile and then set that value as a Chef attribute. Prompts really only makes sense on dev boxes, so they really shouldn't be part of the Chef recipe:
Vagrant.configure('2') do |config|
config.vm.provision :chef_client do |chef|
chef.add_role 'dev'
chef.chef_server_url = 'https://api.opscode.com/organizations/myorg'
chef.node_name = "vagrant-#{ENV['USER']}-dev.example.com"
chef.validation_key_path = 'example-validator'
chef.json = {
'mysvc' => {
'password' => MySvc.password()
}
}
end
end
module MySvc
def self.password
begin
system 'stty -echo'
print 'MySvc Password: '
; pass = $stdin.gets.chomp; puts "\n"
ensure
system 'stty echo'
end
pass
end
end
Correct me if i misunderstood your problem, if you want to read user input :
You can use built-in SHELL command "read".
example:
[myprompt]$ read -p "Insert text : " IN_TEXT
Insert text : user input
[myprompt]$ echo $IN_TEXT
user input
ps: if you use read command for password you can use "-s" option to hide input coming from terminal.
example2 :
[myprompt]$ read -sp "Insert text : " IN_TEXT
Insert text : //stdin <<"user input"
[myprompt]$ echo $IN_TEXT
user input
Related
ok
I don't know how to change rvm version over ssh under os x server.
What I do:
Login on server over ssh
run script (below) and catch error
Error: 'rvm is not a funciton, many-many-words'
What I have as script:
use File::Spec;
my $server_directory = File::Spec->catfile($ENV{HOME},'MyProject');
my $exec_file = File::Spec->catfile($server_directory,'run_script.rb');
my $run_ruby_script = qq'bundle exec ruby $exec_file'.' '.join(' ',#ARGV);
# reload bash profile
print qx(source $ENV{HOME}/.bash_profile);
print qx(source $ENV{HOME}/.bashrc);
# reload ruby
print qx(source $ENV{HOME}/.rvm/scripts/rvm);
my $ruby_setup = qq([[ -s "$ENV{HOME}/.rvm/scripts/rvm" ]] && source "$ENV{HOME}/.rvm/scripts/rvm");
print $ruby_setup. "\n";
# change directory
chdir($server_directory);
# configure gemset name
my $version = qx(cat .ruby-version);
chomp($version);
my $gemset = qx(cat .ruby-gemset);
chomp($gemset);
my $change_rvm_gemset = qq(rvm use $version\#$gemset);
print qx($ruby_setup && $change_rvm_gemset);
print qx(rvm current);
Ok, after all.
def exec_via_bash(line)
puts %Q(#{line})
exec = 'bash -c "#{line}"'
puts `#{exec}`
end
def RubySetup
# reload bash profile
homedir = ENV['HOME']
exec_via_bash %Q(source #{homedir}/.bash_profile);
exec_via_bash %Q(source #{homedir}/.bashrc);
# reload ruby
exec_via_bash %Q(source #{homedir}/.rvm/scripts/rvm);
ruby_setup = %Q([[ -s "#{homedir}/.rvm/scripts/rvm" ]] && source "#{homedir}/.rvm/scripts/rvm")
puts ruby_setup
ruby_setup
end
if ARGV.empty?
puts "there is not enough arguments passed. maybe you forget ruby file to exec?"
exit(1)
end
ruby_script_path = ARGV.shift;
exec_file_absolute_path = File.expand_path(ruby_script_path)
unless File.exists? exec_file_absolute_path
puts "file #{exec_file_absolute_path} doesn't exists!"
exit(1)
end
exec_file_directory = File.dirname(exec_file_absolute_path)
exec_bundle = %Q'bundle exec ruby #{exec_file_absolute_path}' + ' ' + ARGV.join(' ')
# change directory
Dir.chdir(exec_file_directory);
# print %x(ls);
# configure gemset name
version = %x(cat .ruby-version).strip;
gemset = %x(cat .ruby-gemset).strip;
change_rvm_gemset = %Q(rvm use #{version}\##{gemset});
ruby_setup = RubySetup()
exec_bash_login_line = [ruby_setup, change_rvm_gemset, exec_bundle].join ' && ';
puts 'exec bash login line: ' + exec_bash_login_line
forced = %Q(bash --login -c '#{exec_bash_login_line}');
puts forced, "\n";
puts %x(#{forced});
ok, this script is not a kind of beauty, but it works well.
Example of usage?
ruby script.rb ~/bla/bla/bla/run_your_program.rb --first_argument --second_argument a,b,c --etc
As I said before:
I've already on the server via ssh.
So, I need to run scripts via launchd.
And I should do it with
# part of launchd worker
<string>bash</string>
<string>-c</string>
<string>ruby ~/PathToCharmScript.rb -r a</string>
P.S:
Please, help me with improvements of this script for others!
Here is a gist: wow_this_works
I have a password like 'X&Y' and I am trying to run a Ruby script that opens an SSH session, but the script breaks at the & character like :
*server: X
*server: bash: Y: command not found
Escaping the character like & doesn't help either. Ideas appreciated!
The code where it happens is at the ssh.exec:
pass="X\&Y"
Net::SSH.start( host_name, user, :password => pass ) do |ssh|
#do stuff
command = "sudo -S rm file"
cmd = "#{pass}|#{command}"
ssh.exec(cmd) do |ch, stream, data|
puts "*server:" + data.inspect
end
end
You can use ssh like without & getting any special meaning:
ssh -t -t user#localhost "echo 'abc&def'"
abc&def
Connection to localhost closed.
Good day everyone
I need to execute command on linux machine this command is interactive
command.
Interactive command means require input like [yes , no] or password
twice
my real case is.
I create a script execute commands and get the outputs successfully.
but some servers has Loging password expired so I need to interact with
server to send the current password + the new password(twice)
ssh userName#10.0.0.243
userName#10.0.0.243's password:
You are required to change your password immediately (password aged)
Last login: Sun Aug 7 13:15:40 2011 from 10.0.0.28
WARNING: Your password has expired.
You must change your password now and login again!
Changing password for user userName.
Changing password for userName
(current) UNIX password:
New UNIX password:
Retype new UNIX password:
Notes::
I'm using Ruby 1.9.2
I've no problem in executing command in normal case
please, I have to avoid workarounds like (echo "pass" | ssh -S) to make me pass any other interactive situations.
I'm using 'net/ssh' libs
The Script is Attached http://king-sabri.net/files/LinuxHWScanner.rb
I tried "net/ssh/telnet" and it doesn't help
Some advises tell useing 'rake/remote_task' is the solution but I can't understand how it works in my case
if you need more simplicity, here a simple code if you make it works I thing it'll solve my previous issue
require 'net/ssh'
host = "10.0.0.106"
port = 22 # SSH port
user = 'root' # username
pass = "123123" # password
Net::SSH.start( host,user,:password => pass, :port=> port , :verbose => :error ) do |session|
puts session.exec!("passwd root")
end
Something like this?
Net::SSH.start('10.0.0.6', 'not_root', :password => "test") do |ssh|
ssh.open_channel do |channel|
channel.on_request "exit-status" do |channel, data|
$exit_status = data.read_long
end
channel.exec("passwd") do |channel, success|
if success
channel.on_data do |channel, data|
# Don't really need this callback actually
puts "got data: #{data.inspect}"
end
# You don't need this line if you're root
channel.send_data("oldpass\n")
channel.send_data("newpass\n")
channel.send_data("newpass\n")
else
puts "FAILED"
end
end
channel.wait
puts "SUCCESS" if $exit_status == 0
end
end
This one is dirty, but working for both upon premature on-expiration prompt and upon passwd issuing:
Net::SSH.start('localhost', 'not_root', :password => "test") do |ssh|
ssh.open_channel do |channel|
channel.on_request "exit-status" do |channel, data|
$exit_status = data.read_long
end
channel.on_data do |channel, data|
puts data.inspect
if data.inspect.include? "current"
channel.send_data("oldpass\n");
elsif data.inspect.include? "new"
channel.send_data("newpass\n");
end
end
channel.request_pty
# This will just safely fail if we have already a password prompt on
channel.exec("passwd");
channel.wait
# Will reflect a valid status in both cases
puts $exit_status
end
end
I want to ask users to type in a password, but I don't want the chars to appear on screen as they type.
How do I do this in Ruby?
You can use the STDIN.noecho method from the IO/console module:
require 'io/console'
pw = STDIN.noecho(&:gets).chomp
If you're on a system with stty:
`stty -echo`
print "Password: "
pw = gets.chomp
`stty echo`
puts ""
There is a gem for such user interaction: highline.
password = ask("Password: ") { |q| q.echo = false }
Or even:
password = ask("Password: ") { |q| q.echo = "*" }
You want to make sure your code is idempotent... other solutions listed here assume you want to exit this chunk of functionality with echo turned back on. Well, what if it was turned off before entering the code, and it's expected to stay off?
stty_settings = %x[stty -g]
print 'Password: '
begin
%x[stty -echo]
password = gets
ensure
%x[stty #{stty_settings}]
end
puts
print 'regular info: '
regular_info = gets
puts "password: #{password}"
puts "regular: #{regular_info}"
This is solution for UNIX systems:
begin
system "stty -echo"
print "Password: "; pass1 = $stdin.gets.chomp; puts "\n"
print "Password (repeat): "; pass2 = $stdin.gets.chomp; puts "\n"
if pass1 == pass2
# DO YOUR WORK HERE
else
STDERR.puts "Passwords do not match!"
end
ensure
system "stty echo"
end
Similar answer as glenn but more complete: http://dmathieu.com/articles/development/ruby-console-ask-for-a-password/
I am looking to execute a password change over Net-ssh and this code seems to hang:
Net::SSH.start(server_ip, "user", :verbose => :debug ) do |session|
session.process.popen3("ls") do |input, output, error|
["old_pass","test", "test"].each do |x|
input.puts x
end
end
end
I know the connection works because using a simple exec I can get the output from ls on the remote server, but this hangs.
Any ideas?
The last message from debug is that the public key succeeded.
This one will solve your issue ,, note this script to change password for a list of servers list in file
#~~~~~~~~~~~~~~~~~~~~~~~
# Change Password is a simple script to change the password for a list of servers
# Coded by : Sabry Saleh
# License : GPL2
#~~~~~~~~~~~~~~~~~~~~~~~
#=-Notes-=
# You have to install ruby + net-ssh gems
# sudo gem install net-ssh
#~~~~~~~~~~~~~~~~~~~~~~~
require 'net/ssh'
host = IO.readlines('test1.txt') # full path of servers' list
port = 22 # SSH port
user = 'username' # username
i = 0
while i < host.length
Net::SSH.start(host[i], user , :password => "User pass" , :port=> port) do |ssh|
ssh.open_channel do |channel|
channel.on_request "exit-status" do |channel, data|
$exit_status = data.read_long
end
channel.request_pty do |channel, success|
channel.exec("sudo passwd UserName") # Logged user shuold be root or sudoers memeber
if success
channel.on_data do |channel, data|
puts data.inspect.chomp("\r\n")
channel.send_data("New pass\n") # put the New password you need to set
sleep 0.1
end
else
puts "FAILED!!"
end
end
channel.wait
puts "SUCCESS!!" if $exit_status == 0
end
end
i += 1
end