Feature #8268: Basic Puppet agent support on Windows
Exec provider for Windows
|Assignee:||Josh Cooper||% Done:|
|Category:||windows||Estimated time:||32.00 hours|
|Affected Puppet version:||development||Branch:|
Basic ability to run commands.
Commands must either be present in PATH, or fully specified.
#1 Updated by Josh Cooper over 2 years ago
- Category set to windows
- Estimated time set to 32.00
The exec provider will likely call SUIDManager.run_and_capture, which calls Puppet::Util.execute, but there are three problems on Windows:
- The call to Process.create (using win32/process) returns a ProcessInfo struct instead of an array. It needs to call the waitpid2 method with the ProcessInfo.process_id field. Also note that the Process.create method closes the child process HANDLE (in the ProcessInfo struct), so don’t try to use that HANDLE. See http://rubydoc.info/gems/win32-process/0.6.5/Process:create
- On Unix, the parent forks a child, and the child perform the exec. But on Windows, the parent process calls Process.create directly and it is not wrapped in a begin-ensure block, so if there is an error, e.g. program doesn’t exist, the execute method won’t raise an ExecutionFailure like it does on Unix.
- When running spec/integration/parser/compiler_spec.rb, the command passed to Puppet::Util.execute is an array containing a single string [“git rev-parse HEAD”]. On Unix, it calls Kernel.exec(*command), but on Windows it calls Process.create(:command_line =>command), which attempts to execute the program called “git rev-parse HEAD” and fails:
Process::Error: CreateProcess() failed: The system cannot find the file specified. # ./lib/puppet/util.rb:331:in `execute' # ./lib/puppet/resource/type_collection.rb:141:in `version' # ./lib/puppet/parser/compiler.rb:433:in `initvars' # ./lib/puppet/parser/compiler.rb:191:in `initialize' # ./spec/integration/parser/compiler_spec.rb:24:in `new' # ./spec/integration/parser/compiler_spec.rb:24
#3 Updated by Josh Cooper over 2 years ago
Btw, %x does not work when the command is located in a directory with a space.
For example, msysgit is installed in C:\Program Files (x86)\Git. If I add C:\Program Files (x86)\Git\bin to my PATH it fails:
Z:\work\puppet>set PATH="c:\Program Files (x86)\Git\bin";%PATH% Z:\work\puppet>ruby -e "puts `git rev-parse HEAD`" -e:1:in ``': No such file or directory - git rev-parse HEAD (Errno::ENOENT)
But setting the path using short names works:
set PATH=c:\progra~2\git\bin;%PATH% Z:\work\puppet>ruby -e "puts `git rev-parse HEAD`" 922edb28d2eeeea7812b02c7064bc5930aa1aaa5
#5 Updated by Nick Lewis over 2 years ago
- Status changed from Accepted to In Topic Branch Pending Review
Branch available at https://github.com/nicklewis/puppet/tree/ticket/2.7.x/8410. Pull request https://github.com/puppetlabs/puppet/pull/54.
This provider has full support for the exec type, except it is unable to run commands as other users/groups. If they are specified, it will fail with a message indicating such. The implementation of the posix provider has been moved into a base class, Puppet::Provider::Exec, from which the others inherit. The difference between the posix and windows providers is now only in how they look for their command to validate its existence (the windows provider will look for cmd.exe, cmd.bat, cmd.ps1, and cmd.com, in addition to just cmd).
This branch also includes a fairly large refactor of Puppet::Util.execute, splitting the bulk of the functionality involved with actually running commands into two platform-specific methods, execute_posix and execute_windows.
Additionally, there is a platform-aware helper method Puppet::Util.absolute_path?(path, platform) which will return true if the path is an absolute path on the platform specified (:posix or :windows), and will default to the current platform if none is specified. This can be used in the future to validate paths on a non-Windows master which need to be used on a Windows agent.
#8 Updated by Josh Cooper about 2 years ago
- Status changed from Closed to Re-opened
- Assignee changed from Nick Lewis to Josh Cooper
The win32/process gem version of waitpid2 returns a different structure than the default ruby implementation. As a result, if we execute a child process and it returns a non-zero return code, then we attempt to access the last element in the array, which we assume is a Process::Status, but is actually the child exit code. I have a fix for this here: https://github.com/puppetlabs/puppet/pull/112