summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAmol Shinde <amol.shinde@msystechnologies.com>2019-08-28 17:51:27 +0530
committerAmol Shinde <amol.shinde@msystechnologies.com>2019-09-11 17:25:44 +0530
commita8689c07ac5ab970048c75de2d5079fd4ade755a (patch)
tree6f478f683d14780b0f06d18ba5c89d2572de4a5a
parentdd0009d7e77f192cadb1632f2159a6dbc880980f (diff)
downloadchef-a8689c07ac5ab970048c75de2d5079fd4ade755a.tar.gz
Fix password prompt in the bootstrap command.
- Fix test-case for password prompt. Signed-off-by: Amol Shinde <amol.shinde@msystechnologies.com>
-rw-r--r--lib/chef/knife/bootstrap.rb32
-rw-r--r--spec/unit/knife/bootstrap_spec.rb56
2 files changed, 74 insertions, 14 deletions
diff --git a/lib/chef/knife/bootstrap.rb b/lib/chef/knife/bootstrap.rb
index e892f1f2c9..599419f5fa 100644
--- a/lib/chef/knife/bootstrap.rb
+++ b/lib/chef/knife/bootstrap.rb
@@ -616,7 +616,7 @@ class Chef
def connect!
ui.info("Connecting to #{ui.color(server_name, :bold)}")
- opts = connection_opts.dup
+ opts ||= connection_opts.dup
do_connect(opts)
rescue Train::Error => e
# We handle these by message text only because train only loads the
@@ -638,8 +638,10 @@ class Chef
EOM
# FIXME: this should save the key to known_hosts but doesn't appear to be
config[:ssh_verify_host_key] = :accept_new
- do_connect(connection_opts(reset: true))
- elsif ssh? && e.cause && e.cause.class == Net::SSH::AuthenticationFailed
+ conn_opts = connection_opts(reset: true)
+ opts.merge! conn_opts
+ retry
+ elsif (ssh? && e.cause && e.cause.class == Net::SSH::AuthenticationFailed) || (ssh? && e.class == Train::ClientError && e.reason == :no_ssh_password_or_key_available)
if connection.password_auth?
raise
else
@@ -650,7 +652,23 @@ class Chef
end
opts.merge! force_ssh_password_opts(password)
- do_connect(opts)
+ retry
+ else
+ raise
+ end
+ rescue RuntimeError => e
+ if winrm? && e.message == "password is a required option"
+ if connection.password_auth?
+ raise
+ else
+ ui.warn("Failed to authenticate #{opts[:user]} to #{server_name} - trying password auth")
+ password = ui.ask("Enter password for #{opts[:user]}@#{server_name}.") do |q|
+ q.echo = false
+ end
+ end
+
+ opts.merge! force_winrm_password_opts(password)
+ retry
else
raise
end
@@ -1016,6 +1034,12 @@ class Chef
}
end
+ def force_winrm_password_opts(password)
+ {
+ password: password,
+ }
+ end
+
# Looks up configuration entries, first in the class member
# `config` which contains options populated from CLI flags.
# If the entry is not found there, Chef::Config[:knife][KEY]
diff --git a/spec/unit/knife/bootstrap_spec.rb b/spec/unit/knife/bootstrap_spec.rb
index 5f4be8dfa2..0eb08d1dc9 100644
--- a/spec/unit/knife/bootstrap_spec.rb
+++ b/spec/unit/knife/bootstrap_spec.rb
@@ -1844,6 +1844,20 @@ describe Chef::Knife::Bootstrap do
e
end
+ let(:expected_error_password_prompt) do
+ e = Train::ClientError.new
+ message = "Your SSH Agent has no keys added, and you have not specified a password or a key file"
+ allow(e).to receive(:message).and_return(message)
+ e
+ end
+
+ let(:expected_error_password_prompt_winrm) do
+ e = RuntimeError.new
+ message = "password is a required option"
+ allow(e).to receive(:message).and_return(message)
+ e
+ end
+
context "and password auth was used" do
before do
allow(connection).to receive(:password_auth?).and_return true
@@ -1859,19 +1873,41 @@ describe Chef::Knife::Bootstrap do
before do
allow(connection).to receive(:password_auth?).and_return false
allow(connection).to receive(:user).and_return "testuser"
+ allow(knife).to receive(:connection_protocol).and_return connection_protocol
end
- it "warns, prompts for password, then reconnects with a password-enabled configuration using the new password" do
- question_mock = double("question")
- expect(knife).to receive(:do_connect).and_raise(expected_error)
- expect(knife.ui).to receive(:warn).with(/Failed to auth.*/)
- expect(knife.ui).to receive(:ask).and_yield(question_mock).and_return("newpassword")
- # Ensure that we set echo off to prevent showing password on the screen
- expect(question_mock).to receive(:echo=).with false
- expect(knife).to receive(:do_connect) do |opts|
- expect(opts[:password]).to eq "newpassword"
+ context "when using ssh" do
+ let(:connection_protocol) { "ssh" }
+
+ it "warns, prompts for password, then reconnects with a password-enabled configuration using the new password" do
+ question_mock = double("question")
+ expect(knife).to receive(:do_connect).and_raise(expected_error_password_prompt)
+ expect(knife.ui).to receive(:warn).with(/Failed to auth.*/)
+ expect(knife.ui).to receive(:ask).and_yield(question_mock).and_return("newpassword")
+ # Ensure that we set echo off to prevent showing password on the screen
+ expect(question_mock).to receive(:echo=).with false
+ expect(knife).to receive(:do_connect) do |opts|
+ expect(opts[:password]).to eq "newpassword"
+ end
+ knife.connect!
+ end
+ end
+
+ context "when using winrm" do
+ let(:connection_protocol) { "winrm" }
+
+ it "warns, prompts for password, then reconnects with a password-enabled configuration using the new password for" do
+ question_mock = double("question")
+ expect(knife).to receive(:do_connect).and_raise(expected_error_password_prompt_winrm)
+ expect(knife.ui).to receive(:warn).with(/Failed to auth.*/)
+ expect(knife.ui).to receive(:ask).and_yield(question_mock).and_return("newpassword")
+ # Ensure that we set echo off to prevent showing password on the screen
+ expect(question_mock).to receive(:echo=).with false
+ expect(knife).to receive(:do_connect) do |opts|
+ expect(opts[:password]).to eq "newpassword"
+ end
+ knife.connect!
end
- knife.connect!
end
end
end