knife-windows 1.3.0 → 1.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +5 -5
  3. data/.travis.yml +26 -26
  4. data/CHANGELOG.md +112 -108
  5. data/DOC_CHANGES.md +14 -14
  6. data/Gemfile +12 -12
  7. data/LICENSE +201 -201
  8. data/README.md +391 -385
  9. data/RELEASE_NOTES.md +34 -34
  10. data/Rakefile +21 -21
  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 +25 -25
  16. data/lib/chef/knife/bootstrap/windows-chef-client-msi.erb +233 -247
  17. data/lib/chef/knife/bootstrap_windows_base.rb +449 -415
  18. data/lib/chef/knife/bootstrap_windows_ssh.rb +115 -115
  19. data/lib/chef/knife/bootstrap_windows_winrm.rb +95 -95
  20. data/lib/chef/knife/core/windows_bootstrap_context.rb +372 -366
  21. data/lib/chef/knife/knife_windows_base.rb +33 -33
  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 +122 -122
  27. data/lib/chef/knife/winrm_base.rb +117 -117
  28. data/lib/chef/knife/winrm_knife_base.rb +305 -303
  29. data/lib/chef/knife/winrm_session.rb +88 -87
  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 +117 -117
  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 -217
  36. data/spec/assets/win_template_rendered_with_bootstrap_install_command_on_12_5_client.txt +217 -217
  37. data/spec/assets/win_template_rendered_without_bootstrap_install_command.txt +329 -329
  38. data/spec/assets/win_template_rendered_without_bootstrap_install_command_on_12_5_client.txt +329 -329
  39. data/spec/assets/win_template_unrendered.txt +246 -246
  40. data/spec/functional/bootstrap_download_spec.rb +241 -234
  41. data/spec/spec_helper.rb +94 -93
  42. data/spec/unit/knife/bootstrap_options_spec.rb +155 -155
  43. data/spec/unit/knife/bootstrap_template_spec.rb +98 -92
  44. data/spec/unit/knife/bootstrap_windows_winrm_spec.rb +341 -295
  45. data/spec/unit/knife/core/windows_bootstrap_context_spec.rb +177 -177
  46. data/spec/unit/knife/windows_cert_generate_spec.rb +90 -90
  47. data/spec/unit/knife/windows_cert_install_spec.rb +51 -51
  48. data/spec/unit/knife/windows_listener_create_spec.rb +76 -76
  49. data/spec/unit/knife/winrm_session_spec.rb +65 -65
  50. data/spec/unit/knife/winrm_spec.rb +516 -516
  51. data/spec/unit/knife/wsman_test_spec.rb +202 -202
  52. metadata +23 -4
