knife-windows 1.0.0.rc.1 → 1.0.0.rc.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +5 -5
  3. data/.travis.yml +20 -20
  4. data/CHANGELOG.md +75 -74
  5. data/DOC_CHANGES.md +323 -323
  6. data/Gemfile +12 -12
  7. data/LICENSE +201 -201
  8. data/README.md +393 -292
  9. data/RELEASE_NOTES.md +79 -74
  10. data/Rakefile +21 -16
  11. data/appveyor.yml +42 -42
  12. data/ci.gemfile +15 -15
  13. data/features/knife_help.feature +20 -20
  14. data/features/support/env.rb +5 -5
  15. data/knife-windows.gemspec +28 -28
  16. data/lib/chef/knife/bootstrap/windows-chef-client-msi.erb +247 -241
  17. data/lib/chef/knife/bootstrap_windows_base.rb +388 -368
  18. data/lib/chef/knife/bootstrap_windows_ssh.rb +110 -110
  19. data/lib/chef/knife/bootstrap_windows_winrm.rb +102 -113
  20. data/lib/chef/knife/core/windows_bootstrap_context.rb +361 -362
  21. data/lib/chef/knife/knife_windows_base.rb +33 -0
  22. data/lib/chef/knife/windows_cert_generate.rb +155 -155
  23. data/lib/chef/knife/windows_cert_install.rb +68 -68
  24. data/lib/chef/knife/windows_helper.rb +36 -36
  25. data/lib/chef/knife/windows_listener_create.rb +107 -107
  26. data/lib/chef/knife/winrm.rb +212 -191
  27. data/lib/chef/knife/winrm_base.rb +118 -125
  28. data/lib/chef/knife/winrm_knife_base.rb +218 -201
  29. data/lib/chef/knife/winrm_session.rb +80 -71
  30. data/lib/chef/knife/winrm_shared_options.rb +47 -47
  31. data/lib/chef/knife/wsman_endpoint.rb +44 -44
  32. data/lib/chef/knife/wsman_test.rb +96 -96
  33. data/lib/knife-windows/path_helper.rb +234 -234
  34. data/lib/knife-windows/version.rb +6 -6
  35. data/spec/assets/win_template_rendered_with_bootstrap_install_command.txt +217 -0
  36. data/spec/assets/win_template_rendered_without_bootstrap_install_command.txt +329 -0
  37. data/spec/assets/win_template_unrendered.txt +246 -0
  38. data/spec/functional/bootstrap_download_spec.rb +216 -140
  39. data/spec/spec_helper.rb +87 -72
  40. data/spec/unit/knife/bootstrap_options_spec.rb +146 -146
  41. data/spec/unit/knife/bootstrap_template_spec.rb +92 -92
  42. data/spec/unit/knife/bootstrap_windows_winrm_spec.rb +240 -161
  43. data/spec/unit/knife/core/windows_bootstrap_context_spec.rb +151 -101
  44. data/spec/unit/knife/windows_cert_generate_spec.rb +90 -90
  45. data/spec/unit/knife/windows_cert_install_spec.rb +51 -51
  46. data/spec/unit/knife/windows_listener_create_spec.rb +76 -76
  47. data/spec/unit/knife/winrm_session_spec.rb +55 -46
  48. data/spec/unit/knife/winrm_spec.rb +504 -376
  49. data/spec/unit/knife/wsman_test_spec.rb +175 -175
  50. metadata +28 -8
