knife-winops 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
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