knife-winops 2.0.0

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 (54) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +5 -0
  3. data/.rspec +3 -0
  4. data/.travis.yml +30 -0
  5. data/CHANGELOG.md +147 -0
  6. data/DOC_CHANGES.md +22 -0
  7. data/Gemfile +13 -0
  8. data/LICENSE +201 -0
  9. data/README.md +430 -0
  10. data/RELEASE_NOTES.md +17 -0
  11. data/Rakefile +21 -0
  12. data/appveyor.yml +36 -0
  13. data/ci.gemfile +15 -0
  14. data/knife-winops.gemspec +26 -0
  15. data/lib/chef/knife/bootstrap/Chef_bootstrap.erb +44 -0
  16. data/lib/chef/knife/bootstrap/bootstrap.ps1 +134 -0
  17. data/lib/chef/knife/bootstrap/tail.cmd +15 -0
  18. data/lib/chef/knife/bootstrap/windows-chef-client-msi.erb +302 -0
  19. data/lib/chef/knife/bootstrap_windows_base.rb +473 -0
  20. data/lib/chef/knife/bootstrap_windows_ssh.rb +115 -0
  21. data/lib/chef/knife/bootstrap_windows_winrm.rb +102 -0
  22. data/lib/chef/knife/core/windows_bootstrap_context.rb +356 -0
  23. data/lib/chef/knife/knife_windows_base.rb +33 -0
  24. data/lib/chef/knife/windows_cert_generate.rb +155 -0
  25. data/lib/chef/knife/windows_cert_install.rb +68 -0
  26. data/lib/chef/knife/windows_helper.rb +36 -0
  27. data/lib/chef/knife/windows_listener_create.rb +107 -0
  28. data/lib/chef/knife/winrm.rb +127 -0
  29. data/lib/chef/knife/winrm_base.rb +128 -0
  30. data/lib/chef/knife/winrm_knife_base.rb +315 -0
  31. data/lib/chef/knife/winrm_session.rb +101 -0
  32. data/lib/chef/knife/winrm_shared_options.rb +54 -0
  33. data/lib/chef/knife/wsman_endpoint.rb +44 -0
  34. data/lib/chef/knife/wsman_test.rb +118 -0
  35. data/lib/knife-winops/path_helper.rb +242 -0
  36. data/lib/knife-winops/version.rb +6 -0
  37. data/spec/assets/fake_trusted_certs/excluded.txt +2 -0
  38. data/spec/assets/fake_trusted_certs/github.pem +42 -0
  39. data/spec/assets/fake_trusted_certs/google.crt +41 -0
  40. data/spec/assets/win_fake_trusted_cert_script.txt +89 -0
  41. data/spec/dummy_winrm_connection.rb +21 -0
  42. data/spec/functional/bootstrap_download_spec.rb +229 -0
  43. data/spec/spec_helper.rb +93 -0
  44. data/spec/unit/knife/bootstrap_options_spec.rb +164 -0
  45. data/spec/unit/knife/bootstrap_template_spec.rb +98 -0
  46. data/spec/unit/knife/bootstrap_windows_winrm_spec.rb +410 -0
  47. data/spec/unit/knife/core/windows_bootstrap_context_spec.rb +292 -0
  48. data/spec/unit/knife/windows_cert_generate_spec.rb +90 -0
  49. data/spec/unit/knife/windows_cert_install_spec.rb +51 -0
  50. data/spec/unit/knife/windows_listener_create_spec.rb +76 -0
  51. data/spec/unit/knife/winrm_session_spec.rb +101 -0
  52. data/spec/unit/knife/winrm_spec.rb +494 -0
  53. data/spec/unit/knife/wsman_test_spec.rb +209 -0
  54. metadata +157 -0
