diff options
author | Tim Smith <tsmith@chef.io> | 2020-10-05 10:05:02 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-10-05 10:05:02 -0700 |
commit | c93f5fe83898d991188898a1d72f455afad15198 (patch) | |
tree | 86bb6c521f9ae1434f6bdac70001b640c98f85ff /lib | |
parent | 226f03f9d030b90c9e58617a8be60141604362d3 (diff) | |
parent | 751bd6577681aa3d5db9273c4edb007e8136f940 (diff) | |
download | chef-c93f5fe83898d991188898a1d72f455afad15198.tar.gz |
Merge pull request #10476 from chef/exec_core
Signed-off-by: Tim Smith <tsmith@chef.io>
Diffstat (limited to 'lib')
-rw-r--r-- | lib/chef/mixin/powershell_exec.rb | 32 | ||||
-rw-r--r-- | lib/chef/powershell.rb | 5 | ||||
-rw-r--r-- | lib/chef/pwsh.rb | 61 |
3 files changed, 86 insertions, 12 deletions
diff --git a/lib/chef/mixin/powershell_exec.rb b/lib/chef/mixin/powershell_exec.rb index e3410e007f..03b058edcf 100644 --- a/lib/chef/mixin/powershell_exec.rb +++ b/lib/chef/mixin/powershell_exec.rb @@ -16,11 +16,14 @@ # limitations under the License. require_relative "../powershell" +require_relative "../pwsh" -# The powershell_exec mixin provides in-process access to PowerShell engine via -# a COM interop (installed by the Chef Client installer). +# The powershell_exec mixin provides in-process access to the PowerShell engine. # -# powershell_exec returns a Chef::PowerShell object that provides 4 methods: +# powershell_exec is initialized with a string that should be set to the script +# to run and also takes an optional interpreter argument which must be either +# :powershell (Windows PowerShell which is the default) or :pwsh (PowerShell +# Core). It will return a Chef::PowerShell object that provides 4 methods: # # .result - returns a hash representing the results returned by executing the # PowerShell script block @@ -42,6 +45,9 @@ require_relative "../powershell" # > powershell_exec("$a = $true; $a").result # => true # +# > powershell_exec("$PSVersionTable", :pwsh).result["PSEdition"] +# => "Core" +# # > powershell_exec("not-found").errors # => ["ObjectNotFound: (not-found:String) [], CommandNotFoundException: The # term 'not-found' is not recognized as the name of a cmdlet, function, script @@ -90,22 +96,28 @@ require_relative "../powershell" class Chef module Mixin module PowershellExec - # Run a command under PowerShell via a managed (.NET) COM interop API. - # This implementation requires the managed dll to be registered on the - # target machine. + # Run a command under PowerShell via a managed (.NET) API. # # Requires: .NET Framework 4.0 or higher on the target machine. # # @param script [String] script to run + # @param interpreter [Symbol] the interpreter type, `:powershell` or `:pwsh` # @return [Chef::PowerShell] output - def powershell_exec(script) - Chef::PowerShell.new(script) + def powershell_exec(script, interpreter = :powershell) + case interpreter + when :powershell + Chef::PowerShell.new(script) + when :pwsh + Chef::Pwsh.new(script) + else + raise ArgumentError, "Expected interpreter of :powershell or :pwsh" + end end # The same as the #powershell_exec method except this will raise # Chef::PowerShell::CommandFailed if the command fails - def powershell_exec!(script) - cmd = Chef::PowerShell.new(script) + def powershell_exec!(script, interpreter = :powershell) + cmd = powershell_exec(script, interpreter) cmd.error! cmd end diff --git a/lib/chef/powershell.rb b/lib/chef/powershell.rb index 10765384ff..5063e599c6 100644 --- a/lib/chef/powershell.rb +++ b/lib/chef/powershell.rb @@ -36,6 +36,7 @@ class Chef def initialize(script) raise "Chef::PowerShell can only be used on the Windows platform." unless RUBY_PLATFORM.match?(/mswin|mingw32|windows/) + @dll ||= "Chef.PowerShell.Wrapper.dll" exec(script) end @@ -59,10 +60,10 @@ class Chef raise Chef::PowerShell::CommandFailed, "Unexpected exit in PowerShell command: #{@errors}" if error? end - private + protected def exec(script) - FFI.ffi_lib "Chef.PowerShell.Wrapper.dll" + FFI.ffi_lib @dll FFI.attach_function :execute_powershell, :ExecuteScript, [:string], :pointer execution = FFI.execute_powershell(script).read_utf16string hashed_outcome = Chef::JSONCompat.parse(execution) diff --git a/lib/chef/pwsh.rb b/lib/chef/pwsh.rb new file mode 100644 index 0000000000..41087e3541 --- /dev/null +++ b/lib/chef/pwsh.rb @@ -0,0 +1,61 @@ +# +# Author:: Matt Wrock (<mwrock@chef.io>) +# Copyright:: Copyright (c) Chef Software Inc. +# License:: Apache License, Version 2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +class Chef + class Pwsh < Chef::PowerShell + + # Run a command under pwsh (powershell core) via FFI + # This implementation requires the managed dll, native wrapper and a + # published, self contained dotnet core directory tree to exist in the + # bindir directory. + # + # @param script [String] script to run + # @return [Object] output + def initialize(script) + @dll = Pwsh.dll + super + end + + protected + + def exec(script) + # Note that we need to override the location of the shared dotnet core library + # location. With most .net core applications, you can simply publish them as a + # "self-contained" application allowing consumers of the application to run them + # and use its own stand alone version of the .net core runtime. However because + # this is simply a dll and not an exe, it will look for the runtime in the shared + # .net core installation folder. By setting DOTNET_MULTILEVEL_LOOKUP to 0 we can + # override that folder's location with DOTNET_ROOT. To avoid the possibility of + # interfering with other .net core processes that might rely on the common shared + # location, we revert these variables after the script completes. + original_dml = ENV["DOTNET_MULTILEVEL_LOOKUP"] + original_dotnet_root = ENV["DOTNET_ROOT"] + + ENV["DOTNET_MULTILEVEL_LOOKUP"] = "0" + ENV["DOTNET_ROOT"] = RbConfig::CONFIG["bindir"] + + super + ensure + ENV["DOTNET_MULTILEVEL_LOOKUP"] = original_dml + ENV["DOTNET_ROOT"] = original_dotnet_root + end + + def self.dll + @dll ||= Dir.glob("#{RbConfig::CONFIG["bindir"]}/**/Chef.PowerShell.Wrapper.Core.dll").last + end + end +end |