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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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