@@ -0,0 +1,115 @@
1
+ #
2
+ # Original knife-windows 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/bootstrap_windows_base'
20
+
21
+ class Chef
22
+ class Knife
23
+ class BootstrapWindowsSsh < Bootstrap
24
+
25
+ include Chef::Knife::BootstrapWindowsBase
26
+
27
+ deps do
28
+ require 'chef/knife/core/windows_bootstrap_context'
29
+ require 'chef/json_compat'
30
+ require 'tempfile'
31
+ require 'highline'
32
+ require 'net/ssh'
33
+ require 'net/ssh/multi'
34
+ Chef::Knife::Ssh.load_deps
35
+ end
36
+
37
+ banner "knife bootstrap windows ssh FQDN (options)"
38
+
39
+ option :ssh_user,
40
+ :short => "-x USERNAME",
41
+ :long => "--ssh-user USERNAME",
42
+ :description => "The ssh username",
43
+ :default => "root"
44
+
45
+ option :ssh_password,
46
+ :short => "-P PASSWORD",
47
+ :long => "--ssh-password PASSWORD",
48
+ :description => "The ssh password"
49
+
50
+ option :ssh_port,
51
+ :short => "-p PORT",
52
+ :long => "--ssh-port PORT",
53
+ :description => "The ssh port",
54
+ :proc => Proc.new { |key| Chef::Config[:knife][:ssh_port] = key.strip }
55
+
56
+ option :ssh_gateway,
57
+ :short => "-G GATEWAY",
58
+ :long => "--ssh-gateway GATEWAY",
59
+ :description => "The ssh gateway",
60
+ :proc => Proc.new { |key| Chef::Config[:knife][:ssh_gateway] = key }
61
+
62
+ option :forward_agent,
63
+ :short => "-A",
64
+ :long => "--forward-agent",
65
+ :description => "Enable SSH agent forwarding",
66
+ :boolean => true
67
+
68
+ option :identity_file,
69
+ :long => "--identity-file IDENTITY_FILE",
70
+ :description => "The SSH identity file used for authentication. [DEPRECATED] Use --ssh-identity-file instead."
71
+
72
+ option :ssh_identity_file,
73
+ :short => "-i IDENTITY_FILE",
74
+ :long => "--ssh-identity-file IDENTITY_FILE",
75
+ :description => "The SSH identity file used for authentication"
76
+
77
+ # DEPR: Remove this option for the next release.
78
+ option :host_key_verification,
79
+ :long => "--[no-]host-key-verify",
80
+ :description => "Verify host key, enabled by default. [DEPRECATED] Use --host-key-verify option instead.",
81
+ :boolean => true,
82
+ :default => true,
83
+ :proc => Proc.new { |key|
84
+ Chef::Log.warn("[DEPRECATED] --host-key-verification option is deprecated. Use --host-key-verify option instead.")
85
+ config[:host_key_verify] = key
86
+ }
87
+
88
+ option :host_key_verify,
89
+ :long => "--[no-]host-key-verify",
90
+ :description => "Verify host key, enabled by default.",
91
+ :boolean => true,
92
+ :default => true
93
+
94
+ def run
95
+ validate_name_args!
96
+ bootstrap
97
+ end
98
+
99
+ def run_command(command = '')
100
+ ssh = Chef::Knife::Ssh.new
101
+ ssh.name_args = [ server_name, command ]
102
+ ssh.config[:ssh_user] = locate_config_value(:ssh_user)
103
+ ssh.config[:ssh_password] = locate_config_value(:ssh_password)
104
+ ssh.config[:ssh_port] = locate_config_value(:ssh_port)
105
+ ssh.config[:ssh_gateway] = locate_config_value(:ssh_gateway)
106
+ ssh.config[:identity_file] = config[:identity_file]
107
+ ssh.config[:ssh_identity_file] = config[:ssh_identity_file] || config[:identity_file]
108
+ ssh.config[:forward_agent] = config[:forward_agent]
109
+ ssh.config[:manual] = true
110
+ ssh.config[:host_key_verify] = config[:host_key_verify]
111
+ ssh.run
112
+ end
113
+ end
114
+ end
115
+ end
@@ -0,0 +1,102 @@
1
+ #
2
+ # Original knife-windows 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/bootstrap_windows_base'
20
+ require 'chef/knife/winrm'
21
+ require 'chef/knife/winrm_base'
22
+ require 'chef/knife/winrm_knife_base'
23
+
24
+
25
+ class Chef
26
+ class Knife
27
+ class BootstrapWindowsWinrm < Bootstrap
28
+
29
+ include Chef::Knife::BootstrapWindowsBase
30
+ include Chef::Knife::WinrmBase
31
+ include Chef::Knife::WinrmCommandSharedFunctions
32
+
33
+ deps do
34
+ require 'chef/knife/core/windows_bootstrap_context'
35
+ require 'chef/json_compat'
36
+ require 'tempfile'
37
+ Chef::Knife::Winrm.load_deps
38
+ end
39
+
40
+ banner 'knife bootstrap windows winrm FQDN (options)'
41
+
42
+ def run
43
+ validate_name_args!
44
+
45
+ if (Chef::Config[:validation_key] && !File.exist?(File.expand_path(Chef::Config[:validation_key])))
46
+ if !negotiate_auth? && !(locate_config_value(:winrm_transport) == 'ssl')
47
+ ui.error('Validatorless bootstrap over unsecure winrm channels could expose your key to network sniffing')
48
+ exit 1
49
+ end
50
+ end
51
+
52
+ unless locate_config_value(:winrm_shell) == :cmd
53
+ ui.warn("The cmd shell is the only valid winrm-shell for 'knife bootstrap windows winrm'. Switching to the cmd shell.")
54
+ config[:winrm_shell] = :cmd
55
+ end
56
+
57
+ config[:manual] = true
58
+ configure_session
59
+
60
+ bootstrap
61
+ end
62
+
63
+ protected
64
+
65
+ def wait_for_remote_response(wait_max_minutes)
66
+ wait_max_seconds = wait_max_minutes * 60
67
+ retry_interval_seconds = 10
68
+ retries_left = wait_max_seconds / retry_interval_seconds
69
+ print(ui.color("\nWaiting for remote response before bootstrap", :magenta))
70
+ wait_start_time = Time.now
71
+ begin
72
+ print(".")
73
+ # Return status of the command is non-zero, typically nil,
74
+ # for our simple echo command in cases where run_command
75
+ # swallows the exception, such as 401's. Treat such cases
76
+ # the same as the case where we encounter an exception.
77
+ status = run_command("echo . & echo Response received.")
78
+ raise RuntimeError, 'Command execution failed.' if status != 0
79
+ ui.info(ui.color("Remote node responded after #{elapsed_time_in_minutes(wait_start_time)} minutes.", :magenta))
80
+ return
81
+ rescue Errno::ECONNREFUSED => e
82
+ ui.error("Connection refused connecting to #{locate_config_value(:server_name)}:#{locate_config_value(:winrm_port)}.")
83
+ raise
84
+ rescue Exception => e
85
+ retries_left -= 1
86
+ if retries_left <= 0 || (elapsed_time_in_minutes(wait_start_time) > wait_max_minutes)
87
+ ui.error("No response received from remote node after #{elapsed_time_in_minutes(wait_start_time)} minutes, giving up.")
88
+ ui.error("Exception: #{e.message}")
89
+ raise
90
+ end
91
+ print '.'
92
+ sleep retry_interval_seconds
93
+ retry
94
+ end
95
+ end
96
+
97
+ def elapsed_time_in_minutes(start_time)
98
+ ((Time.now - start_time) / 60).round(2)
99
+ end
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,356 @@
1
+ #
2
+ # Original knife-windows 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
+ # Chef::Util::PathHelper in Chef 11 is a bit juvenile still
21
+ require 'knife-winops/path_helper'
22
+ # require 'chef/util/path_helper'
23
+ require 'chef/knife/core/windows_bootstrap_context'
24
+
25
+ class Chef
26
+ class Knife
27
+ module Core
28
+ # Instances of BootstrapContext are the context objects (i.e., +self+) for
29
+ # bootstrap templates. For backwards compatability, they +must+ set the
30
+ # following instance variables:
31
+ # * @config - a hash of knife's config values
32
+ # * @run_list - the run list for the node to boostrap
33
+ #
34
+ class WindowsBootstrapContext < BootstrapContext
35
+ PathHelper = ::Knife::Windows::PathHelper
36
+
37
+ attr_accessor :client_pem
38
+
39
+ def initialize(config, run_list, chef_config, secret=nil)
40
+ @config = config
41
+ @run_list = run_list
42
+ @chef_config = chef_config
43
+ @secret = secret
44
+ # Compatibility with Chef 12 and Chef 11 versions
45
+ begin
46
+ # Pass along the secret parameter for Chef 12
47
+ super(config, run_list, chef_config, secret)
48
+ rescue ArgumentError
49
+ # The Chef 11 base class only has parameters for initialize
50
+ super(config, run_list, chef_config)
51
+ end
52
+ end
53
+
54
+ def validation_key
55
+ if File.exist?(File.expand_path(@chef_config[:validation_key]))
56
+ IO.read(File.expand_path(@chef_config[:validation_key]))
57
+ else
58
+ false
59
+ end
60
+ end
61
+
62
+ def secret
63
+ if @config[:secret].nil?
64
+ nil
65
+ else
66
+ escape_and_echo(@config[:secret])
67
+ end
68
+ end
69
+
70
+ def trusted_certs_script
71
+ @trusted_certs_script ||= trusted_certs_content
72
+ end
73
+
74
+ def config_content
75
+ client_rb = <<-CONFIG
76
+ chef_server_url "#{@chef_config[:chef_server_url]}"
77
+ validation_client_name "#{@chef_config[:validation_client_name]}"
78
+ file_cache_path "c:/chef/cache"
79
+ file_backup_path "c:/chef/backup"
80
+ cache_options ({:path => "c:/chef/cache/checksums", :skip_expires => true})
81
+ CONFIG
82
+ if @config[:chef_node_name]
83
+ client_rb << %Q{node_name "#{@config[:chef_node_name]}"\n}
84
+ else
85
+ client_rb << "# Using default node name (fqdn)\n"
86
+ end
87
+
88
+ if @chef_config[:config_log_level]
89
+ client_rb << %Q{log_level :#{@chef_config[:config_log_level]}\n}
90
+ else
91
+ client_rb << "log_level :info\n"
92
+ end
93
+
94
+ client_rb << "log_location #{get_log_location}"
95
+
96
+ # We configure :verify_api_cert only when it's overridden on the CLI
97
+ # or when specified in the knife config.
98
+ if !@config[:node_verify_api_cert].nil? || knife_config.has_key?(:verify_api_cert)
99
+ value = @config[:node_verify_api_cert].nil? ? knife_config[:verify_api_cert] : @config[:node_verify_api_cert]
100
+ client_rb << %Q{verify_api_cert #{value}\n}
101
+ end
102
+
103
+ # We configure :ssl_verify_mode only when it's overridden on the CLI
104
+ # or when specified in the knife config.
105
+ if @config[:node_ssl_verify_mode] || knife_config.has_key?(:ssl_verify_mode)
106
+ value = case @config[:node_ssl_verify_mode]
107
+ when "peer"
108
+ :verify_peer
109
+ when "none"
110
+ :verify_none
111
+ when nil
112
+ knife_config[:ssl_verify_mode]
113
+ else
114
+ nil
115
+ end
116
+
117
+ if value
118
+ client_rb << %Q{ssl_verify_mode :#{value}\n}
119
+ end
120
+ end
121
+
122
+ if @config[:ssl_verify_mode]
123
+ client_rb << %Q{ssl_verify_mode :#{knife_config[:ssl_verify_mode]}\n}
124
+ end
125
+
126
+ if knife_config[:bootstrap_proxy]
127
+ client_rb << "\n"
128
+ client_rb << %Q{http_proxy "#{knife_config[:bootstrap_proxy]}"\n}
129
+ client_rb << %Q{https_proxy "#{knife_config[:bootstrap_proxy]}"\n}
130
+ client_rb << %Q{no_proxy "#{knife_config[:bootstrap_no_proxy]}"\n} if knife_config[:bootstrap_no_proxy]
131
+ end
132
+
133
+ if knife_config[:bootstrap_no_proxy]
134
+ client_rb << %Q{no_proxy "#{knife_config[:bootstrap_no_proxy]}"\n}
135
+ end
136
+
137
+ if @config[:secret]
138
+ client_rb << %Q{encrypted_data_bag_secret "c:/chef/encrypted_data_bag_secret"\n}
139
+ end
140
+
141
+ unless trusted_certs_script.empty?
142
+ client_rb << %Q{trusted_certs_dir "c:/chef/trusted_certs"\n}
143
+ end
144
+
145
+ if Chef::Config[:fips]
146
+ client_rb << <<-CONFIG
147
+ fips true
148
+ chef_version = ::Chef::VERSION.split(".")
149
+ unless chef_version[0].to_i > 12 || (chef_version[0].to_i == 12 && chef_version[1].to_i >= 8)
150
+ raise "FIPS Mode requested but not supported by this client"
151
+ end
152
+ CONFIG
153
+ end
154
+
155
+ escape_and_echo(client_rb)
156
+ end
157
+
158
+ def get_log_location
159
+ if @chef_config[:config_log_location].equal?(:win_evt)
160
+ %Q{:#{@chef_config[:config_log_location]}\n}
161
+ elsif @chef_config[:config_log_location].equal?(:syslog)
162
+ raise "syslog is not supported for log_location on Windows OS\n"
163
+ elsif (@chef_config[:config_log_location].equal?(STDOUT))
164
+ "STDOUT\n"
165
+ elsif (@chef_config[:config_log_location].equal?(STDERR))
166
+ "STDERR\n"
167
+ elsif @chef_config[:config_log_location].nil? || @chef_config[:config_log_location].empty?
168
+ "STDOUT\n"
169
+ elsif @chef_config[:config_log_location]
170
+ %Q{"#{@chef_config[:config_log_location]}"\n}
171
+ else
172
+ "STDOUT\n"
173
+ end
174
+ end
175
+
176
+ def chef_version_in_url
177
+ installer_version_string = nil
178
+ if @config[:bootstrap_version]
179
+ installer_version_string = "&v=#{@config[:bootstrap_version]}"
180
+ elsif @config[:prerelease]
181
+ installer_version_string = "&prerelease=true"
182
+ else
183
+ chef_version_string = if knife_config[:bootstrap_version]
184
+ knife_config[:bootstrap_version]
185
+ else
186
+ Chef::VERSION.split(".").first
187
+ end
188
+
189
+ installer_version_string = "&v=#{chef_version_string}"
190
+
191
+ # If bootstrapping a pre-release version add the prerelease query string
192
+ if chef_version_string.split(".").length > 3
193
+ installer_version_string << "&prerelease=true"
194
+ end
195
+ end
196
+
197
+ installer_version_string
198
+ end
199
+
200
+ def win_ps_write_filechunk
201
+ win_ps_write_filechunk = <<-PS_WRITEFILECHUNK
202
+ $data=$args[0]
203
+ $filename=$args[1]
204
+ $bytes = @()
205
+ if (Test-Path $filename) { $bytes = [System.IO.File]::ReadAllBytes($filename) }
206
+ $bytes += $args[0]
207
+ [io.file]::WriteAllBytes($filename,$bytes)
208
+ PS_WRITEFILECHUNK
209
+ escape_and_echo(win_ps_write_filechunk)
210
+ end
211
+
212
+ def win_cmd_wait_for_file(filename_in_envvar)
213
+ win_cmd_wait_for_file = <<-filename_in_envvar
214
+ :waitfor#{filename_in_envvar}
215
+ @if NOT EXIST "%#{filename_in_envvar}%" (
216
+ @powershell.exe -command Start-Sleep 1
217
+ @echo %#{filename_in_envvar}% does not exist yet.
218
+ @goto waitfor#{filename_in_envvar}
219
+ ) else (
220
+ @echo Logfile %#{filename_in_envvar}% found.
221
+ )
222
+ filename_in_envvar
223
+ win_cmd_wait_for_file
224
+ end
225
+
226
+ def win_cmd_tail(target_filename)
227
+ cmd_tail_file = Gem.find_files(File.join('chef', 'knife', 'bootstrap', 'tail.cmd')).first
228
+ cmd_tail_content = IO.read(cmd_tail_file)
229
+ win_parse_file_content(cmd_tail_content, target_filename)
230
+ end
231
+
232
+ def bootstrap_context
233
+ @bootstrap_context ||= Knife::Core::WindowsBootstrapContext.new(@config, @config[:run_list], Chef::Config)
234
+ end
235
+
236
+ def win_ps_bootstrap(target_filename)
237
+ ps_bootstrap_file = Gem.find_files(File.join('chef', 'knife', 'bootstrap', 'bootstrap.ps1')).first
238
+ ps_bootstrap_content = IO.read(ps_bootstrap_file)
239
+ win_parse_file_content(ps_bootstrap_content, target_filename)
240
+ end
241
+
242
+ def win_schedtask_xml(target_filename)
243
+ sched_xml_file = Gem.find_files(File.join('chef', 'knife', 'bootstrap', 'Chef_bootstrap.erb')).first
244
+ sched_xml_file_content = IO.read(sched_xml_file).chomp
245
+ win_parse_file_content(Erubis::Eruby.new(sched_xml_file_content).evaluate(bootstrap_context), target_filename)
246
+ end
247
+
248
+ def win_folder_cp(folder_src,folder_dest, folder_src_original = nil)
249
+ win_folder_cp = ''
250
+ Dir.foreach(folder_src) do |item|
251
+ next if item == '.' or item == '..'
252
+ folder_src_original = folder_src_original ? folder_src_original : folder_src
253
+ if File.file?(File.join(folder_src,item))
254
+ filename_target = File.join(folder_src,item).gsub(folder_src_original, folder_dest).gsub("/","\\")
255
+ win_folder_cp << win_parse_file_content(IO.read(File.join(folder_src,item)).force_encoding('binary'), filename_target)
256
+ else
257
+ folder = File.join(folder_src,item).gsub(folder_src_original, folder_dest).gsub("/","\\")
258
+ win_folder_cp << "mkdir #{folder}\n"
259
+ win_folder_cp << win_folder_cp(File.join(folder_src,item), folder_dest, folder_src_original)
260
+ end
261
+ end
262
+ win_folder_cp
263
+ end
264
+
265
+ def win_parse_file_content(content, target_filename)
266
+ byte_chunks = data_to_byte_chunks(content, 2000)
267
+ win_parse_file_content = ''
268
+ win_parse_file_content << "del \"#{target_filename}\" /Q 2> nul\n"
269
+ byte_chunks.each { |file_chunk|
270
+ if file_chunk.length > 0
271
+ win_parse_file_content << write_file_chunk(file_chunk, target_filename)
272
+ end
273
+ }
274
+ win_parse_file_content
275
+ end
276
+
277
+ def write_file_chunk(byte_chunk, target_filename)
278
+ "@powershell.exe -command #{bootstrap_directory}\\writefile.ps1 #{byte_chunk} #{target_filename}\n"
279
+ end
280
+
281
+ def data_to_byte_chunks(data, max_chuck_size)
282
+ chunk = ''
283
+ chunk_buffer = ''
284
+ data_to_byte_chunks = []
285
+ data.split('').each { |char|
286
+ chunk_buffer << char.ord.to_s << ','
287
+ if (chunk_buffer.length) >= max_chuck_size
288
+ data_to_byte_chunks.push(chunk_buffer.chomp(','))
289
+ chunk_buffer = ''
290
+ end
291
+ }
292
+ data_to_byte_chunks.push(chunk_buffer.chomp(','))
293
+ data_to_byte_chunks
294
+ end
295
+
296
+
297
+ def win_ps_exitcheck
298
+ <<-ps_exitcheck
299
+ @powershell.exe -command Start-Sleep 1
300
+ @echo off
301
+ REM Wait for the file to exist
302
+ :wff
303
+ if NOT EXIST "%CHEF_PS_EXITCODE%" (
304
+ goto wff
305
+ )
306
+ if EXIST %CHEF_PS_EXITCODE% (
307
+ setlocal disabledelayedexpansion
308
+ for /f "tokens=1* delims=]" %%A in ('type "%CHEF_PS_EXITCODE%"^|find /v /n ""') do (
309
+ set psexitcode=%%B
310
+ setlocal enabledelayedexpansion
311
+ )
312
+ ) else (
313
+ echo %CHEF_PS_EXITCODE% not found. This should never happen
314
+ exit 328
315
+ )
316
+ ps_exitcheck
317
+ end
318
+
319
+ def bootstrap_directory
320
+ bootstrap_directory = "C:\\chef"
321
+ end
322
+
323
+ def local_download_path
324
+ local_download_path = "%TEMP%\\chef-client-latest.msi"
325
+ end
326
+
327
+ def first_boot
328
+ escape_and_echo(super.to_json)
329
+ end
330
+
331
+ # escape WIN BATCH special chars
332
+ # and prefixes each line with an
333
+ # echo
334
+ def escape_and_echo(file_contents)
335
+ file_contents.gsub(/^(.*)$/, 'echo.\1').gsub(/([(<|>)^])/, '^\1').gsub(/(!)/, '^^\1').gsub(/(%)/,'%\1')
336
+ end
337
+
338
+ private
339
+
340
+ # Returns a string for copying the trusted certificates on the workstation to the system being bootstrapped
341
+ # This string should contain both the commands necessary to both create the files, as well as their content
342
+ def trusted_certs_content
343
+ content = ""
344
+ if @chef_config[:trusted_certs_dir]
345
+ Dir.glob(File.join(PathHelper.escape_glob_dir(@chef_config[:trusted_certs_dir]), "*.{crt,pem}")).each do |cert|
346
+ content << "> #{bootstrap_directory}/trusted_certs/#{File.basename(cert)} (\n" +
347
+ escape_and_echo(IO.read(File.expand_path(cert))) + "\n)\n"
348
+ end
349
+ end
350
+ content
351
+ end
352
+
353
+ end
354
+ end
355
+ end
356
+ end