@@ -1,366 +1,372 @@
1
- #
2
- # Author:: Seth Chisamore (<schisamo@opscode.com>)
3
- # Copyright:: Copyright (c) 2011 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 implied.
15
- # See the License for the specific language governing permissions and
16
- # limitations under the License.
17
- #
18
-
19
- require 'chef/knife/core/bootstrap_context'
20
-
21
- # Chef::Util::PathHelper in Chef 11 is a bit juvenile still
22
- require 'knife-windows/path_helper'
23
- # require 'chef/util/path_helper'
24
-
25
-
26
- class Chef
27
- class Knife
28
- module Core
29
- # Instances of BootstrapContext are the context objects (i.e., +self+) for
30
- # bootstrap templates. For backwards compatability, they +must+ set the
31
- # following instance variables:
32
- # * @config - a hash of knife's config values
33
- # * @run_list - the run list for the node to boostrap
34
- #
35
- class WindowsBootstrapContext < BootstrapContext
36
- PathHelper = ::Knife::Windows::PathHelper
37
-
38
- attr_accessor :client_pem
39
-
40
- def initialize(config, run_list, chef_config, secret=nil)
41
- @config = config
42
- @run_list = run_list
43
- @chef_config = chef_config
44
- @secret = secret
45
- # Compatibility with Chef 12 and Chef 11 versions
46
- begin
47
- # Pass along the secret parameter for Chef 12
48
- super(config, run_list, chef_config, secret)
49
- rescue ArgumentError
50
- # The Chef 11 base class only has parameters for initialize
51
- super(config, run_list, chef_config)
52
- end
53
- end
54
-
55
- def validation_key
56
- if File.exist?(File.expand_path(@chef_config[:validation_key]))
57
- IO.read(File.expand_path(@chef_config[:validation_key]))
58
- else
59
- false
60
- end
61
- end
62
-
63
- def secret
64
- escape_and_echo(@config[:secret])
65
- end
66
-
67
- def trusted_certs_script
68
- @trusted_certs_script ||= trusted_certs_content
69
- end
70
-
71
- def config_content
72
- client_rb = <<-CONFIG
73
- log_level :info
74
- log_location STDOUT
75
-
76
- chef_server_url "#{@chef_config[:chef_server_url]}"
77
- validation_client_name "#{@chef_config[:validation_client_name]}"
78
-
79
- file_cache_path "c:/chef/cache"
80
- file_backup_path "c:/chef/backup"
81
- cache_options ({:path => "c:/chef/cache/checksums", :skip_expires => true})
82
-
83
- CONFIG
84
- if @config[:chef_node_name]
85
- client_rb << %Q{node_name "#{@config[:chef_node_name]}"\n}
86
- else
87
- client_rb << "# Using default node name (fqdn)\n"
88
- end
89
-
90
- # We configure :verify_api_cert only when it's overridden on the CLI
91
- # or when specified in the knife config.
92
- if !@config[:node_verify_api_cert].nil? || knife_config.has_key?(:verify_api_cert)
93
- value = @config[:node_verify_api_cert].nil? ? knife_config[:verify_api_cert] : @config[:node_verify_api_cert]
94
- client_rb << %Q{verify_api_cert #{value}\n}
95
- end
96
-
97
- # We configure :ssl_verify_mode only when it's overridden on the CLI
98
- # or when specified in the knife config.
99
- if @config[:node_ssl_verify_mode] || knife_config.has_key?(:ssl_verify_mode)
100
- value = case @config[:node_ssl_verify_mode]
101
- when "peer"
102
- :verify_peer
103
- when "none"
104
- :verify_none
105
- when nil
106
- knife_config[:ssl_verify_mode]
107
- else
108
- nil
109
- end
110
-
111
- if value
112
- client_rb << %Q{ssl_verify_mode :#{value}\n}
113
- end
114
- end
115
-
116
- if @config[:ssl_verify_mode]
117
- client_rb << %Q{ssl_verify_mode :#{knife_config[:ssl_verify_mode]}\n}
118
- end
119
-
120
- if knife_config[:bootstrap_proxy]
121
- client_rb << "\n"
122
- client_rb << %Q{http_proxy "#{knife_config[:bootstrap_proxy]}"\n}
123
- client_rb << %Q{https_proxy "#{knife_config[:bootstrap_proxy]}"\n}
124
- client_rb << %Q{no_proxy "#{knife_config[:bootstrap_no_proxy]}"\n} if knife_config[:bootstrap_no_proxy]
125
- end
126
-
127
- if knife_config[:bootstrap_no_proxy]
128
- client_rb << %Q{no_proxy "#{knife_config[:bootstrap_no_proxy]}"\n}
129
- end
130
-
131
- if @config[:secret]
132
- client_rb << %Q{encrypted_data_bag_secret "c:/chef/encrypted_data_bag_secret"\n}
133
- end
134
-
135
- unless trusted_certs_script.empty?
136
- client_rb << %Q{trusted_certs_dir "c:/chef/trusted_certs"\n}
137
- end
138
-
139
- if Chef::Config[:fips]
140
- client_rb << %Q{fips true\n}
141
- end
142
-
143
- escape_and_echo(client_rb)
144
- end
145
-
146
- def start_chef
147
- bootstrap_environment_option = bootstrap_environment.nil? ? '' : " -E #{bootstrap_environment}"
148
- start_chef = "SET \"PATH=%PATH%;C:\\ruby\\bin;C:\\opscode\\chef\\bin;C:\\opscode\\chef\\embedded\\bin\"\n"
149
- start_chef << "chef-client -c c:/chef/client.rb -j c:/chef/first-boot.json#{bootstrap_environment_option}\n"
150
- end
151
-
152
- def latest_current_windows_chef_version_query
153
- installer_version_string = nil
154
- if @config[:prerelease]
155
- installer_version_string = "&prerelease=true"
156
- else
157
- chef_version_string = if knife_config[:bootstrap_version]
158
- knife_config[:bootstrap_version]
159
- else
160
- Chef::VERSION.split(".").first
161
- end
162
-
163
- installer_version_string = "&v=#{chef_version_string}"
164
-
165
- # If bootstrapping a pre-release version add the prerelease query string
166
- if chef_version_string.split(".").length > 3
167
- installer_version_string << "&prerelease=true"
168
- end
169
- end
170
-
171
- installer_version_string
172
- end
173
-
174
- def win_wget
175
- # I tried my best to figure out how to properly url decode and switch / to \
176
- # but this is VBScript - so I don't really care that badly.
177
- win_wget = <<-WGET
178
- url = WScript.Arguments.Named("url")
179
- path = WScript.Arguments.Named("path")
180
- proxy = null
181
- '* Vaguely attempt to handle file:// scheme urls by url unescaping and switching all
182
- '* / into \. Also assume that file:/// is a local absolute path and that file://<foo>
183
- '* is possibly a network file path.
184
- If InStr(url, "file://") = 1 Then
185
- url = Unescape(url)
186
- If InStr(url, "file:///") = 1 Then
187
- sourcePath = Mid(url, Len("file:///") + 1)
188
- Else
189
- sourcePath = Mid(url, Len("file:") + 1)
190
- End If
191
- sourcePath = Replace(sourcePath, "/", "\\")
192
-
193
- Set objFSO = CreateObject("Scripting.FileSystemObject")
194
- If objFSO.Fileexists(path) Then objFSO.DeleteFile path
195
- objFSO.CopyFile sourcePath, path, true
196
- Set objFSO = Nothing
197
-
198
- Else
199
- Set objXMLHTTP = CreateObject("MSXML2.ServerXMLHTTP")
200
- Set wshShell = CreateObject( "WScript.Shell" )
201
- Set objUserVariables = wshShell.Environment("USER")
202
-
203
- rem http proxy is optional
204
- rem attempt to read from HTTP_PROXY env var first
205
- On Error Resume Next
206
-
207
- If NOT (objUserVariables("HTTP_PROXY") = "") Then
208
- proxy = objUserVariables("HTTP_PROXY")
209
-
210
- rem fall back to named arg
211
- ElseIf NOT (WScript.Arguments.Named("proxy") = "") Then
212
- proxy = WScript.Arguments.Named("proxy")
213
- End If
214
-
215
- If NOT isNull(proxy) Then
216
- rem setProxy method is only available on ServerXMLHTTP 6.0+
217
- Set objXMLHTTP = CreateObject("MSXML2.ServerXMLHTTP.6.0")
218
- objXMLHTTP.setProxy 2, proxy
219
- End If
220
-
221
- On Error Goto 0
222
-
223
- objXMLHTTP.open "GET", url, false
224
- objXMLHTTP.send()
225
- If objXMLHTTP.Status = 200 Then
226
- Set objADOStream = CreateObject("ADODB.Stream")
227
- objADOStream.Open
228
- objADOStream.Type = 1
229
- objADOStream.Write objXMLHTTP.ResponseBody
230
- objADOStream.Position = 0
231
- Set objFSO = Createobject("Scripting.FileSystemObject")
232
- If objFSO.Fileexists(path) Then objFSO.DeleteFile path
233
- Set objFSO = Nothing
234
- objADOStream.SaveToFile path
235
- objADOStream.Close
236
- Set objADOStream = Nothing
237
- End If
238
- Set objXMLHTTP = Nothing
239
- End If
240
- WGET
241
- escape_and_echo(win_wget)
242
- end
243
-
244
- def win_wget_ps
245
- win_wget_ps = <<-WGET_PS
246
- param(
247
- [String] $remoteUrl,
248
- [String] $localPath
249
- )
250
-
251
- $webClient = new-object System.Net.WebClient;
252
-
253
- $webClient.DownloadFile($remoteUrl, $localPath);
254
- WGET_PS
255
-
256
- escape_and_echo(win_wget_ps)
257
- end
258
-
259
- def install_chef
260
- # The normal install command uses regular double quotes in
261
- # the install command, so request such a string from install_command
262
- install_chef = install_command('"') + "\n" + fallback_install_task_command
263
- end
264
-
265
- def bootstrap_directory
266
- bootstrap_directory = "C:\\chef"
267
- end
268
-
269
- def local_download_path
270
- local_download_path = "%TEMP%\\chef-client-latest.msi"
271
- end
272
-
273
- def msi_url(machine_os=nil, machine_arch=nil, download_context=nil)
274
- # The default msi path has a number of url query parameters - we attempt to substitute
275
- # such parameters in as long as they are provided by the template.
276
-
277
- if @config[:msi_url].nil? || @config[:msi_url].empty?
278
- url = "https://www.chef.io/chef/download?p=windows"
279
- url += "&pv=#{machine_os}" unless machine_os.nil?
280
- url += "&m=#{machine_arch}" unless machine_arch.nil?
281
- url += "&DownloadContext=#{download_context}" unless download_context.nil?
282
- url += latest_current_windows_chef_version_query
283
- else
284
- @config[:msi_url]
285
- end
286
- end
287
-
288
- def first_boot
289
- escape_and_echo(super.to_json)
290
- end
291
-
292
- # escape WIN BATCH special chars
293
- # and prefixes each line with an
294
- # echo
295
- def escape_and_echo(file_contents)
296
- file_contents.gsub(/^(.*)$/, 'echo.\1').gsub(/([(<|>)^])/, '^\1')
297
- end
298
-
299
- private
300
-
301
- def install_command(executor_quote)
302
- if @config[:install_as_service]
303
- "msiexec /qn /log #{executor_quote}%CHEF_CLIENT_MSI_LOG_PATH%#{executor_quote} /i #{executor_quote}%LOCAL_DESTINATION_MSI_PATH%#{executor_quote} ADDLOCAL=#{executor_quote}ChefClientFeature,ChefServiceFeature#{executor_quote}"
304
- else
305
- "msiexec /qn /log #{executor_quote}%CHEF_CLIENT_MSI_LOG_PATH%#{executor_quote} /i #{executor_quote}%LOCAL_DESTINATION_MSI_PATH%#{executor_quote}"
306
- end
307
- end
308
-
309
- # Returns a string for copying the trusted certificates on the workstation to the system being bootstrapped
310
- # This string should contain both the commands necessary to both create the files, as well as their content
311
- def trusted_certs_content
312
- content = ""
313
- if @chef_config[:trusted_certs_dir]
314
- Dir.glob(File.join(PathHelper.escape_glob(@chef_config[:trusted_certs_dir]), "*.{crt,pem}")).each do |cert|
315
- content << "> #{bootstrap_directory}/trusted_certs/#{File.basename(cert)} (\n" +
316
- escape_and_echo(IO.read(File.expand_path(cert))) + "\n)\n"
317
- end
318
- end
319
- content
320
- end
321
-
322
- def fallback_install_task_command
323
- # This command will be executed by schtasks.exe in the batch
324
- # code below. To handle tasks that contain arguments that
325
- # need to be double quoted, schtasks allows the use of single
326
- # quotes that will later be converted to double quotes
327
- command = install_command('\'')
328
- <<-EOH
329
- @set MSIERRORCODE=!ERRORLEVEL!
330
- @if ERRORLEVEL 1 (
331
- @echo WARNING: Failed to install Chef Client MSI package in remote context with status code !MSIERRORCODE!.
332
- @echo WARNING: This may be due to a defect in operating system update KB2918614: http://support.microsoft.com/kb/2918614
333
- @set OLDLOGLOCATION="%CHEF_CLIENT_MSI_LOG_PATH%-fail.log"
334
- @move "%CHEF_CLIENT_MSI_LOG_PATH%" "!OLDLOGLOCATION!" > NUL
335
- @echo WARNING: Saving installation log of failure at !OLDLOGLOCATION!
336
- @echo WARNING: Retrying installation with local context...
337
- @schtasks /create /f /sc once /st 00:00:00 /tn chefclientbootstraptask /ru SYSTEM /rl HIGHEST /tr \"cmd /c #{command} & sleep 2 & waitfor /s %computername% /si chefclientinstalldone\"
338
-
339
- @if ERRORLEVEL 1 (
340
- @echo ERROR: Failed to create Chef Client installation scheduled task with status code !ERRORLEVEL! > "&2"
341
- ) else (
342
- @echo Successfully created scheduled task to install Chef Client.
343
- @schtasks /run /tn chefclientbootstraptask
344
- @if ERRORLEVEL 1 (
345
- @echo ERROR: Failed to execut Chef Client installation scheduled task with status code !ERRORLEVEL!. > "&2"
346
- ) else (
347
- @echo Successfully started Chef Client installation scheduled task.
348
- @echo Waiting for installation to complete -- this may take a few minutes...
349
- waitfor chefclientinstalldone /t 600
350
- if ERRORLEVEL 1 (
351
- @echo ERROR: Timed out waiting for Chef Client package to install
352
- ) else (
353
- @echo Finished waiting for Chef Client package to install.
354
- )
355
- @schtasks /delete /f /tn chefclientbootstraptask > NUL
356
- )
357
- )
358
- ) else (
359
- @echo Successfully installed Chef Client package.
360
- )
361
- EOH
362
- end
363
- end
364
- end
365
- end
366
- end
1
+ #
2
+ # Author:: Seth Chisamore (<schisamo@chef.io>)
3
+ # Copyright:: Copyright (c) 2011-2016 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 'chef/knife/core/bootstrap_context'
20
+
21
+ # Chef::Util::PathHelper in Chef 11 is a bit juvenile still
22
+ require 'knife-windows/path_helper'
23
+ # require 'chef/util/path_helper'
24
+
25
+
26
+ class Chef
27
+ class Knife
28
+ module Core
29
+ # Instances of BootstrapContext are the context objects (i.e., +self+) for
30
+ # bootstrap templates. For backwards compatability, they +must+ set the
31
+ # following instance variables:
32
+ # * @config - a hash of knife's config values
33
+ # * @run_list - the run list for the node to boostrap
34
+ #
35
+ class WindowsBootstrapContext < BootstrapContext
36
+ PathHelper = ::Knife::Windows::PathHelper
37
+
38
+ attr_accessor :client_pem
39
+
40
+ def initialize(config, run_list, chef_config, secret=nil)
41
+ @config = config
42
+ @run_list = run_list
43
+ @chef_config = chef_config
44
+ @secret = secret
45
+ # Compatibility with Chef 12 and Chef 11 versions
46
+ begin
47
+ # Pass along the secret parameter for Chef 12
48
+ super(config, run_list, chef_config, secret)
49
+ rescue ArgumentError
50
+ # The Chef 11 base class only has parameters for initialize
51
+ super(config, run_list, chef_config)
52
+ end
53
+ end
54
+
55
+ def validation_key
56
+ if File.exist?(File.expand_path(@chef_config[:validation_key]))
57
+ IO.read(File.expand_path(@chef_config[:validation_key]))
58
+ else
59
+ false
60
+ end
61
+ end
62
+
63
+ def secret
64
+ escape_and_echo(@config[:secret])
65
+ end
66
+
67
+ def trusted_certs_script
68
+ @trusted_certs_script ||= trusted_certs_content
69
+ end
70
+
71
+ def config_content
72
+ client_rb = <<-CONFIG
73
+ log_level :info
74
+ log_location STDOUT
75
+
76
+ chef_server_url "#{@chef_config[:chef_server_url]}"
77
+ validation_client_name "#{@chef_config[:validation_client_name]}"
78
+
79
+ file_cache_path "c:/chef/cache"
80
+ file_backup_path "c:/chef/backup"
81
+ cache_options ({:path => "c:/chef/cache/checksums", :skip_expires => true})
82
+
83
+ CONFIG
84
+ if @config[:chef_node_name]
85
+ client_rb << %Q{node_name "#{@config[:chef_node_name]}"\n}
86
+ else
87
+ client_rb << "# Using default node name (fqdn)\n"
88
+ end
89
+
90
+ # We configure :verify_api_cert only when it's overridden on the CLI
91
+ # or when specified in the knife config.
92
+ if !@config[:node_verify_api_cert].nil? || knife_config.has_key?(:verify_api_cert)
93
+ value = @config[:node_verify_api_cert].nil? ? knife_config[:verify_api_cert] : @config[:node_verify_api_cert]
94
+ client_rb << %Q{verify_api_cert #{value}\n}
95
+ end
96
+
97
+ # We configure :ssl_verify_mode only when it's overridden on the CLI
98
+ # or when specified in the knife config.
99
+ if @config[:node_ssl_verify_mode] || knife_config.has_key?(:ssl_verify_mode)
100
+ value = case @config[:node_ssl_verify_mode]
101
+ when "peer"
102
+ :verify_peer
103
+ when "none"
104
+ :verify_none
105
+ when nil
106
+ knife_config[:ssl_verify_mode]
107
+ else
108
+ nil
109
+ end
110
+
111
+ if value
112
+ client_rb << %Q{ssl_verify_mode :#{value}\n}
113
+ end
114
+ end
115
+
116
+ if @config[:ssl_verify_mode]
117
+ client_rb << %Q{ssl_verify_mode :#{knife_config[:ssl_verify_mode]}\n}
118
+ end
119
+
120
+ if knife_config[:bootstrap_proxy]
121
+ client_rb << "\n"
122
+ client_rb << %Q{http_proxy "#{knife_config[:bootstrap_proxy]}"\n}
123
+ client_rb << %Q{https_proxy "#{knife_config[:bootstrap_proxy]}"\n}
124
+ client_rb << %Q{no_proxy "#{knife_config[:bootstrap_no_proxy]}"\n} if knife_config[:bootstrap_no_proxy]
125
+ end
126
+
127
+ if knife_config[:bootstrap_no_proxy]
128
+ client_rb << %Q{no_proxy "#{knife_config[:bootstrap_no_proxy]}"\n}
129
+ end
130
+
131
+ if @config[:secret]
132
+ client_rb << %Q{encrypted_data_bag_secret "c:/chef/encrypted_data_bag_secret"\n}
133
+ end
134
+
135
+ unless trusted_certs_script.empty?
136
+ client_rb << %Q{trusted_certs_dir "c:/chef/trusted_certs"\n}
137
+ end
138
+
139
+ if Chef::Config[:fips]
140
+ client_rb << <<-CONFIG
141
+ fips true
142
+ chef_version = ::Chef::VERSION.split(".")
143
+ unless chef_version[0].to_i > 12 || (chef_version[0].to_i == 12 && chef_version[1].to_i >= 8)
144
+ raise "FIPS Mode requested but not supported by this client"
145
+ end
146
+ CONFIG
147
+ end
148
+
149
+ escape_and_echo(client_rb)
150
+ end
151
+
152
+ def start_chef
153
+ bootstrap_environment_option = bootstrap_environment.nil? ? '' : " -E #{bootstrap_environment}"
154
+ start_chef = "SET \"PATH=%PATH%;C:\\ruby\\bin;C:\\opscode\\chef\\bin;C:\\opscode\\chef\\embedded\\bin\"\n"
155
+ start_chef << "chef-client -c c:/chef/client.rb -j c:/chef/first-boot.json#{bootstrap_environment_option}\n"
156
+ end
157
+
158
+ def latest_current_windows_chef_version_query
159
+ installer_version_string = nil
160
+ if @config[:prerelease]
161
+ installer_version_string = "&prerelease=true"
162
+ else
163
+ chef_version_string = if knife_config[:bootstrap_version]
164
+ knife_config[:bootstrap_version]
165
+ else
166
+ Chef::VERSION.split(".").first
167
+ end
168
+
169
+ installer_version_string = "&v=#{chef_version_string}"
170
+
171
+ # If bootstrapping a pre-release version add the prerelease query string
172
+ if chef_version_string.split(".").length > 3
173
+ installer_version_string << "&prerelease=true"
174
+ end
175
+ end
176
+
177
+ installer_version_string
178
+ end
179
+
180
+ def win_wget
181
+ # I tried my best to figure out how to properly url decode and switch / to \
182
+ # but this is VBScript - so I don't really care that badly.
183
+ win_wget = <<-WGET
184
+ url = WScript.Arguments.Named("url")
185
+ path = WScript.Arguments.Named("path")
186
+ proxy = null
187
+ '* Vaguely attempt to handle file:// scheme urls by url unescaping and switching all
188
+ '* / into \. Also assume that file:/// is a local absolute path and that file://<foo>
189
+ '* is possibly a network file path.
190
+ If InStr(url, "file://") = 1 Then
191
+ url = Unescape(url)
192
+ If InStr(url, "file:///") = 1 Then
193
+ sourcePath = Mid(url, Len("file:///") + 1)
194
+ Else
195
+ sourcePath = Mid(url, Len("file:") + 1)
196
+ End If
197
+ sourcePath = Replace(sourcePath, "/", "\\")
198
+
199
+ Set objFSO = CreateObject("Scripting.FileSystemObject")
200
+ If objFSO.Fileexists(path) Then objFSO.DeleteFile path
201
+ objFSO.CopyFile sourcePath, path, true
202
+ Set objFSO = Nothing
203
+
204
+ Else
205
+ Set objXMLHTTP = CreateObject("MSXML2.ServerXMLHTTP")
206
+ Set wshShell = CreateObject( "WScript.Shell" )
207
+ Set objUserVariables = wshShell.Environment("USER")
208
+
209
+ rem http proxy is optional
210
+ rem attempt to read from HTTP_PROXY env var first
211
+ On Error Resume Next
212
+
213
+ If NOT (objUserVariables("HTTP_PROXY") = "") Then
214
+ proxy = objUserVariables("HTTP_PROXY")
215
+
216
+ rem fall back to named arg
217
+ ElseIf NOT (WScript.Arguments.Named("proxy") = "") Then
218
+ proxy = WScript.Arguments.Named("proxy")
219
+ End If
220
+
221
+ If NOT isNull(proxy) Then
222
+ rem setProxy method is only available on ServerXMLHTTP 6.0+
223
+ Set objXMLHTTP = CreateObject("MSXML2.ServerXMLHTTP.6.0")
224
+ objXMLHTTP.setProxy 2, proxy
225
+ End If
226
+
227
+ On Error Goto 0
228
+
229
+ objXMLHTTP.open "GET", url, false
230
+ objXMLHTTP.send()
231
+ If objXMLHTTP.Status = 200 Then
232
+ Set objADOStream = CreateObject("ADODB.Stream")
233
+ objADOStream.Open
234
+ objADOStream.Type = 1
235
+ objADOStream.Write objXMLHTTP.ResponseBody
236
+ objADOStream.Position = 0
237
+ Set objFSO = Createobject("Scripting.FileSystemObject")
238
+ If objFSO.Fileexists(path) Then objFSO.DeleteFile path
239
+ Set objFSO = Nothing
240
+ objADOStream.SaveToFile path
241
+ objADOStream.Close
242
+ Set objADOStream = Nothing
243
+ End If
244
+ Set objXMLHTTP = Nothing
245
+ End If
246
+ WGET
247
+ escape_and_echo(win_wget)
248
+ end
249
+
250
+ def win_wget_ps
251
+ win_wget_ps = <<-WGET_PS
252
+ param(
253
+ [String] $remoteUrl,
254
+ [String] $localPath
255
+ )
256
+
257
+ $webClient = new-object System.Net.WebClient;
258
+
259
+ $webClient.DownloadFile($remoteUrl, $localPath);
260
+ WGET_PS
261
+
262
+ escape_and_echo(win_wget_ps)
263
+ end
264
+
265
+ def install_chef
266
+ # The normal install command uses regular double quotes in
267
+ # the install command, so request such a string from install_command
268
+ install_chef = install_command('"') + "\n" + fallback_install_task_command
269
+ end
270
+
271
+ def bootstrap_directory
272
+ bootstrap_directory = "C:\\chef"
273
+ end
274
+
275
+ def local_download_path
276
+ local_download_path = "%TEMP%\\chef-client-latest.msi"
277
+ end
278
+
279
+ def msi_url(machine_os=nil, machine_arch=nil, download_context=nil)
280
+ # The default msi path has a number of url query parameters - we attempt to substitute
281
+ # such parameters in as long as they are provided by the template.
282
+
283
+ if @config[:msi_url].nil? || @config[:msi_url].empty?
284
+ url = "https://www.chef.io/chef/download?p=windows"
285
+ url += "&pv=#{machine_os}" unless machine_os.nil?
286
+ url += "&m=#{machine_arch}" unless machine_arch.nil?
287
+ url += "&DownloadContext=#{download_context}" unless download_context.nil?
288
+ url += latest_current_windows_chef_version_query
289
+ else
290
+ @config[:msi_url]
291
+ end
292
+ end
293
+
294
+ def first_boot
295
+ escape_and_echo(super.to_json)
296
+ end
297
+
298
+ # escape WIN BATCH special chars
299
+ # and prefixes each line with an
300
+ # echo
301
+ def escape_and_echo(file_contents)
302
+ file_contents.gsub(/^(.*)$/, 'echo.\1').gsub(/([(<|>)^])/, '^\1')
303
+ end
304
+
305
+ private
306
+
307
+ def install_command(executor_quote)
308
+ if @config[:install_as_service]
309
+ "msiexec /qn /log #{executor_quote}%CHEF_CLIENT_MSI_LOG_PATH%#{executor_quote} /i #{executor_quote}%LOCAL_DESTINATION_MSI_PATH%#{executor_quote} ADDLOCAL=#{executor_quote}ChefClientFeature,ChefServiceFeature#{executor_quote}"
310
+ else
311
+ "msiexec /qn /log #{executor_quote}%CHEF_CLIENT_MSI_LOG_PATH%#{executor_quote} /i #{executor_quote}%LOCAL_DESTINATION_MSI_PATH%#{executor_quote}"
312
+ end
313
+ end
314
+
315
+ # Returns a string for copying the trusted certificates on the workstation to the system being bootstrapped
316
+ # This string should contain both the commands necessary to both create the files, as well as their content
317
+ def trusted_certs_content
318
+ content = ""
319
+ if @chef_config[:trusted_certs_dir]
320
+ Dir.glob(File.join(PathHelper.escape_glob(@chef_config[:trusted_certs_dir]), "*.{crt,pem}")).each do |cert|
321
+ content << "> #{bootstrap_directory}/trusted_certs/#{File.basename(cert)} (\n" +
322
+ escape_and_echo(IO.read(File.expand_path(cert))) + "\n)\n"
323
+ end
324
+ end
325
+ content
326
+ end
327
+
328
+ def fallback_install_task_command
329
+ # This command will be executed by schtasks.exe in the batch
330
+ # code below. To handle tasks that contain arguments that
331
+ # need to be double quoted, schtasks allows the use of single
332
+ # quotes that will later be converted to double quotes
333
+ command = install_command('\'')
334
+ <<-EOH
335
+ @set MSIERRORCODE=!ERRORLEVEL!
336
+ @if ERRORLEVEL 1 (
337
+ @echo WARNING: Failed to install Chef Client MSI package in remote context with status code !MSIERRORCODE!.
338
+ @echo WARNING: This may be due to a defect in operating system update KB2918614: http://support.microsoft.com/kb/2918614
339
+ @set OLDLOGLOCATION="%CHEF_CLIENT_MSI_LOG_PATH%-fail.log"
340
+ @move "%CHEF_CLIENT_MSI_LOG_PATH%" "!OLDLOGLOCATION!" > NUL
341
+ @echo WARNING: Saving installation log of failure at !OLDLOGLOCATION!
342
+ @echo WARNING: Retrying installation with local context...
343
+ @schtasks /create /f /sc once /st 00:00:00 /tn chefclientbootstraptask /ru SYSTEM /rl HIGHEST /tr \"cmd /c #{command} & sleep 2 & waitfor /s %computername% /si chefclientinstalldone\"
344
+
345
+ @if ERRORLEVEL 1 (
346
+ @echo ERROR: Failed to create Chef Client installation scheduled task with status code !ERRORLEVEL! > "&2"
347
+ ) else (
348
+ @echo Successfully created scheduled task to install Chef Client.
349
+ @schtasks /run /tn chefclientbootstraptask
350
+ @if ERRORLEVEL 1 (
351
+ @echo ERROR: Failed to execut Chef Client installation scheduled task with status code !ERRORLEVEL!. > "&2"
352
+ ) else (
353
+ @echo Successfully started Chef Client installation scheduled task.
354
+ @echo Waiting for installation to complete -- this may take a few minutes...
355
+ waitfor chefclientinstalldone /t 600
356
+ if ERRORLEVEL 1 (
357
+ @echo ERROR: Timed out waiting for Chef Client package to install
358
+ ) else (
359
+ @echo Finished waiting for Chef Client package to install.
360
+ )
361
+ @schtasks /delete /f /tn chefclientbootstraptask > NUL
362
+ )
363
+ )
364
+ ) else (
365
+ @echo Successfully installed Chef Client package.
366
+ )
367
+ EOH
368
+ end
369
+ end
370
+ end
371
+ end
372
+ end