diff --git a/plugins/provisioners/chef/command_builder_windows.rb b/plugins/provisioners/chef/command_builder_windows.rb new file mode 100644 index 000000000..8bd4f972e --- /dev/null +++ b/plugins/provisioners/chef/command_builder_windows.rb @@ -0,0 +1,83 @@ +require "tempfile" + +require "vagrant/util/template_renderer" + +module VagrantPlugins + module Chef + class CommandBuilderWindows < CommandBuilder + def build_command + binary_path = "chef-#{@client_type}" + if @config.binary_path + binary_path = File.join(@config.binary_path, binary_path) + binary_path.gsub!("/", "\\") + binary_path = "c:#{binary_path}" if binary_path.start_with?("\\") + end + + chef_arguments = "-c #{provisioning_path("#{@client_type}.rb")}" + chef_arguments << " -j #{provisioning_path("dna.json")}" + chef_arguments << " #{@config.arguments}" if @config.arguments + + command_env = "" + command_env = "#{@config.binary_env} " if @config.binary_env + + task_ps1_path = provisioning_path("cheftask.ps1") + + opts = { + user: @machine.config.winrm.username, + pass: @machine.config.winrm.password, + chef_arguments: chef_arguments, + chef_binary_path: "#{command_env}#{binary_path}", + chef_stdout_log: provisioning_path("chef-#{@client_type}.log"), + chef_stderr_log: provisioning_path("chef-#{@client_type}.err.log"), + chef_task_exitcode: provisioning_path('cheftask.exitcode'), + chef_task_running: provisioning_path('cheftask.running'), + chef_task_ps1: task_ps1_path, + chef_task_run_ps1: provisioning_path('cheftaskrun.ps1'), + chef_task_xml: provisioning_path('cheftask.xml'), + } + + # Upload the files we'll need + render_and_upload( + "cheftaskrun.ps1", opts[:chef_task_run_ps1], opts) + render_and_upload( + "cheftask.xml", opts[:chef_task_xml], opts) + render_and_upload( + "cheftask.ps1", opts[:chef_task_ps1], opts) + + return <<-EOH + $old = Get-ExecutionPolicy; + Set-ExecutionPolicy Unrestricted -force; + #{task_ps1_path}; + Set-ExecutionPolicy $old -force + EOH + end + + protected + + def provisioning_path(file) + path = "#{@config.provisioning_path}/#{file}" + path.gsub!("/", "\\") + path = "c:#{path}" if path.start_with?("\\") + path + end + + def render_and_upload(template, dest, opts) + path = File.expand_path("../scripts/#{template}", __FILE__) + data = Vagrant::Util::TemplateRenderer.render(path, options) + + file = Tempfile.new("vagrant-chef") + file.binmode + file.write(data) + file.fsync + file.close + + @machine.communicate.upload(file.path, dest) + ensure + if file + file.close + file.unlink + end + end + end + end +end diff --git a/plugins/provisioners/chef/scripts/cheftask.ps1.erb b/plugins/provisioners/chef/scripts/cheftask.ps1.erb new file mode 100644 index 000000000..f025425ff --- /dev/null +++ b/plugins/provisioners/chef/scripts/cheftask.ps1.erb @@ -0,0 +1,48 @@ +# kill the task so we can recreate it +schtasks /delete /tn "chef-solo" /f 2>&1 | out-null + +# Ensure the chef task running file doesn't exist from a previous failure +if (Test-Path "<%= options[:chef_task_running] %>") { + del "<%= options[:chef_task_running] %>" +} + +# schedule the task to run once in the far distant future +schtasks /create /tn 'chef-solo' /xml '<%= options[:chef_task_xml] %>' /ru '<%= options[:user] %>' /rp '<%= options[:pass] %>' | Out-Null + +# start the scheduled task right now +schtasks /run /tn "chef-solo" | Out-Null + +# wait for run_chef.ps1 to start or timeout after 1 minute +$timeoutSeconds = 60 +$elapsedSeconds = 0 +while ( (!(Test-Path "<%= options[:chef_task_running] %>")) -and ($elapsedSeconds -lt $timeoutSeconds) ) { + Start-Sleep -s 1 + $elapsedSeconds++ +} + +if ($elapsedSeconds -ge $timeoutSeconds) { + Write-Error "Timed out waiting for chef scheduled task to start" + exit -2 +} + +# read the entire file, but only write out new lines we haven't seen before +$numLinesRead = 0 +$success = $TRUE +while (Test-Path "<%= options[:chef_task_running] %>") { + Start-Sleep -m 100 + + if (Test-Path "<%= options[:chef_stdout_log] %>") { + $text = (get-content "<%= options[:chef_stdout_log] %>") + $numLines = ($text | Measure-Object -line).lines + $numLinesToRead = $numLines - $numLinesRead + + if ($numLinesToRead -gt 0) { + $text | select -first $numLinesToRead -skip $numLinesRead | ForEach { + Write-Host "$_" + } + $numLinesRead += $numLinesToRead + } + } +} + +exit Get-Content "<%= options[:chef_task_exitcode] %>" diff --git a/plugins/provisioners/chef/scripts/cheftask.xml.erb b/plugins/provisioners/chef/scripts/cheftask.xml.erb new file mode 100644 index 000000000..6b598ec4c --- /dev/null +++ b/plugins/provisioners/chef/scripts/cheftask.xml.erb @@ -0,0 +1,45 @@ + + + + 2013-06-21T22:41:43 + Administrator + + + + 2045-01-01T12:00:00 + true + + + + + vagrant + Password + HighestAvailable + + + + IgnoreNew + false + false + true + false + false + + true + false + + true + true + false + false + false + PT2H + 4 + + + + powershell + -file <%= options[:chef_task_run_ps1] %> + + + diff --git a/plugins/provisioners/chef/scripts/cheftaskrun.ps1.erb b/plugins/provisioners/chef/scripts/cheftaskrun.ps1.erb new file mode 100644 index 000000000..0d4a43bf4 --- /dev/null +++ b/plugins/provisioners/chef/scripts/cheftaskrun.ps1.erb @@ -0,0 +1,18 @@ +$exitCode = -1 +Set-ExecutionPolicy Unrestricted -force; + +Try +{ + "running" | Out-File "<%= options[:chef_task_running] %>" + $process = (Start-Process "<%= options[:chef_binary_path] %>" -ArgumentList "<%= options[:chef_arguments] %>" -NoNewWindow -PassThru -Wait -RedirectStandardOutput "<%= options[:chef_stdout_log] %>" -RedirectStandardError "<%= options[:chef_stderr_log] %>") + $exitCode = $process.ExitCode +} +Finally +{ + $exitCode | Out-File "<%= options[:chef_task_exitcode] %>" + if (Test-Path "<%= options[:chef_task_running] %>") { + del "<%= options[:chef_task_running] %>" + } +} + +exit $exitCode