summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAdam Edwards <adamed@opscode.com>2014-10-23 12:30:44 -0700
committerAdam Edwards <adamed@opscode.com>2014-10-23 12:30:44 -0700
commit4e9b9ff856632a16559afc52fc4651340986c2e3 (patch)
tree689b188328e2bb060cb06cf29f712534030ac6f3
parent94dc0364dfa99dba3a5dbfbefeb08f41926b5468 (diff)
parenteef5c6b5cc36b6a4e92a8bd49459ad5ed7019040 (diff)
downloadchef-4e9b9ff856632a16559afc52fc4651340986c2e3.tar.gz
Merge pull request #1495 from opscode/adamed/guard-interpreter-default
guard_interpreter default change for powershell_script, batch resources
-rw-r--r--CHANGELOG.md1
-rw-r--r--DOC_CHANGES.md22
-rw-r--r--RELEASE_NOTES.md71
-rw-r--r--lib/chef/provider/powershell_script.rb4
-rw-r--r--lib/chef/resource/powershell_script.rb2
-rw-r--r--lib/chef/resource/windows_script.rb3
-rw-r--r--spec/functional/resource/batch_spec.rb15
-rw-r--r--spec/functional/resource/powershell_spec.rb50
-rw-r--r--spec/support/shared/functional/windows_script.rb118
-rw-r--r--spec/support/shared/unit/script_resource.rb2
-rw-r--r--spec/support/shared/unit/windows_script_resource.rb37
11 files changed, 289 insertions, 36 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index c0f58e7349..edc5190296 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -138,6 +138,7 @@
### Chef Contributions
+* Default `guard_interpreter` for `powershell_script` resource set to `:powershell_script`, for `batch` to `:batch`
* Recipe definition now returns the retval of the definition
* Add support for Windows 10 to version helper.
* `dsc_script` resource should honor configuration parameters when `configuration_data_script` is not set (Issue #2209)
diff --git a/DOC_CHANGES.md b/DOC_CHANGES.md
index 08c343809a..7eb85eb894 100644
--- a/DOC_CHANGES.md
+++ b/DOC_CHANGES.md
@@ -521,3 +521,25 @@ end
Chef will then execute the Homebrew command as that user. The `homebrew_user` attribute can only be provided to the
`homebrew_package` resource, not the `package` resource.
+
+### Default `guard_interpreter` attribute for `powershell_script` resource
+
+For the `powershell_script` resource, the `guard_interpreter` attribute is set to `:powershell_script` by default. This means
+that if a string is supplied to an `only_if` or `not_if` attribute of a `powersell_script` resource, the PowerShell command
+interpreter (the 64-bit version) will be used to evaluate the guard. It also means that other features available to the guard
+when `guard_interpreter` is set to something other than `:default`, such as inheritance of attributes and the specification of
+process architectur of the guard process (i.e. 32-bit or 64-bit process) are available by default.
+
+In versions of Chef prior to Chef 12, the value of the attribute was `:default` by default, which uses the 32-bit version of the
+`cmd.exe` (batch script language) shell to evaluate strings supplied to guards.
+
+### Default `guard_interpreter` attribute for `batch` resource
+
+For the`batch` resource, the `guard_interpreter` attribute it is set to `:batch` by default. This means
+that if a string is supplied to an `only_if` or `not_if` attribute of a `batch` resource, the 64-bit version of the Windows
+default command interpreter, `cmd.exe`, will be used to evaluate the guard. It also means that other features available to the guard
+when `guard_interpreter` is set to something other than `:default`, such as inheritance of attributes and the specification of
+process architectur of the guard process (i.e. 32-bit or 64-bit process) are available by default.
+
+In versions of Chef prior to Chef 12, the value of the attribute was `:default` by default, which means the 32-bit version of the
+`cmd.exe` (batch script language) shell would be used to evaluate strings supplied to guards.
diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md
index 9b316516e4..7ecabc770c 100644
--- a/RELEASE_NOTES.md
+++ b/RELEASE_NOTES.md
@@ -344,3 +344,74 @@ You can read more about this RFC [here](https://github.com/opscode/chef-rfc/blob
Chef 12 now supports managing services on AIX, using both the SRC (Subsystem Resource Controller) as well as the BSD-style init system. SRC is the default; the BSD-style provider can be selected using `Chef::Provider::Service::AixInit`.
The SRC service provider will manage services as well as service groups. However, because SRC has no standard mechanism for starting services on system boot, `action :enable` and `action :disable` are not supported for SRC services. You may use the `execute` resource to invoke `mkitab`, for example, to add lines to `/etc/inittab` with the right parameters.
+
+## `guard_interpreter` attribute for `powershell_script` defaults to `:powershell_script`
+The default `guard_interpreter` attribute for the `powershell_script` resource is `:powershell_script`. This means that the
+64-bit version of the PowerShell shell will be used to evaluate strings supplied to the `not_if` or `only_if` attributes of the
+resource. Prior to this release, the default value was `:default`, which used the 32-bit version of the `cmd.exe` shell to evaluate the guard.
+
+If you are using guard expressions with the `powershell_script` resource in your recipes, you should override the
+`guard_interpreter` attribute to restore the behavior of guards for this resource in Chef 11:
+
+```ruby
+# The not_if will be evaluated with 64-bit PowerShell by default,
+# So override it to :default if your guard assumes 32-bit cmd.exe
+powershell_script 'make_safe_backup' do
+ guard_interpreter :default # Chef 11 behavior
+ code 'cp ~/data/nodes.json $env:systemroot/system32/data/nodes.bak'
+
+ # cmd.exe (batch) guard below behaves differently in 32-bit vs. 64-bit processes
+ not_if 'if NOT EXIST %SYSTEMROOT%\\system32\\data\\nodes.bak exit /b 1'
+end
+```
+
+If the code in your guard expression does not rely on the `cmd.exe` interpreter, e.g. it simply executes a process that should
+return an exit code such as `findstr datafile sentinelvalue`, and does not rely on being executed from a 32-bit process, then it
+should function identically when executed from the PowerShell shell and it is not necessary to override the attribute
+to`:default` to restore Chef 11 behavior.
+
+Note that with this change guards for the `powershell_script` resource will also inherit some attributes like `:architecture`, `:cwd`,
+`:environment`, and `:path`.
+
+## `guard_interpreter` attribute for `batch` resource defaults to `:batch`
+
+The default `guard_interpreter` attribute for the `batch` resource is now `:batch`. This means that the
+64-bit version of the `cmd.exe` shell will be used to evaluate strings supplied to the `not_if` or `only_if` attributes of the
+resource. Prior to this release, the default value was `:default`, which used the 32-bit version of the `cmd.exe` shell to evaluate the guard.
+
+Note that with this change guards for the `batch` resource will also inherit some attributes like `:architecture`, `:cwd`,
+`:environment`, and `:path`.
+
+Unless the code you supply to guard attributes (`only_if` and `not_if`) has logic that requires that the 32-bit version of
+`cmd.exe` be used to evaluate the guard or you need to avoid the inheritance behavior of guard options, that code should function identically in this release of Chef and Chef 11 releases.
+
+If an assumption of a 32-bit process for guard evaluation exists in your code, you can obtain the equivalent of Chef 11's 32-bit
+process behavior by supplying an architecture attribute to the guard as follows:
+
+```ruby
+# The not_if will be evaluated with 64-bit cmd.exe by default,
+# so you can verride it with the :architecture guard option to
+# make it 32-bit as it is in Chef 11
+batch 'make_safe_backup' do
+ code 'copy %USERPROFILE%\\data\\nodes.json %SYSTEMROOT%\\system32\\data\\nodes.bak'
+
+ # cmd.exe (batch) guard code below behaves differently in 32-bit vs. 64-bit processes
+ not_if 'if NOT EXIST %SYSTEMROOT%\\system32\\data\\nodes.bak exit /b 1', :architecture => :i386
+end
+```
+
+If in addition to the 32-bit process assumption you also need to avoid the inheritance behavior, you can revert completely to
+the Chef 11's 32-bit process, no inheritance behavior by supplying `:default` for the `guard_interpreter` as follows:
+
+```ruby
+# The not_if will be evaluated with 64-bit cmd.exe by default,
+# so override it to :default if your guard assumes 32-bit cmd.exe
+batch 'make_safe_backup' do
+ guard_interpreter :default # Revert to Chef 11 behavior
+ code 'copy %USERPROFILE%\\data\\nodes.json %SYSTEMROOT%\\system32\\data\\nodes.bak'
+
+ # cmd.exe (batch) guard code below behaves differently in 32-bit vs. 64-bit processes
+ not_if 'if NOT EXIST %SYSTEMROOT%\\system32\\data\\nodes.bak exit /b 1'
+end
+```
+
diff --git a/lib/chef/provider/powershell_script.rb b/lib/chef/provider/powershell_script.rb
index 967b2d822b..0e76cd1656 100644
--- a/lib/chef/provider/powershell_script.rb
+++ b/lib/chef/provider/powershell_script.rb
@@ -51,6 +51,8 @@ $chefscriptresult = {
}.invokereturnasis()
if ($interpolatedexitcode -and $chefscriptresult.gettype().name -eq 'boolean') { exit [int32](!$chefscriptresult) } else { exit 0 }
EOH
+ Chef::Log.debug("powershell_script provider called with script code:\n\n#{code}\n")
+ Chef::Log.debug("powershell_script provider will execute transformed code:\n\n#{@code}\n")
end
public
@@ -65,7 +67,7 @@ EOH
"-NoLogo",
"-NonInteractive",
"-NoProfile",
- "-ExecutionPolicy RemoteSigned",
+ "-ExecutionPolicy Unrestricted",
# Powershell will hang if STDIN is redirected
# http://connect.microsoft.com/PowerShell/feedback/details/572313/powershell-exe-can-hang-if-stdin-is-redirected
"-InputFormat None",
diff --git a/lib/chef/resource/powershell_script.rb b/lib/chef/resource/powershell_script.rb
index 1b47e7411a..a88fb5701b 100644
--- a/lib/chef/resource/powershell_script.rb
+++ b/lib/chef/resource/powershell_script.rb
@@ -21,8 +21,6 @@ class Chef
class Resource
class PowershellScript < Chef::Resource::WindowsScript
- set_guard_inherited_attributes(:architecture)
-
def initialize(name, run_context=nil)
super(name, run_context, :powershell_script, "powershell.exe")
@convert_boolean_return = false
diff --git a/lib/chef/resource/windows_script.rb b/lib/chef/resource/windows_script.rb
index 108891e9ba..6b0827b77c 100644
--- a/lib/chef/resource/windows_script.rb
+++ b/lib/chef/resource/windows_script.rb
@@ -23,12 +23,15 @@ class Chef
class Resource
class WindowsScript < Chef::Resource::Script
+ set_guard_inherited_attributes(:architecture)
+
protected
def initialize(name, run_context, resource_name, interpreter_command)
super(name, run_context)
@interpreter = interpreter_command
@resource_name = resource_name
+ @default_guard_interpreter = resource_name
end
include Chef::Mixin::WindowsArchitectureHelper
diff --git a/spec/functional/resource/batch_spec.rb b/spec/functional/resource/batch_spec.rb
index baa01ee5d9..39133fd40b 100644
--- a/spec/functional/resource/batch_spec.rb
+++ b/spec/functional/resource/batch_spec.rb
@@ -21,17 +21,10 @@ require 'spec_helper'
describe Chef::Resource::WindowsScript::Batch, :windows_only do
include_context Chef::Resource::WindowsScript
- let(:script_content) { "whoami" }
+ let(:output_command) { ' > ' }
- let!(:resource) do
- Chef::Resource::WindowsScript::Batch.new("Batch resource functional test", @run_context)
- end
+ let (:architecture_command) { '@echo %PROCESSOR_ARCHITECTURE%' }
+
+ it_behaves_like "a Windows script running on Windows"
- describe "when the run action is invoked on Windows" do
- it "executes the script code" do
- resource.code(script_content + " > #{script_output_path}")
- resource.returns(0)
- resource.run_action(:run)
- end
- end
end
diff --git a/spec/functional/resource/powershell_spec.rb b/spec/functional/resource/powershell_spec.rb
index 96a356f441..e1e9f787a3 100644
--- a/spec/functional/resource/powershell_spec.rb
+++ b/spec/functional/resource/powershell_spec.rb
@@ -22,6 +22,12 @@ describe Chef::Resource::WindowsScript::PowershellScript, :windows_only do
include_context Chef::Resource::WindowsScript
+ let (:architecture_command) { 'echo $env:PROCESSOR_ARCHITECTURE' }
+ let (:output_command) { ' | out-file -encoding ASCII ' }
+
+ it_behaves_like "a Windows script running on Windows"
+
+
let(:successful_executable_script_content) { "#{ENV['SystemRoot']}\\system32\\attrib.exe $env:systemroot" }
let(:failed_executable_script_content) { "#{ENV['SystemRoot']}\\system32\\attrib.exe /badargument" }
let(:processor_architecture_script_content) { "echo $env:PROCESSOR_ARCHITECTURE" }
@@ -36,6 +42,7 @@ describe Chef::Resource::WindowsScript::PowershellScript, :windows_only do
let(:arbitrary_nonzero_process_exit_code_content) { "exit #{arbitrary_nonzero_process_exit_code}" }
let(:invalid_powershell_interpreter_flag) { "/thisflagisinvalid" }
let(:valid_powershell_interpreter_flag) { "-Sta" }
+
let!(:resource) do
r = Chef::Resource::WindowsScript::PowershellScript.new("Powershell resource functional test", @run_context)
r.code(successful_executable_script_content)
@@ -214,32 +221,36 @@ describe Chef::Resource::WindowsScript::PowershellScript, :windows_only do
before(:each) do
resource.not_if.clear
resource.only_if.clear
- # resource.guard_interpreter should be :default by default
end
- it "evaluates a succeeding not_if block using cmd.exe as false by default" do
- resource.not_if "exit /b 0"
- resource.should_skip?(:run).should be_true
- end
+ context "when the guard_interpreter's default value of :powershell_script is overridden to :default" do
+ before(:each) do
+ resource.guard_interpreter :default
+ end
- it "evaluates a failing not_if block using cmd.exe as true by default" do
- resource.not_if "exit /b 2"
- resource.should_skip?(:run).should be_false
- end
+ it "evaluates a succeeding not_if block using cmd.exe as false by default" do
+ resource.not_if "exit /b 0"
+ resource.should_skip?(:run).should be_true
+ end
- it "evaluates an succeeding only_if block using cmd.exe as true by default" do
- resource.only_if "exit /b 0"
- resource.should_skip?(:run).should be_false
- end
+ it "evaluates a failing not_if block using cmd.exe as true by default" do
+ resource.not_if "exit /b 2"
+ resource.should_skip?(:run).should be_false
+ end
+
+ it "evaluates an succeeding only_if block using cmd.exe as true by default" do
+ resource.only_if "exit /b 0"
+ resource.should_skip?(:run).should be_false
+ end
- it "evaluates a failing only_if block using cmd.exe as false by default" do
- resource.only_if "exit /b 2"
- resource.should_skip?(:run).should be_true
+ it "evaluates a failing only_if block using cmd.exe as false by default" do
+ resource.only_if "exit /b 2"
+ resource.should_skip?(:run).should be_true
+ end
end
context "the only_if is specified before the guard" do
before do
- # force the guard_interpreter to :default in case the default changes later
resource.guard_interpreter :default
end
@@ -251,8 +262,9 @@ describe Chef::Resource::WindowsScript::PowershellScript, :windows_only do
end
context "with powershell_script as the guard_interpreter" do
- before(:each) do
- resource.guard_interpreter :powershell_script
+
+ it "has a guard_interpreter attribute set to :powershell_script" do
+ expect(resource.guard_interpreter).to eq(:powershell_script)
end
it "evaluates a powershell $false for a not_if block as true" do
diff --git a/spec/support/shared/functional/windows_script.rb b/spec/support/shared/functional/windows_script.rb
index fc06fb55d0..f677828167 100644
--- a/spec/support/shared/functional/windows_script.rb
+++ b/spec/support/shared/functional/windows_script.rb
@@ -44,4 +44,122 @@ shared_context Chef::Resource::WindowsScript do
after(:each) do
File.delete(script_output_path) if File.exists?(script_output_path)
end
+
+ let!(:resource) do
+ Chef::Resource::WindowsScript::Batch.new("Batch resource functional test", @run_context)
+ end
+
+ shared_examples_for "a script resource with architecture attribute" do
+ context "with the given architecture attribute value" do
+ let(:resource_architecture) { architecture }
+ let(:expected_architecture) do
+ if architecture
+ expected_architecture = architecture
+ else
+ expected_architecture = :i386
+ end
+ end
+ let(:expected_architecture_output) do
+ expected_architecture == :i386 ? 'X86' : 'AMD64'
+ end
+ let(:guard_script_suffix) do
+ "guard"
+ end
+ let(:guard_script_output_path) do
+ "#{script_output_path}#{guard_script_suffix}"
+ end
+ let(:resource_command) do
+ "#{architecture_command} #{output_command} #{script_output_path}"
+ end
+ let(:resource_guard_command) do
+ "#{architecture_command} #{output_command} #{guard_script_output_path}"
+ end
+
+ before(:each) do
+ resource.code resource_command
+ (resource.architecture architecture) if architecture
+ resource.returns(0)
+ end
+
+ it "should create a process with the expected architecture" do
+ resource.run_action(:run)
+ get_process_architecture.should == expected_architecture_output.downcase
+ end
+
+ it "should execute guards with the same architecture as the resource" do
+ resource.only_if resource_guard_command
+ resource.run_action(:run)
+ get_process_architecture.should == expected_architecture_output.downcase
+ get_guard_process_architecture.should == expected_architecture_output.downcase
+ get_guard_process_architecture.should == get_process_architecture
+ end
+
+ let (:architecture) { :x86_64 }
+ it "should execute a 64-bit guard if the guard's architecture is specified as 64-bit" do
+ resource.only_if resource_guard_command, :architecture => :x86_64
+ resource.run_action(:run)
+ get_guard_process_architecture.should == 'amd64'
+ end
+
+ let (:architecture) { :i386 }
+ it "should execute a 32-bit guard if the guard's architecture is specified as 32-bit" do
+ resource.only_if resource_guard_command, :architecture => :i386
+ resource.run_action(:run)
+ get_guard_process_architecture.should == 'x86'
+ end
+ end
+ end
+
+ shared_examples_for "a Windows script running on Windows" do
+
+ describe "when the run action is invoked on Windows" do
+ it "executes the script code" do
+ resource.code("@whoami > #{script_output_path}")
+ resource.returns(0)
+ resource.run_action(:run)
+ end
+ end
+
+ context "when evaluating guards" do
+ it "has a guard_interpreter attribute set to the short name of the resource" do
+ resource.guard_interpreter.should == resource.resource_name
+ resource.not_if "findstr.exe /thiscommandhasnonzeroexitstatus"
+ expect(Chef::Resource).to receive(:resource_for_node).and_call_original
+ expect(resource.class).to receive(:new).and_call_original
+ resource.should_skip?(:run).should be_false
+ end
+ end
+
+ context "when the architecture attribute is not set" do
+ let(:architecture) { nil }
+ it_behaves_like "a script resource with architecture attribute"
+ end
+
+ context "when the architecture attribute is :i386" do
+ let(:architecture) { :i386 }
+ it_behaves_like "a script resource with architecture attribute"
+ end
+
+ context "when the architecture attribute is :x86_64" do
+ let(:architecture) { :x86_64 }
+ it_behaves_like "a script resource with architecture attribute"
+ end
+ end
+
+ def get_windows_script_output(suffix = '')
+ File.read("#{script_output_path}#{suffix}")
+ end
+
+ def source_contains_case_insensitive_content?( source, content )
+ source.downcase.include?(content.downcase)
+ end
+
+ def get_guard_process_architecture
+ get_process_architecture(guard_script_suffix)
+ end
+
+ def get_process_architecture(suffix = '')
+ get_windows_script_output(suffix).strip.downcase
+ end
+
end
diff --git a/spec/support/shared/unit/script_resource.rb b/spec/support/shared/unit/script_resource.rb
index 1137958420..a34f930fc8 100644
--- a/spec/support/shared/unit/script_resource.rb
+++ b/spec/support/shared/unit/script_resource.rb
@@ -72,8 +72,8 @@ shared_examples_for "a script resource" do
it "when guard_interpreter is set to the default value, the guard command string should be evaluated by command execution and not through a resource" do
Chef::Resource::Conditional.any_instance.should_not_receive(:evaluate_block)
- Chef::Resource::Conditional.any_instance.should_receive(:evaluate_command).and_return(true)
Chef::GuardInterpreter::ResourceGuardInterpreter.any_instance.should_not_receive(:evaluate_action)
+ Chef::GuardInterpreter::DefaultGuardInterpreter.any_instance.should_receive(:evaluate).and_return(true)
resource.only_if 'echo hi'
resource.should_skip?(:run).should == nil
end
diff --git a/spec/support/shared/unit/windows_script_resource.rb b/spec/support/shared/unit/windows_script_resource.rb
index 23dbfbe722..888ad600c5 100644
--- a/spec/support/shared/unit/windows_script_resource.rb
+++ b/spec/support/shared/unit/windows_script_resource.rb
@@ -39,8 +39,41 @@ shared_examples_for "a Windows script resource" do
@resource.should be_a_kind_of(Chef::Resource::WindowsScript)
end
- context "script" do
- let(:script_resource) { resource_instance }
+ context "when evaluating guards" do
+ it "should have a default_guard_interpreter attribute that is the same as the resource" do
+ @resource.default_guard_interpreter.should == @resource.resource_name
+ end
+
+ it "should default to using guard_interpreter attribute that is the same as the resource" do
+ @resource.guard_interpreter.should == @resource.resource_name
+ end
+
+ it "should use a resource to evaluate the guard when guard_interpreter is not specified" do
+ Chef::GuardInterpreter::ResourceGuardInterpreter.any_instance.should_receive(:evaluate_action).and_return(true)
+ Chef::GuardInterpreter::DefaultGuardInterpreter.any_instance.should_not_receive(:evaluate)
+ @resource.only_if 'echo hi'
+ @resource.should_skip?(:run).should == nil
+ end
+
+ describe "when the guard is given a ruby block" do
+ it "should evaluate the guard if the guard_interpreter is set to its default value" do
+ @resource.only_if { true }
+ @resource.should_skip?(:run).should == nil
+ end
+
+ it "should raise an exception if the guard_interpreter is overridden from its default value" do
+ @resource.guard_interpreter :bash
+ @resource.only_if { true }
+ expect { @resource.should_skip?(:run) }.to raise_error
+ end
+ end
+ end
+
+ context "script with a default guard interpreter" do
+ let(:script_resource) do
+ resource_instance.guard_interpreter :default
+ resource_instance
+ end
it_should_behave_like "a script resource"
end