diff options
author | Amol Shinde <amol.shinde@msystechnologies.com> | 2019-08-28 17:51:27 +0530 |
---|---|---|
committer | Amol Shinde <amol.shinde@msystechnologies.com> | 2019-09-11 17:25:44 +0530 |
commit | a8689c07ac5ab970048c75de2d5079fd4ade755a (patch) | |
tree | 6f478f683d14780b0f06d18ba5c89d2572de4a5a | |
parent | dd0009d7e77f192cadb1632f2159a6dbc880980f (diff) | |
download | chef-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.rb | 32 | ||||
-rw-r--r-- | spec/unit/knife/bootstrap_spec.rb | 56 |
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 |