@@ -1,92 +1,92 @@
1
- #
2
- # Author:: Chirag Jog (<chirag@clogeny.com>)
3
- # Copyright:: Copyright (c) 2013 Chirag Jog
4
- # License:: Apache License, Version 2.0
5
- #
6
- # Licensed under the Apache License, Version 2.0 (the "License");
7
- # you may not use this file except in compliance with the License.
8
- # You may obtain a copy of the License at
9
- #
10
- # http://www.apache.org/licenses/LICENSE-2.0
11
- #
12
- # Unless required by applicable law or agreed to in writing, software
13
- # distributed under the License is distributed on an "AS IS" BASIS,
14
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
- # See the License for the specific language governing permissions and
16
- # limitations under the License.
17
-
18
-
19
- TEMPLATE_FILE = File.expand_path(File.dirname(__FILE__)) + "/../../../lib/chef/knife/bootstrap/windows-chef-client-msi.erb"
20
-
21
- require 'spec_helper'
22
-
23
- describe Chef::Knife::BootstrapWindowsWinrm do
24
- let(:template_file) { TEMPLATE_FILE }
25
- let(:options) { [] }
26
- let(:rendered_template) do
27
- knife.instance_variable_set("@template_file", template_file)
28
- knife.parse_options(options)
29
- # Avoid referencing a validation keyfile we won't find during #render_template
30
- template = IO.read(template_file).chomp
31
- knife.render_template(template)
32
- end
33
-
34
- before(:all) do
35
- @original_config = Chef::Config.hash_dup
36
- @original_knife_config = Chef::Config[:knife].dup
37
- end
38
-
39
- after(:all) do
40
- Chef::Config.configuration = @original_config
41
- Chef::Config[:knife] = @original_knife_config
42
- end
43
-
44
- before(:each) do
45
- Chef::Log.logger = Logger.new(StringIO.new)
46
- @knife = Chef::Knife::BootstrapWindowsWinrm.new
47
- # Merge default settings in.
48
- @knife.merge_configs
49
- @knife.config[:template_file] = template_file
50
- @stdout = StringIO.new
51
- allow(@knife.ui).to receive(:stdout).and_return(@stdout)
52
- @stderr = StringIO.new
53
- allow(@knife.ui).to receive(:stderr).and_return(@stderr)
54
- end
55
-
56
- describe "specifying no_proxy with various entries" do
57
- subject(:knife) { described_class.new }
58
- let(:options){ ["--bootstrap-proxy", "", "--bootstrap-no-proxy", setting] }
59
-
60
- context "via --bootstrap-no-proxy" do
61
- let(:setting) { "api.opscode.com" }
62
-
63
- it "renders the client.rb with a single FQDN no_proxy entry" do
64
- expect(rendered_template).to match(%r{.*no_proxy\s*\"api.opscode.com\".*})
65
- end
66
- end
67
- context "via --bootstrap-no-proxy multiple" do
68
- let(:setting) { "api.opscode.com,172.16.10.*" }
69
-
70
- it "renders the client.rb with comma-separated FQDN and wildcard IP address no_proxy entries" do
71
- expect(rendered_template).to match(%r{.*no_proxy\s*"api.opscode.com,172.16.10.\*".*})
72
- end
73
- end
74
- end
75
-
76
- describe "specifying --msi-url" do
77
- subject(:knife) { described_class.new }
78
-
79
- context "with explicitly provided --msi-url" do
80
- let(:options) { ["--msi-url", "file:///something.msi"] }
81
-
82
- it "bootstrap batch file must fetch from provided url" do
83
- expect(rendered_template).to match(%r{.*REMOTE_SOURCE_MSI_URL=file:///something\.msi.*})
84
- end
85
- end
86
- context "with no provided --msi-url" do
87
- it "bootstrap batch file must fetch from provided url" do
88
- expect(rendered_template).to match(%r{.*REMOTE_SOURCE_MSI_URL=https://www\.chef\.io/.*})
89
- end
90
- end
91
- end
92
- end
1
+ #
2
+ # Author:: Chirag Jog (<chirag@clogeny.com>)
3
+ # Copyright:: Copyright (c) 2013 Chirag Jog
4
+ # License:: Apache License, Version 2.0
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+
18
+
19
+ TEMPLATE_FILE = File.expand_path(File.dirname(__FILE__)) + "/../../../lib/chef/knife/bootstrap/windows-chef-client-msi.erb"
20
+
21
+ require 'spec_helper'
22
+
23
+ describe Chef::Knife::BootstrapWindowsWinrm do
24
+ let(:template_file) { TEMPLATE_FILE }
25
+ let(:options) { [] }
26
+ let(:rendered_template) do
27
+ knife.instance_variable_set("@template_file", template_file)
28
+ knife.parse_options(options)
29
+ # Avoid referencing a validation keyfile we won't find during #render_template
30
+ template = IO.read(template_file).chomp
31
+ knife.render_template(template)
32
+ end
33
+
34
+ before(:all) do
35
+ @original_config = Chef::Config.hash_dup
36
+ @original_knife_config = Chef::Config[:knife].dup
37
+ end
38
+
39
+ after(:all) do
40
+ Chef::Config.configuration = @original_config
41
+ Chef::Config[:knife] = @original_knife_config
42
+ end
43
+
44
+ before(:each) do
45
+ Chef::Log.logger = Logger.new(StringIO.new)
46
+ @knife = Chef::Knife::BootstrapWindowsWinrm.new
47
+ # Merge default settings in.
48
+ @knife.merge_configs
49
+ @knife.config[:template_file] = template_file
50
+ @stdout = StringIO.new
51
+ allow(@knife.ui).to receive(:stdout).and_return(@stdout)
52
+ @stderr = StringIO.new
53
+ allow(@knife.ui).to receive(:stderr).and_return(@stderr)
54
+ end
55
+
56
+ describe "specifying no_proxy with various entries" do
57
+ subject(:knife) { described_class.new }
58
+ let(:options){ ["--bootstrap-proxy", "", "--bootstrap-no-proxy", setting] }
59
+
60
+ context "via --bootstrap-no-proxy" do
61
+ let(:setting) { "api.opscode.com" }
62
+
63
+ it "renders the client.rb with a single FQDN no_proxy entry" do
64
+ expect(rendered_template).to match(%r{.*no_proxy\s*\"api.opscode.com\".*})
65
+ end
66
+ end
67
+ context "via --bootstrap-no-proxy multiple" do
68
+ let(:setting) { "api.opscode.com,172.16.10.*" }
69
+
70
+ it "renders the client.rb with comma-separated FQDN and wildcard IP address no_proxy entries" do
71
+ expect(rendered_template).to match(%r{.*no_proxy\s*"api.opscode.com,172.16.10.\*".*})
72
+ end
73
+ end
74
+ end
75
+
76
+ describe "specifying --msi-url" do
77
+ subject(:knife) { described_class.new }
78
+
79
+ context "with explicitly provided --msi-url" do
80
+ let(:options) { ["--msi-url", "file:///something.msi"] }
81
+
82
+ it "bootstrap batch file must fetch from provided url" do
83
+ expect(rendered_template).to match(%r{.*REMOTE_SOURCE_MSI_URL=file:///something\.msi.*})
84
+ end
85
+ end
86
+ context "with no provided --msi-url" do
87
+ it "bootstrap batch file must fetch from provided url" do
88
+ expect(rendered_template).to match(%r{.*REMOTE_SOURCE_MSI_URL=https://www\.chef\.io/.*})
89
+ end
90
+ end
91
+ end
92
+ end
@@ -1,161 +1,240 @@
1
- #
2
- # Author:: Adam Edwards(<adamed@getchef.com>)
3
- # Copyright:: Copyright (c) 2014 Chef Software, Inc.
4
- # License:: Apache License, Version 2.0
5
- #
6
- # Licensed under the Apache License, Version 2.0 (the "License");
7
- # you may not use this file except in compliance with the License.
8
- # You may obtain a copy of the License at
9
- #
10
- # http://www.apache.org/licenses/LICENSE-2.0
11
- #
12
- # Unless required by applicable law or agreed to in writing, software
13
- # distributed under the License is distributed on an "AS IS" BASIS,
14
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
- # See the License for the specific language governing permissions and
16
- # limitations under the License.
17
- #
18
-
19
- require 'spec_helper'
20
-
21
- Chef::Knife::Winrm.load_deps
22
-
23
- describe Chef::Knife::BootstrapWindowsWinrm do
24
- before(:all) do
25
- Chef::Config.reset
26
- end
27
-
28
- before do
29
- # Kernel.stub(:sleep).and_return 10
30
- allow(bootstrap).to receive(:sleep).and_return(10)
31
- allow(File).to receive(:exist?).with(File.expand_path(Chef::Config[:validation_key])).and_return(true)
32
- end
33
-
34
- after do
35
- # Kernel.unstub(:sleep)
36
- allow(bootstrap).to receive(:sleep).and_return(10)
37
- end
38
-
39
- let(:bootstrap) { Chef::Knife::BootstrapWindowsWinrm.new(['winrm', '-d', 'windows-chef-client-msi', '-x', 'Administrator', 'localhost']) }
40
- let(:session) { Chef::Knife::Winrm::WinrmSession.new({ :host => 'winrm.cloudapp.net', :port => '5986', :transport => :ssl }) }
41
-
42
- let(:initial_fail_count) { 4 }
43
-
44
- it 'should retry if a 401 is received from WinRM' do
45
- call_result_sequence = Array.new(initial_fail_count) {lambda {raise WinRM::WinRMHTTPTransportError.new('', '401')}}
46
- call_result_sequence.push(0)
47
- allow(bootstrap).to receive(:run_command).and_return(*call_result_sequence)
48
- allow(bootstrap).to receive(:print)
49
- allow(bootstrap.ui).to receive(:info)
50
-
51
- expect(bootstrap).to receive(:run_command).exactly(call_result_sequence.length).times
52
- bootstrap.send(:wait_for_remote_response, 2)
53
- end
54
-
55
- it 'should retry if something other than a 401 is received from WinRM' do
56
- call_result_sequence = Array.new(initial_fail_count) {lambda {raise WinRM::WinRMHTTPTransportError.new('', '500')}}
57
- call_result_sequence.push(0)
58
- allow(bootstrap).to receive(:run_command).and_return(*call_result_sequence)
59
- allow(bootstrap).to receive(:print)
60
- allow(bootstrap.ui).to receive(:info)
61
-
62
- expect(bootstrap).to receive(:run_command).exactly(call_result_sequence.length).times
63
- bootstrap.send(:wait_for_remote_response, 2)
64
- end
65
-
66
- it 'should keep retrying at 10s intervals if the timeout in minutes has not elapsed' do
67
- call_result_sequence = Array.new(initial_fail_count) {lambda {raise WinRM::WinRMHTTPTransportError.new('', '500')}}
68
- call_result_sequence.push(0)
69
- allow(bootstrap).to receive(:run_command).and_return(*call_result_sequence)
70
- allow(bootstrap).to receive(:print)
71
- allow(bootstrap.ui).to receive(:info)
72
-
73
- expect(bootstrap).to receive(:run_command).exactly(call_result_sequence.length).times
74
- bootstrap.send(:wait_for_remote_response, 2)
75
- end
76
-
77
- it 'should have a wait timeout of 2 minutes by default' do
78
- allow(bootstrap).to receive(:run_command).and_raise(WinRM::WinRMHTTPTransportError.new('','500'))
79
- allow(bootstrap).to receive(:create_bootstrap_bat_command).and_raise(SystemExit)
80
- expect(bootstrap).to receive(:wait_for_remote_response).with(2)
81
- allow(bootstrap).to receive(:validate_name_args!).and_return(nil)
82
- allow(bootstrap.ui).to receive(:info)
83
- bootstrap.config[:auth_timeout] = bootstrap.options[:auth_timeout][:default]
84
- expect { bootstrap.bootstrap }.to raise_error(SystemExit)
85
- end
86
-
87
- it 'should not a wait for timeout on Errno::ECONNREFUSED' do
88
- allow(bootstrap).to receive(:run_command).and_raise(Errno::ECONNREFUSED.new)
89
- allow(bootstrap.ui).to receive(:info)
90
- bootstrap.config[:auth_timeout] = bootstrap.options[:auth_timeout][:default]
91
- expect(bootstrap.ui).to receive(:error).with("Connection refused connecting to localhost:5985.")
92
-
93
- # wait_for_remote_response is protected method, So define singleton test method to call it.
94
- bootstrap.define_singleton_method(:test_wait_for_remote_response){wait_for_remote_response(bootstrap.options[:auth_timeout][:default])}
95
- expect { bootstrap.test_wait_for_remote_response }.to raise_error(Errno::ECONNREFUSED)
96
- end
97
-
98
- it "should exit bootstrap with non-zero status if the bootstrap fails" do
99
- command_status = 1
100
-
101
- #Stub out calls to create the session and just get the exit codes back
102
- winrm_mock = Chef::Knife::Winrm.new
103
- allow(Chef::Knife::Winrm).to receive(:new).and_return(winrm_mock)
104
- allow(winrm_mock).to receive(:run).and_raise(SystemExit.new(command_status))
105
- #Skip over templating stuff and checking with the remote end
106
- allow(bootstrap).to receive(:create_bootstrap_bat_command)
107
- allow(bootstrap).to receive(:wait_for_remote_response)
108
- allow(bootstrap.ui).to receive(:info)
109
-
110
- expect { bootstrap.run_with_pretty_exceptions }.to raise_error(SystemExit) { |e| expect(e.status).to eq(command_status) }
111
- end
112
-
113
- it 'should stop retrying if more than 2 minutes has elapsed' do
114
- times = [ Time.new(2014, 4, 1, 22, 25), Time.new(2014, 4, 1, 22, 51), Time.new(2014, 4, 1, 22, 28) ]
115
- allow(Time).to receive(:now).and_return(*times)
116
- run_command_result = lambda {raise WinRM::WinRMHTTPTransportError, '401'}
117
- allow(bootstrap).to receive(:validate_name_args!).and_return(nil)
118
- allow(bootstrap).to receive(:run_command).and_return(run_command_result)
119
- allow(bootstrap).to receive(:print)
120
- allow(bootstrap.ui).to receive(:info)
121
- allow(bootstrap.ui).to receive(:error)
122
- expect(bootstrap).to receive(:run_command).exactly(1).times
123
- bootstrap.config[:auth_timeout] = bootstrap.options[:auth_timeout][:default]
124
- expect { bootstrap.bootstrap }.to raise_error RuntimeError
125
- end
126
-
127
- context "when validation_key is not present" do
128
- context "using chef 11", :chef_lt_12_only do
129
- before do
130
- allow(File).to receive(:exist?).with(File.expand_path(Chef::Config[:validation_key])).and_return(false)
131
- end
132
-
133
- it 'raises an exception if validation_key is not present in chef 11' do
134
- expect(bootstrap.ui).to receive(:error)
135
- expect { bootstrap.bootstrap }.to raise_error(SystemExit)
136
- end
137
- end
138
-
139
- context "using chef 12", :chef_gte_12_only do
140
- before do
141
- allow(File).to receive(:exist?).with(File.expand_path(Chef::Config[:validation_key])).and_return(false)
142
- bootstrap.client_builder = instance_double("Chef::Knife::Bootstrap::ClientBuilder", :run => nil, :client_path => nil)
143
- Chef::Config[:knife] = {:chef_node_name => 'foo.example.com'}
144
- end
145
-
146
- it 'raises an exception if winrm_authentication_protocol is basic and transport is plaintext' do
147
- Chef::Config[:knife] = {:winrm_authentication_protocol => 'basic', :winrm_transport => 'plaintext', :chef_node_name => 'foo.example.com'}
148
- expect(bootstrap.ui).to receive(:error)
149
- expect { bootstrap.run }.to raise_error(SystemExit)
150
- end
151
-
152
- it 'raises an exception if chef_node_name is not present ' do
153
- Chef::Config[:knife] = {:chef_node_name => nil}
154
- expect(bootstrap.client_builder).not_to receive(:run)
155
- expect(bootstrap.client_builder).not_to receive(:client_path)
156
- expect(bootstrap.ui).to receive(:error)
157
- expect { bootstrap.bootstrap }.to raise_error(SystemExit)
158
- end
159
- end
160
- end
161
- end
1
+ #
2
+ # Author:: Adam Edwards(<adamed@getchef.com>)
3
+ # Copyright:: Copyright (c) 2014 Chef Software, Inc.
4
+ # License:: Apache License, Version 2.0
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+
19
+ require 'spec_helper'
20
+
21
+ Chef::Knife::Winrm.load_deps
22
+
23
+ describe Chef::Knife::BootstrapWindowsWinrm do
24
+ before(:all) do
25
+ Chef::Config.reset
26
+ end
27
+
28
+ before do
29
+ # Kernel.stub(:sleep).and_return 10
30
+ allow(bootstrap).to receive(:sleep).and_return(10)
31
+ allow(File).to receive(:exist?).with(File.expand_path(Chef::Config[:validation_key])).and_return(true)
32
+ end
33
+
34
+ after do
35
+ # Kernel.unstub(:sleep)
36
+ allow(bootstrap).to receive(:sleep).and_return(10)
37
+ end
38
+
39
+ let(:bootstrap) { Chef::Knife::BootstrapWindowsWinrm.new(['winrm', '-d', 'windows-chef-client-msi', '-x', 'Administrator', 'localhost']) }
40
+ let(:session) { Chef::Knife::Winrm::WinrmSession.new({ :host => 'winrm.cloudapp.net', :port => '5986', :transport => :ssl }) }
41
+
42
+ let(:initial_fail_count) { 4 }
43
+
44
+ context "knife secret-file && knife secret options are passed" do
45
+ before do
46
+ Chef::Config.reset
47
+ Chef::Config[:knife][:encrypted_data_bag_secret_file] = "/tmp/encrypted_data_bag_secret"
48
+ Chef::Config[:knife][:encrypted_data_bag_secret] = "data_bag_secret_key_passed_under_knife_secret_option"
49
+ end
50
+ it "gives preference to secret key passed under knife's secret-file option" do
51
+ allow(Chef::EncryptedDataBagItem).to receive(:load_secret).with(
52
+ Chef::Config[:knife][:encrypted_data_bag_secret_file]).
53
+ and_return("data_bag_secret_key_passed_under_knife_secret_file_option")
54
+ expect(bootstrap.load_correct_secret).to eq(
55
+ "data_bag_secret_key_passed_under_knife_secret_file_option")
56
+ end
57
+ end
58
+
59
+ context "cli secret-file && cli secret options are passed" do
60
+ before do
61
+ Chef::Config.reset
62
+ bootstrap.config[:encrypted_data_bag_secret_file] = "/tmp/encrypted_data_bag_secret"
63
+ bootstrap.config[:encrypted_data_bag_secret] = "data_bag_secret_key_passed_under_cli_secret_option"
64
+ end
65
+ it "gives preference to secret key passed under cli's secret-file option" do
66
+ allow(Chef::EncryptedDataBagItem).to receive(:load_secret).with(
67
+ bootstrap.config[:encrypted_data_bag_secret_file]).
68
+ and_return("data_bag_secret_key_passed_under_cli_secret_file_option")
69
+ expect(bootstrap.load_correct_secret).to eq(
70
+ "data_bag_secret_key_passed_under_cli_secret_file_option")
71
+ end
72
+ end
73
+
74
+ context "knife secret-file, knife secret, cli secret-file && cli secret options are passed" do
75
+ before do
76
+ Chef::Config.reset
77
+ Chef::Config[:knife][:encrypted_data_bag_secret_file] = "/tmp/knife_encrypted_data_bag_secret"
78
+ Chef::Config[:knife][:encrypted_data_bag_secret] = "data_bag_secret_key_passed_under_knife_secret_option"
79
+ bootstrap.config[:encrypted_data_bag_secret_file] = "/tmp/cli_encrypted_data_bag_secret"
80
+ bootstrap.config[:encrypted_data_bag_secret] = "data_bag_secret_key_passed_under_cli_secret_option"
81
+ end
82
+ it "gives preference to secret key passed under cli's secret-file option" do
83
+ allow(Chef::EncryptedDataBagItem).to receive(:load_secret).with(
84
+ Chef::Config[:knife][:encrypted_data_bag_secret_file]).
85
+ and_return("data_bag_secret_key_passed_under_knife_secret_file_option")
86
+ allow(Chef::EncryptedDataBagItem).to receive(:load_secret).with(
87
+ bootstrap.config[:encrypted_data_bag_secret_file]).
88
+ and_return("data_bag_secret_key_passed_under_cli_secret_file_option")
89
+ expect(bootstrap.load_correct_secret).to eq(
90
+ "data_bag_secret_key_passed_under_cli_secret_file_option")
91
+ end
92
+ end
93
+
94
+ context "knife secret-file && cli secret options are passed" do
95
+ before do
96
+ Chef::Config.reset
97
+ Chef::Config[:knife][:encrypted_data_bag_secret_file] = "/tmp/encrypted_data_bag_secret"
98
+ bootstrap.config[:encrypted_data_bag_secret] = "data_bag_secret_key_passed_under_cli_secret_option"
99
+ end
100
+ it "gives preference to secret key passed under cli's secret option" do
101
+ allow(Chef::EncryptedDataBagItem).to receive(:load_secret).with(
102
+ Chef::Config[:knife][:encrypted_data_bag_secret_file]).
103
+ and_return("data_bag_secret_key_passed_under_knife_secret_file_option")
104
+ expect(bootstrap.load_correct_secret).to eq(
105
+ "data_bag_secret_key_passed_under_cli_secret_option")
106
+ end
107
+ end
108
+
109
+ context "knife secret && cli secret-file options are passed" do
110
+ before do
111
+ Chef::Config.reset
112
+ Chef::Config[:knife][:encrypted_data_bag_secret] = "data_bag_secret_key_passed_under_knife_secret_option"
113
+ bootstrap.config[:encrypted_data_bag_secret_file] = "/tmp/encrypted_data_bag_secret"
114
+ end
115
+ it "gives preference to secret key passed under cli's secret-file option" do
116
+ allow(Chef::EncryptedDataBagItem).to receive(:load_secret).with(
117
+ bootstrap.config[:encrypted_data_bag_secret_file]).
118
+ and_return("data_bag_secret_key_passed_under_cli_secret_file_option")
119
+ expect(bootstrap.load_correct_secret).to eq(
120
+ "data_bag_secret_key_passed_under_cli_secret_file_option")
121
+ end
122
+ end
123
+
124
+ context "cli secret-file option is passed" do
125
+ before do
126
+ Chef::Config.reset
127
+ bootstrap.config[:encrypted_data_bag_secret_file] = "/tmp/encrypted_data_bag_secret"
128
+ end
129
+ it "takes the secret key passed under cli's secret-file option" do
130
+ allow(Chef::EncryptedDataBagItem).to receive(:load_secret).with(
131
+ bootstrap.config[:encrypted_data_bag_secret_file]).
132
+ and_return("data_bag_secret_key_passed_under_cli_secret_file_option")
133
+ expect(bootstrap.load_correct_secret).to eq(
134
+ "data_bag_secret_key_passed_under_cli_secret_file_option")
135
+ end
136
+ end
137
+
138
+ it 'should retry if a 401 is received from WinRM' do
139
+ call_result_sequence = Array.new(initial_fail_count) {lambda {raise WinRM::WinRMHTTPTransportError.new('', '401')}}
140
+ call_result_sequence.push(0)
141
+ allow(bootstrap).to receive(:run_command).and_return(*call_result_sequence)
142
+ allow(bootstrap).to receive(:print)
143
+ allow(bootstrap.ui).to receive(:info)
144
+
145
+ expect(bootstrap).to receive(:run_command).exactly(call_result_sequence.length).times
146
+ bootstrap.send(:wait_for_remote_response, 2)
147
+ end
148
+
149
+ it 'should retry if something other than a 401 is received from WinRM' do
150
+ call_result_sequence = Array.new(initial_fail_count) {lambda {raise WinRM::WinRMHTTPTransportError.new('', '500')}}
151
+ call_result_sequence.push(0)
152
+ allow(bootstrap).to receive(:run_command).and_return(*call_result_sequence)
153
+ allow(bootstrap).to receive(:print)
154
+ allow(bootstrap.ui).to receive(:info)
155
+
156
+ expect(bootstrap).to receive(:run_command).exactly(call_result_sequence.length).times
157
+ bootstrap.send(:wait_for_remote_response, 2)
158
+ end
159
+
160
+ it 'should keep retrying at 10s intervals if the timeout in minutes has not elapsed' do
161
+ call_result_sequence = Array.new(initial_fail_count) {lambda {raise WinRM::WinRMHTTPTransportError.new('', '500')}}
162
+ call_result_sequence.push(0)
163
+ allow(bootstrap).to receive(:run_command).and_return(*call_result_sequence)
164
+ allow(bootstrap).to receive(:print)
165
+ allow(bootstrap.ui).to receive(:info)
166
+
167
+ expect(bootstrap).to receive(:run_command).exactly(call_result_sequence.length).times
168
+ bootstrap.send(:wait_for_remote_response, 2)
169
+ end
170
+
171
+ it 'should have a wait timeout of 2 minutes by default' do
172
+ allow(bootstrap).to receive(:run_command).and_raise(WinRM::WinRMHTTPTransportError.new('','500'))
173
+ allow(bootstrap).to receive(:create_bootstrap_bat_command).and_raise(SystemExit)
174
+ expect(bootstrap).to receive(:wait_for_remote_response).with(2)
175
+ allow(bootstrap).to receive(:validate_name_args!).and_return(nil)
176
+ allow(bootstrap.ui).to receive(:info)
177
+ bootstrap.config[:auth_timeout] = bootstrap.options[:auth_timeout][:default]
178
+ expect { bootstrap.bootstrap }.to raise_error(SystemExit)
179
+ end
180
+
181
+ it 'should not a wait for timeout on Errno::ECONNREFUSED' do
182
+ allow(bootstrap).to receive(:run_command).and_raise(Errno::ECONNREFUSED.new)
183
+ allow(bootstrap.ui).to receive(:info)
184
+ bootstrap.config[:auth_timeout] = bootstrap.options[:auth_timeout][:default]
185
+ expect(bootstrap.ui).to receive(:error).with("Connection refused connecting to localhost:5985.")
186
+
187
+ # wait_for_remote_response is protected method, So define singleton test method to call it.
188
+ bootstrap.define_singleton_method(:test_wait_for_remote_response){wait_for_remote_response(bootstrap.options[:auth_timeout][:default])}
189
+ expect { bootstrap.test_wait_for_remote_response }.to raise_error(Errno::ECONNREFUSED)
190
+ end
191
+
192
+ it 'should stop retrying if more than 2 minutes has elapsed' do
193
+ times = [ Time.new(2014, 4, 1, 22, 25), Time.new(2014, 4, 1, 22, 51), Time.new(2014, 4, 1, 22, 28) ]
194
+ allow(Time).to receive(:now).and_return(*times)
195
+ run_command_result = lambda {raise WinRM::WinRMHTTPTransportError, '401'}
196
+ allow(bootstrap).to receive(:validate_name_args!).and_return(nil)
197
+ allow(bootstrap).to receive(:run_command).and_return(run_command_result)
198
+ allow(bootstrap).to receive(:print)
199
+ allow(bootstrap.ui).to receive(:info)
200
+ allow(bootstrap.ui).to receive(:error)
201
+ expect(bootstrap).to receive(:run_command).exactly(1).times
202
+ bootstrap.config[:auth_timeout] = bootstrap.options[:auth_timeout][:default]
203
+ expect { bootstrap.bootstrap }.to raise_error RuntimeError
204
+ end
205
+
206
+ context "when validation_key is not present" do
207
+ context "using chef 11", :chef_lt_12_only do
208
+ before do
209
+ allow(File).to receive(:exist?).with(File.expand_path(Chef::Config[:validation_key])).and_return(false)
210
+ end
211
+
212
+ it 'raises an exception if validation_key is not present in chef 11' do
213
+ expect(bootstrap.ui).to receive(:error)
214
+ expect { bootstrap.bootstrap }.to raise_error(SystemExit)
215
+ end
216
+ end
217
+
218
+ context "using chef 12", :chef_gte_12_only do
219
+ before do
220
+ allow(File).to receive(:exist?).with(File.expand_path(Chef::Config[:validation_key])).and_return(false)
221
+ bootstrap.client_builder = instance_double("Chef::Knife::Bootstrap::ClientBuilder", :run => nil, :client_path => nil)
222
+ Chef::Config[:knife] = {:chef_node_name => 'foo.example.com'}
223
+ end
224
+
225
+ it 'raises an exception if winrm_authentication_protocol is basic and transport is plaintext' do
226
+ Chef::Config[:knife] = {:winrm_authentication_protocol => 'basic', :winrm_transport => 'plaintext', :chef_node_name => 'foo.example.com'}
227
+ expect(bootstrap.ui).to receive(:error)
228
+ expect { bootstrap.run }.to raise_error(SystemExit)
229
+ end
230
+
231
+ it 'raises an exception if chef_node_name is not present ' do
232
+ Chef::Config[:knife] = {:chef_node_name => nil}
233
+ expect(bootstrap.client_builder).not_to receive(:run)
234
+ expect(bootstrap.client_builder).not_to receive(:client_path)
235
+ expect(bootstrap.ui).to receive(:error)
236
+ expect { bootstrap.bootstrap }.to raise_error(SystemExit)
237
+ end
238
+ end
239
+ end
240
+ end