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
@@ -0,0 +1,246 @@
1
+ @rem
2
+ @rem Author:: Seth Chisamore (<schisamo@opscode.com>)
3
+ @rem Copyright:: Copyright (c) 2011 Opscode, Inc.
4
+ @rem License:: Apache License, Version 2.0
5
+ @rem
6
+ @rem Licensed under the Apache License, Version 2.0 (the "License");
7
+ @rem you may not use this file except in compliance with the License.
8
+ @rem You may obtain a copy of the License at
9
+ @rem
10
+ @rem http://www.apache.org/licenses/LICENSE-2.0
11
+ @rem
12
+ @rem Unless required by applicable law or agreed to in writing, software
13
+ @rem distributed under the License is distributed on an "AS IS" BASIS,
14
+ @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ @rem See the License for the specific language governing permissions and
16
+ @rem limitations under the License.
17
+ @rem
18
+
19
+ @rem Use delayed environment expansion so that ERRORLEVEL can be evaluated with the
20
+ @rem !ERRORLEVEL! syntax which evaluates at execution of the line of script, not when
21
+ @rem the line is read. See help for the /E switch from cmd.exe /? .
22
+ @setlocal ENABLEDELAYEDEXPANSION
23
+
24
+ <%= "SETX HTTP_PROXY \"#{knife_config[:bootstrap_proxy]}\"" if knife_config[:bootstrap_proxy] %>
25
+
26
+ @set BOOTSTRAP_DIRECTORY=<%= bootstrap_directory %>
27
+ @echo Checking for existing directory "%BOOTSTRAP_DIRECTORY%"...
28
+ @if NOT EXIST %BOOTSTRAP_DIRECTORY% (
29
+ @echo Existing directory not found, creating.
30
+ @mkdir %BOOTSTRAP_DIRECTORY%
31
+ ) else (
32
+ @echo Existing directory found, skipping creation.
33
+ )
34
+
35
+ > <%= bootstrap_directory %>\wget.vbs (
36
+ <%= win_wget %>
37
+ )
38
+
39
+ > <%= bootstrap_directory %>\wget.ps1 (
40
+ <%= win_wget_ps %>
41
+ )
42
+
43
+ @rem Determine the version and the architecture
44
+
45
+ @FOR /F "usebackq tokens=1-8 delims=.[] " %%A IN (`ver`) DO (
46
+ @set WinMajor=%%D
47
+ @set WinMinor=%%E
48
+ @set WinBuild=%%F
49
+ )
50
+
51
+ @echo Detected Windows Version %WinMajor%.%WinMinor% Build %WinBuild%
52
+
53
+ @set LATEST_OS_VERSION_MAJOR=6
54
+ @set LATEST_OS_VERSION_MINOR=3
55
+
56
+ @if /i %WinMajor% GTR %LATEST_OS_VERSION_MAJOR% goto VersionUnknown
57
+ @if /i %WinMajor% EQU %LATEST_OS_VERSION_MAJOR% (
58
+ @if /i %WinMinor% GTR %LATEST_OS_VERSION_MINOR% goto VersionUnknown
59
+ )
60
+
61
+ goto Version%WinMajor%.%WinMinor%
62
+
63
+ :VersionUnknown
64
+ @rem If this is an unknown version of windows set the default
65
+ @set MACHINE_OS=2008r2
66
+ @echo Warning: Unknown version of Windows, assuming default of Windows %MACHINE_OS%
67
+ goto architecture_select
68
+
69
+ :Version6.0
70
+ @set MACHINE_OS=2008
71
+ goto architecture_select
72
+
73
+ :Version5.2
74
+ @set MACHINE_OS=2003r2
75
+ goto architecture_select
76
+
77
+ :Version6.1
78
+ @set MACHINE_OS=2008r2
79
+ goto architecture_select
80
+
81
+ :Version6.2
82
+ @set MACHINE_OS=2012
83
+ goto architecture_select
84
+
85
+ @rem Currently Windows Server 2012 R2 is treated as equivalent to Windows Server 2012
86
+ :Version6.3
87
+ goto Version6.2
88
+
89
+ :architecture_select
90
+ goto Architecture%PROCESSOR_ARCHITEW6432%
91
+
92
+ :Architecture
93
+ goto Architecture%PROCESSOR_ARCHITECTURE%
94
+
95
+ @rem If this is an unknown architecture set the default
96
+ @set MACHINE_ARCH=i686
97
+ goto install
98
+
99
+ :Architecturex86
100
+ @set MACHINE_ARCH=i686
101
+ goto install
102
+
103
+ :Architectureamd64
104
+ @set MACHINE_ARCH=x86_64
105
+ goto install
106
+
107
+ :install
108
+ @rem If user has provided the custom installation command for chef-client then execute it
109
+ <% if @chef_config[:knife][:bootstrap_install_command] %>
110
+ <%= @chef_config[:knife][:bootstrap_install_command] %>
111
+ <% else %>
112
+ @rem Install Chef using chef-client MSI installer
113
+
114
+ @set "LOCAL_DESTINATION_MSI_PATH=<%= local_download_path %>"
115
+ @set "CHEF_CLIENT_MSI_LOG_PATH=%TEMP%\chef-client-msi%RANDOM%.log"
116
+
117
+ @rem Clear any pre-existing downloads
118
+ @echo Checking for existing downloaded package at "%LOCAL_DESTINATION_MSI_PATH%"
119
+ @if EXIST "%LOCAL_DESTINATION_MSI_PATH%" (
120
+ @echo Found existing downloaded package, deleting.
121
+ @del /f /q "%LOCAL_DESTINATION_MSI_PATH%"
122
+ @if ERRORLEVEL 1 (
123
+ echo Warning: Failed to delete pre-existing package with status code !ERRORLEVEL! > "&2"
124
+ )
125
+ ) else (
126
+ echo No existing downloaded packages to delete.
127
+ )
128
+
129
+ @rem If there is somehow a name collision, remove pre-existing log
130
+ @if EXIST "%CHEF_CLIENT_MSI_LOG_PATH%" del /f /q "%CHEF_CLIENT_MSI_LOG_PATH%"
131
+
132
+ @echo Attempting to download client package using PowerShell if available...
133
+ @set "REMOTE_SOURCE_MSI_URL=<%= msi_url('%MACHINE_OS%', '%MACHINE_ARCH%', 'PowerShell') %>"
134
+ @set powershell_download=powershell.exe -ExecutionPolicy Unrestricted -NoProfile -NonInteractive -File <%= bootstrap_directory %>\wget.ps1 "%REMOTE_SOURCE_MSI_URL%" "%LOCAL_DESTINATION_MSI_PATH%"
135
+ @echo !powershell_download!
136
+ @call !powershell_download!
137
+
138
+ @set DOWNLOAD_ERROR_STATUS=!ERRORLEVEL!
139
+
140
+ @if ERRORLEVEL 1 (
141
+ @echo Failed PowerShell download with status code !DOWNLOAD_ERROR_STATUS! > "&2"
142
+ @if !DOWNLOAD_ERROR_STATUS!==0 set DOWNLOAD_ERROR_STATUS=2
143
+ ) else (
144
+ @rem Sometimes the error level is not set even when the download failed,
145
+ @rem so check for the file to be sure it is there -- if it is not, we will retry
146
+ @if NOT EXIST "%LOCAL_DESTINATION_MSI_PATH%" (
147
+ echo Failed download: download completed, but downloaded file not found > "&2"
148
+ set DOWNLOAD_ERROR_STATUS=2
149
+ ) else (
150
+ echo Download via PowerShell succeeded.
151
+ )
152
+ )
153
+
154
+ @if NOT %DOWNLOAD_ERROR_STATUS%==0 (
155
+ @echo Warning: Failed to download "%REMOTE_SOURCE_MSI_URL%" to "%LOCAL_DESTINATION_MSI_PATH%"
156
+ @echo Warning: Retrying download with cscript ...
157
+
158
+ @if EXIST "%LOCAL_DESTINATION_MSI_PATH%" del /f /q "%LOCAL_DESTINATION_MSI_PATH%"
159
+
160
+ @set "REMOTE_SOURCE_MSI_URL=<%= msi_url('%MACHINE_OS%', '%MACHINE_ARCH%') %>"
161
+ cscript /nologo <%= bootstrap_directory %>\wget.vbs /url:"%REMOTE_SOURCE_MSI_URL%" /path:"%LOCAL_DESTINATION_MSI_PATH%"
162
+
163
+ @if NOT ERRORLEVEL 1 (
164
+ @rem Sometimes the error level is not set even when the download failed,
165
+ @rem so check for the file to be sure it is there.
166
+ @if NOT EXIST "%LOCAL_DESTINATION_MSI_PATH%" (
167
+ echo Failed download: download completed, but downloaded file not found > "&2"
168
+ echo Exiting without bootstrapping due to download failure. > "&2"
169
+ exit /b 1
170
+ ) else (
171
+ echo Download via cscript succeeded.
172
+ )
173
+ ) else (
174
+ echo Failed to download "%REMOTE_SOURCE_MSI_URL%" with status code !ERRORLEVEL!. > "&2"
175
+ echo Exiting without bootstrapping due to download failure. > "&2"
176
+ exit /b 1
177
+ )
178
+ )
179
+
180
+ @echo Installing downloaded client package...
181
+
182
+ <%= install_chef %>
183
+
184
+ @if ERRORLEVEL 1 (
185
+ echo Chef-client package failed to install with status code !ERRORLEVEL!. > "&2"
186
+ echo See installation log for additional detail: %CHEF_CLIENT_MSI_LOG_PATH%. > "&2"
187
+ ) else (
188
+ @echo Installation completed successfully
189
+ del /f /q "%CHEF_CLIENT_MSI_LOG_PATH%"
190
+ )
191
+
192
+ <% end %>
193
+
194
+ @endlocal
195
+
196
+ @echo off
197
+
198
+ <% if client_pem -%>
199
+ > <%= bootstrap_directory %>\client.pem (
200
+ <%= escape_and_echo(::File.read(::File.expand_path(client_pem))) %>
201
+ )
202
+ <% end -%>
203
+
204
+ echo Writing validation key...
205
+
206
+ <% if validation_key -%>
207
+ > <%= bootstrap_directory %>\validation.pem (
208
+ <%= escape_and_echo(validation_key) %>
209
+ )
210
+ <% end -%>
211
+
212
+ echo Validation key written.
213
+ @echo on
214
+
215
+ <% if @config[:secret] -%>
216
+ > <%= bootstrap_directory %>\encrypted_data_bag_secret (
217
+ <%= secret %>
218
+ )
219
+ <% end -%>
220
+
221
+ <% unless trusted_certs_script.empty? -%>
222
+ mkdir <%= bootstrap_directory %>\trusted_certs
223
+ <%= trusted_certs_script %>
224
+ <% end -%>
225
+
226
+ <%# Generate Ohai Hints -%>
227
+ <% unless @chef_config[:knife][:hints].nil? || @chef_config[:knife][:hints].empty? -%>
228
+ mkdir <%= bootstrap_directory %>\ohai\hints
229
+
230
+ <% @chef_config[:knife][:hints].each do |name, hash| -%>
231
+ > <%= bootstrap_directory %>\ohai\hints\<%= name %>.json (
232
+ <%= escape_and_echo(hash.to_json) %>
233
+ )
234
+ <% end -%>
235
+ <% end -%>
236
+
237
+ > <%= bootstrap_directory %>\client.rb (
238
+ <%= config_content %>
239
+ )
240
+
241
+ > <%= bootstrap_directory %>\first-boot.json (
242
+ <%= first_boot %>
243
+ )
244
+
245
+ @echo Starting chef to bootstrap the node...
246
+ <%= start_chef %>
@@ -1,140 +1,216 @@
1
- #
2
- # Author:: Adam Edwards (<adamed@opscode.com>)
3
- # Copyright:: Copyright (c) 2012 Opscode, 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
15
- # implied.
16
- # See the License for the specific language governing permissions and
17
- # limitations under the License.
18
- #
19
-
20
- require 'spec_helper'
21
- require 'tmpdir'
22
-
23
- # These test cases exercise the Knife::Windows knife plugin's ability
24
- # to download a bootstrap msi as part of the bootstrap process on
25
- # Windows nodes. The test modifies the Windows batch file generated
26
- # from an erb template in the plugin source in order to enable execution
27
- # of only the download functionality contained in the bootstrap template.
28
- # The test relies on knowledge of the fields of the template itself and
29
- # also on knowledge of the contents and structure of the Windows batch
30
- # file generated by the template.
31
- #
32
- # Note that if the bootstrap template changes substantially, the tests
33
- # should fail and will require re-implementation. If such changes
34
- # occur, the bootstrap code should be refactored to explicitly expose
35
- # the download funcitonality separately from other tasks to make the
36
- # test more robust.
37
- describe 'Knife::Windows::Core msi download functionality for knife Windows winrm bootstrap template' do
38
-
39
- before(:all) do
40
- # Since we're always running 32-bit Ruby, fix the
41
- # PROCESSOR_ARCHITECTURE environment variable.
42
-
43
- if ENV["PROCESSOR_ARCHITEW6432"]
44
- ENV["PROCESSOR_ARCHITECTURE"] = ENV["PROCESSOR_ARCHITEW6432"]
45
- end
46
-
47
- # All file artifacts from this test will be written into this directory
48
- @temp_directory = Dir.mktmpdir("bootstrap_test")
49
-
50
- # Location to which the download script will be modified to write
51
- # the downloaded msi
52
- @local_file_download_destination = "#{@temp_directory}/chef-client-latest.msi"
53
-
54
- source_code_directory = File.dirname(__FILE__)
55
- @template_file_path ="#{source_code_directory}/../../lib/chef/knife/bootstrap/windows-chef-client-msi.erb"
56
- end
57
-
58
- after(:all) do
59
- # Clear the temp directory upon exit
60
- if Dir.exists?(@temp_directory)
61
- FileUtils::remove_dir(@temp_directory)
62
- end
63
- end
64
-
65
- describe "running on any version of the Windows OS", :windows_only do
66
- let(:mock_bootstrap_context) { Chef::Knife::Core::WindowsBootstrapContext.new({ }, nil, { :knife => {} }) }
67
- let(:mock_winrm) { Chef::Knife::Winrm.new }
68
-
69
- before do
70
- # Stub the bootstrap context and prevent config related sections
71
- # from being populated, i.e. chef installation and first chef
72
- # run sections
73
- allow(mock_bootstrap_context).to receive(:validation_key).and_return("echo.validation_key")
74
- allow(mock_bootstrap_context).to receive(:secret).and_return("echo.encrypted_data_bag_secret")
75
- allow(mock_bootstrap_context).to receive(:config_content).and_return("echo.config_content")
76
- allow(mock_bootstrap_context).to receive(:start_chef).and_return("echo.echo start_chef_command")
77
- allow(mock_bootstrap_context).to receive(:run_list).and_return("echo.run_list")
78
- allow(mock_bootstrap_context).to receive(:install_chef).and_return("echo.echo install_chef_command")
79
-
80
- # Change the directories where bootstrap files will be created
81
- allow(mock_bootstrap_context).to receive(:bootstrap_directory).and_return(@temp_directory.gsub(::File::SEPARATOR, ::File::ALT_SEPARATOR))
82
- allow(mock_bootstrap_context).to receive(:local_download_path).and_return(@local_file_download_destination.gsub(::File::SEPARATOR, ::File::ALT_SEPARATOR))
83
-
84
- # Prevent password prompt during bootstrap process
85
- allow(mock_winrm).to receive(:get_password).and_return(nil)
86
- allow(Chef::Knife::Winrm).to receive(:new).and_return(mock_winrm)
87
-
88
- allow(Chef::Knife::Core::WindowsBootstrapContext).to receive(:new).and_return(mock_bootstrap_context)
89
- Chef::Config[:knife] = {:winrm_transport => 'plaintext', :chef_node_name => 'foo.example.com', :winrm_authentication_protocol => 'negotiate'}
90
- end
91
-
92
- it "downloads the chef-client MSI from the default location during winrm bootstrap" do
93
- run_download_scenario
94
- end
95
-
96
- context "when provided a custom msi_url to fetch from" do
97
- let(:mock_bootstrap_context) { Chef::Knife::Core::WindowsBootstrapContext.new(
98
- { :msi_url => "file:///C:/Windows/System32/xcopy.exe" }, nil, { :knife => {} }) }
99
- it "downloads the chef-client MSI from a custom path during winrm bootstrap" do
100
- run_download_scenario
101
- end
102
- end
103
- end
104
-
105
- def download_succeeded?
106
- File.exists?(@local_file_download_destination) && ! File.zero?(@local_file_download_destination)
107
- end
108
-
109
- # Remove file artifacts generated by individual test cases
110
- def clean_test_case
111
- if File.exists?(@local_file_download_destination)
112
- File.delete(@local_file_download_destination)
113
- end
114
- end
115
-
116
- def run_download_scenario
117
- clean_test_case
118
-
119
- winrm_bootstrapper = Chef::Knife::BootstrapWindowsWinrm.new([ "127.0.0.1" ])
120
-
121
- if chef_gte_12?
122
- winrm_bootstrapper.client_builder = instance_double("Chef::Knife::Bootstrap::ClientBuilder", :run => nil, :client_path => nil)
123
- elsif chef_lt_12?
124
- allow(File).to receive(:exist?).with(File.expand_path(Chef::Config[:validation_key])).and_return(true)
125
- end
126
-
127
- allow(winrm_bootstrapper).to receive(:wait_for_remote_response)
128
- winrm_bootstrapper.config[:template_file] = @template_file_path
129
-
130
- # Execute the commands locally that would normally be executed via WinRM
131
- allow(winrm_bootstrapper).to receive(:run_command) do |command|
132
- system(command)
133
- end
134
-
135
- winrm_bootstrapper.run
136
-
137
- # Download should succeed
138
- expect(download_succeeded?).to be true
139
- end
140
- end
1
+ #
2
+ # Author:: Adam Edwards (<adamed@opscode.com>)
3
+ # Copyright:: Copyright (c) 2012 Opscode, 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
15
+ # implied.
16
+ # See the License for the specific language governing permissions and
17
+ # limitations under the License.
18
+ #
19
+
20
+ require 'spec_helper'
21
+ require 'tmpdir'
22
+
23
+ # These test cases exercise the Knife::Windows knife plugin's ability
24
+ # to download a bootstrap msi as part of the bootstrap process on
25
+ # Windows nodes. The test modifies the Windows batch file generated
26
+ # from an erb template in the plugin source in order to enable execution
27
+ # of only the download functionality contained in the bootstrap template.
28
+ # The test relies on knowledge of the fields of the template itself and
29
+ # also on knowledge of the contents and structure of the Windows batch
30
+ # file generated by the template.
31
+ #
32
+ # Note that if the bootstrap template changes substantially, the tests
33
+ # should fail and will require re-implementation. If such changes
34
+ # occur, the bootstrap code should be refactored to explicitly expose
35
+ # the download funcitonality separately from other tasks to make the
36
+ # test more robust.
37
+ describe 'Knife::Windows::Core msi download functionality for knife Windows winrm bootstrap template' do
38
+
39
+ before(:all) do
40
+ # Since we're always running 32-bit Ruby, fix the
41
+ # PROCESSOR_ARCHITECTURE environment variable.
42
+
43
+ if ENV["PROCESSOR_ARCHITEW6432"]
44
+ ENV["PROCESSOR_ARCHITECTURE"] = ENV["PROCESSOR_ARCHITEW6432"]
45
+ end
46
+
47
+ # All file artifacts from this test will be written into this directory
48
+ @temp_directory = Dir.mktmpdir("bootstrap_test")
49
+
50
+ # Location to which the download script will be modified to write
51
+ # the downloaded msi
52
+ @local_file_download_destination = "#{@temp_directory}/chef-client-latest.msi"
53
+
54
+ source_code_directory = File.dirname(__FILE__)
55
+ @template_file_path ="#{source_code_directory}/../../lib/chef/knife/bootstrap/windows-chef-client-msi.erb"
56
+ end
57
+
58
+ after(:all) do
59
+ # Clear the temp directory upon exit
60
+ if Dir.exists?(@temp_directory)
61
+ FileUtils::remove_dir(@temp_directory)
62
+ end
63
+ end
64
+
65
+ describe "running on any version of the Windows OS", :windows_only do
66
+ let(:mock_bootstrap_context) { Chef::Knife::Core::WindowsBootstrapContext.new({ }, nil, { :knife => {} }) }
67
+ let(:mock_winrm) { Chef::Knife::Winrm.new }
68
+
69
+ before do
70
+ # Stub the bootstrap context and prevent config related sections
71
+ # from being populated, i.e. chef installation and first chef
72
+ # run sections
73
+ allow(mock_bootstrap_context).to receive(:validation_key).and_return("echo.validation_key")
74
+ allow(mock_bootstrap_context).to receive(:secret).and_return("echo.encrypted_data_bag_secret")
75
+ allow(mock_bootstrap_context).to receive(:config_content).and_return("echo.config_content")
76
+ allow(mock_bootstrap_context).to receive(:start_chef).and_return("echo.echo start_chef_command")
77
+ allow(mock_bootstrap_context).to receive(:run_list).and_return("echo.run_list")
78
+ allow(mock_bootstrap_context).to receive(:install_chef).and_return("echo.echo install_chef_command")
79
+
80
+ # Change the directories where bootstrap files will be created
81
+ allow(mock_bootstrap_context).to receive(:bootstrap_directory).and_return(@temp_directory.gsub(::File::SEPARATOR, ::File::ALT_SEPARATOR))
82
+ allow(mock_bootstrap_context).to receive(:local_download_path).and_return(@local_file_download_destination.gsub(::File::SEPARATOR, ::File::ALT_SEPARATOR))
83
+
84
+ # Prevent password prompt during bootstrap process
85
+ allow(mock_winrm).to receive(:get_password).and_return(nil)
86
+ allow(Chef::Knife::Winrm).to receive(:new).and_return(mock_winrm)
87
+
88
+ allow(Chef::Knife::Core::WindowsBootstrapContext).to receive(:new).and_return(mock_bootstrap_context)
89
+ Chef::Config[:knife] = {:winrm_transport => 'plaintext', :chef_node_name => 'foo.example.com', :winrm_authentication_protocol => 'negotiate'}
90
+ end
91
+
92
+ it "downloads the chef-client MSI from the default location during winrm bootstrap" do
93
+ run_download_scenario
94
+ end
95
+
96
+ context "when provided a custom msi_url to fetch from" do
97
+ let(:mock_bootstrap_context) { Chef::Knife::Core::WindowsBootstrapContext.new(
98
+ { :msi_url => "file:///C:/Windows/System32/xcopy.exe" }, nil, { :knife => {} }) }
99
+ it "downloads the chef-client MSI from a custom path during winrm bootstrap" do
100
+ run_download_scenario
101
+ end
102
+ end
103
+
104
+ context "when provided a custom msi_url with space in path to fetch from" do
105
+ let(:mock_bootstrap_context) { Chef::Knife::Core::WindowsBootstrapContext.new(
106
+ { :msi_url => "file:///C:/Program Files/Windows NT/Accessories/wordpad.exe" }, nil, { :knife => {} }) }
107
+ it "downloads the chef-client MSI from a custom path with spaces during winrm bootstrap" do
108
+ run_download_scenario
109
+ end
110
+ end
111
+ end
112
+
113
+ def download_succeeded?
114
+ File.exists?(@local_file_download_destination) && ! File.zero?(@local_file_download_destination)
115
+ end
116
+
117
+ # Remove file artifacts generated by individual test cases
118
+ def clean_test_case
119
+ if File.exists?(@local_file_download_destination)
120
+ File.delete(@local_file_download_destination)
121
+ end
122
+ end
123
+
124
+ def run_download_scenario
125
+ clean_test_case
126
+
127
+ winrm_bootstrapper = Chef::Knife::BootstrapWindowsWinrm.new([ "127.0.0.1" ])
128
+
129
+ if chef_gte_12?
130
+ winrm_bootstrapper.client_builder = instance_double("Chef::Knife::Bootstrap::ClientBuilder", :run => nil, :client_path => nil)
131
+ elsif chef_lt_12?
132
+ allow(File).to receive(:exist?).with(File.expand_path(Chef::Config[:validation_key])).and_return(true)
133
+ end
134
+
135
+ allow(winrm_bootstrapper).to receive(:wait_for_remote_response)
136
+ winrm_bootstrapper.config[:template_file] = @template_file_path
137
+
138
+ # Execute the commands locally that would normally be executed via WinRM
139
+ allow(winrm_bootstrapper).to receive(:run_command) do |command|
140
+ system(command)
141
+ end
142
+
143
+ winrm_bootstrapper.run
144
+
145
+ # Download should succeed
146
+ expect(download_succeeded?).to be true
147
+ end
148
+ end
149
+
150
+ describe "bootstrap_install_command functionality through WinRM protocol", :if_chef_11 => true do
151
+ context "bootstrap_install_command option is not specified" do
152
+ let(:bootstrap) { Chef::Knife::BootstrapWindowsWinrm.new([]) }
153
+ before do
154
+ @template_input = sample_data('win_template_unrendered.txt')
155
+ @template_output = sample_data('win_template_rendered_without_bootstrap_install_command.txt')
156
+ end
157
+
158
+ it "bootstrap_install_command option is not rendered in the windows-chef-client-msi.erb template as its value is nil" do
159
+ expect(bootstrap.send(:render_template,@template_input)).to eq(
160
+ @template_output)
161
+ end
162
+ end
163
+
164
+ context "bootstrap_install_command option is specified" do
165
+ let(:bootstrap) { Chef::Knife::BootstrapWindowsWinrm.new(['--bootstrap-install-command', 'chef-client -o recipe[cbk1::rec2]']) }
166
+ before do
167
+ bootstrap.config[:bootstrap_install_command] = "chef-client -o recipe[cbk1::rec2]"
168
+ @template_input = sample_data('win_template_unrendered.txt')
169
+ @template_output = sample_data('win_template_rendered_with_bootstrap_install_command.txt')
170
+ end
171
+
172
+ it "bootstrap_install_command option is rendered in the windows-chef-client-msi.erb template" do
173
+ expect(bootstrap.send(:render_template,@template_input)).to eq(
174
+ @template_output)
175
+ end
176
+
177
+ after do
178
+ bootstrap.config.delete(:bootstrap_install_command)
179
+ Chef::Config[:knife].delete(:bootstrap_install_command)
180
+ end
181
+ end
182
+ end
183
+
184
+ describe "bootstrap_install_command functionality through SSH protocol", :if_chef_11 => true do
185
+ context "bootstrap_install_command option is not specified" do
186
+ let(:bootstrap) { Chef::Knife::BootstrapWindowsSsh.new([]) }
187
+ before do
188
+ @template_input = sample_data('win_template_unrendered.txt')
189
+ @template_output = sample_data('win_template_rendered_without_bootstrap_install_command.txt')
190
+ end
191
+
192
+ it "bootstrap_install_command option is not rendered in the windows-chef-client-msi.erb template as its value is nil" do
193
+ expect(bootstrap.send(:render_template,@template_input)).to eq(
194
+ @template_output)
195
+ end
196
+ end
197
+
198
+ context "bootstrap_install_command option is specified" do
199
+ let(:bootstrap) { Chef::Knife::BootstrapWindowsSsh.new(['--bootstrap-install-command', 'chef-client -o recipe[cbk1::rec2]']) }
200
+ before do
201
+ bootstrap.config[:bootstrap_install_command] = "chef-client -o recipe[cbk1::rec2]"
202
+ @template_input = sample_data('win_template_unrendered.txt')
203
+ @template_output = sample_data('win_template_rendered_with_bootstrap_install_command.txt')
204
+ end
205
+
206
+ it "bootstrap_install_command option is rendered in the windows-chef-client-msi.erb template" do
207
+ expect(bootstrap.send(:render_template,@template_input)).to eq(
208
+ @template_output)
209
+ end
210
+
211
+ after do
212
+ bootstrap.config.delete(:bootstrap_install_command)
213
+ Chef::Config[:knife].delete(:bootstrap_install_command)
214
+ end
215
+ end
216
+ end