summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAdam Edwards <adamed@opscode.com>2014-03-30 00:29:24 -0700
committerAdam Edwards <adamed@opscode.com>2014-03-30 00:29:24 -0700
commitdaa0f2af387df26bf254d5d287c4a27934540b97 (patch)
tree2624da050d3c0d843d6afa1ce8e00e3d38359454
parent852cadd9040c71759bbeb55311ebc7a8cff27150 (diff)
parentd8d8ae0e97a7d8a2f7b6a3217407e5fe19fe632b (diff)
downloadchef-daa0f2af387df26bf254d5d287c4a27934540b97.tar.gz
Merge pull request #1316 from opscode/adamed/guard-interpreter
CHEF-4553: Guard interpreter and powershell boolean awareness
-rw-r--r--CHANGELOG.md1
-rw-r--r--DOC_CHANGES.md108
-rw-r--r--RELEASE_NOTES.md26
-rw-r--r--lib/chef/guard_interpreter/default_guard_interpreter.rb42
-rw-r--r--lib/chef/guard_interpreter/resource_guard_interpreter.rb122
-rw-r--r--lib/chef/platform/provider_mapping.rb12
-rw-r--r--lib/chef/provider/powershell_script.rb25
-rw-r--r--lib/chef/resource.rb15
-rw-r--r--lib/chef/resource/conditional.rb27
-rw-r--r--lib/chef/resource/execute.rb2
-rw-r--r--lib/chef/resource/powershell_script.rb24
-rw-r--r--lib/chef/resource/script.rb25
-rw-r--r--lib/chef/resource/windows_script.rb5
-rw-r--r--spec/functional/resource/powershell_spec.rb263
-rw-r--r--spec/support/shared/functional/windows_script.rb2
-rw-r--r--spec/support/shared/unit/script_resource.rb38
-rw-r--r--spec/unit/guard_interpreter/resource_guard_interpreter_spec.rb56
-rw-r--r--spec/unit/resource/conditional_spec.rb25
-rw-r--r--spec/unit/resource/powershell_spec.rb87
-rw-r--r--spec/unit/resource_spec.rb24
20 files changed, 885 insertions, 44 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2eacaa9e40..657e1f6adf 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -65,6 +65,7 @@
* Increase bootstrap log_level when knife -V -V is set (CHEF-3610)
* Knife cookbook test should honor chefignore (CHEF-4203)
* Fix ImmutableMash and ImmutableArray to_hash and to_a methods (CHEF-5132)
+* guard_interpreter attribute: use powershell\_script, other script resources in guards (CHEF-4553)
## Last Release: 11.10.0 (02/06/2014)
diff --git a/DOC_CHANGES.md b/DOC_CHANGES.md
index dff930ac09..f29d6ab132 100644
--- a/DOC_CHANGES.md
+++ b/DOC_CHANGES.md
@@ -177,3 +177,111 @@ end
* installer_type - The type of package being installed. Can be auto-detected. Currently only :msi is supported.
* timeout - The time in seconds allowed for the package to successfully be installed. Defaults to 600 seconds.
* returns - Return codes that signal a successful installation. Defaults to 0.
+
+### New resource attribute: `guard_interpreter`
+All resources have a new attribute, `guard_interpreter`, which specifies a
+Chef script resource that should be used to evaluate a string command
+passed to a guard. Any attributes of the evaluating resource may be specified in
+the options that normally follow the guard's command string argument. For example:
+
+ # Tell Chef to use bash to interpret the guard string.
+ # Then we can use a guard command that is valid for bash
+ # but not for csh for instance
+ bash 'backupsettings' do
+ guard_interpreter :bash
+ code 'cp ~/appsettings.json ~/backup/appsettings.json'
+ not_if '[[ -e ./appsettings.json ]]', :cwd => '~/backup'
+ end
+
+The argument for `guard_interpreter` may be set to any of the following values:
+* The symbol name for a Chef Resource derived from the Chef `script` resource
+ such as `:bash`, `:powershell_script`, etc.
+* The symbol `:default` which means that a resource is not used to evaluate
+ the guard command argument, it is simply executed by the default shell as in
+ previous releases of Chef.
+
+By default, `guard_interpreter` is set to `:default` in this release.
+
+#### Attribute inheritance with `guard_interpreter`
+
+When `guard_interpreter` is not set to `:default`, the resource that evaluates the command will
+also inherit certain attribute values from the resource that contains the
+guard.
+
+Inherited attributes for all `script` resources are:
+
+* `:cwd`
+* `:environment`
+* `:group`
+* `:path`
+* `:user`
+* `:umask`
+
+For the `powershell_script` resource, the following attribute is inherited:
+* `:architecture`
+
+Inherited attributes may be overridden by specifying the same attribute as an
+argument to the guard itself.
+
+#### Guard inheritance example
+
+In the following example, the `:environment` hash only needs to be set once
+since the `bash` resource that execute the guard will inherit the same value:
+
+ script "javatooling" do
+ environment {"JAVA_HOME" => '/usr/lib/java/jdk1.7/home'}
+ code 'java-based-daemon-ctl.sh -start'
+ not_if 'java-based-daemon-ctl.sh -test-started' # No need to specify environment again
+ end
+
+### New `powershell_script` resource attribute: `convert_boolean_return`
+
+The `powershell_script` resource has a new attribute, `convert_boolean_return`
+that causes the script interpreter to return 0 if the last line of the command
+evaluted by PowerShell results in a boolean PowerShell data type that is true, or 1 if
+it results in a boolean PowerShell data type that is false. For example, the
+following two fragments will run successfully without error:
+
+ powershell_script 'false' do
+ code '$false'
+ end
+
+ powershell_script 'true' do
+ code '$true'
+ end
+
+But when `convert_boolean_return` is set to `true`, the "true" case above will
+still succeed, but the false case will raise an exception:
+
+ # Raises an exception
+ powershell_script 'false' do
+ convert_boolean_return true
+ code '$false'
+ end
+
+When used at recipe scope, the default value of `convert_boolean_return` is
+`false` in this release. However, if `guard_interpreter` is set to
+`:powershell_script`, the guard expression will be evaluted with a
+`powershell_script` resource that has the `convert_boolean_return` attribute
+set to `true`.
+
+#### Guard command example
+
+The behavior of `convert_boolean_return` is similar to the "$?"
+expression's value after use of the `test` command in Unix-flavored shells and
+its translation to an exit code for the shell. Since this attribute is set to
+`true` when `powershell_script` is used via the `guard_interpreter` to
+evaluate the guard expression, the behavior of `powershell_script` is very
+similar to guards executed with Unix shell interpreters as seen below:
+
+ bash 'make_safe_backup' do
+ code 'cp ~/data/nodes.json ~/data/nodes.bak'
+ not_if 'test -e ~/data/nodes.bak'
+ end
+
+ # convert_boolean_return is true by default in guards
+ powershell_script 'make_safe_backup' do
+ guard_interpreter :powershell_script
+ code 'cp ~/data/nodes.json ~/data/nodes.bak'
+ not_if 'test-path ~/data/nodes.bak'
+ end
diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md
index 355733af38..a4796c3e5a 100644
--- a/RELEASE_NOTES.md
+++ b/RELEASE_NOTES.md
@@ -133,6 +133,32 @@ If you're an advanced user of attribute precedence, you may find some attributes
The weekday attribute now accepts the weekday as a symbol, e.g. :monday or :thursday.
There is a new attribute named ```time``` that takes special cron time values as a symbol, such as :reboot or :monthly.
+#### `guard_interpreter` attribute
+
+All Chef resources now support the `guard_interpreter` attribute, which
+enables you to use a Chef `script` such as `bash`, `powershell_script`,
+`perl`, etc., to evaluate the string command passed to a
+guard (i.e. `not_if` or `only_if` attribute). This addresses the related ticket
+[CHEF-4553](https://tickets.opscode.com/browse/CHEF-4453) which is concerned
+with the usability of the `powershell_script` resource, but also benefits
+users of resources like `python`, `bash`, etc:
+
+ # See CHEF-4553 -- let powershell_script execute the guard
+ powershell_script 'make_logshare' do
+ guard_interpreter :powershell_script
+ code 'new-smbshare logshare $env:systemdrive\\logs'
+ not_if 'get-smbshare logshare'
+ end
+
+#### `convert_boolean_return` attribute for `powershell_script`
+
+When set to `true`, the `convert_boolean_return` attribute will allow any script executed by
+`powershell_script` that exits with a PowerShell boolean data type to convert
+PowerShell boolean `$true` to exit status 0 and `$false` to exit status 1.
+
+The new attribute defaults to `false` except when the `powershell_script` resource is executing script passed to a guard attribute
+via the `guard_interpreter` attribute in which case it is `true` by default.
+
#### knife bootstrap log_level
Running ```knife bootstrap -V -V``` will run the initial chef-client with a log level of debug.
diff --git a/lib/chef/guard_interpreter/default_guard_interpreter.rb b/lib/chef/guard_interpreter/default_guard_interpreter.rb
new file mode 100644
index 0000000000..df91c2b1ad
--- /dev/null
+++ b/lib/chef/guard_interpreter/default_guard_interpreter.rb
@@ -0,0 +1,42 @@
+#
+# Author:: Adam Edwards (<adamed@getchef.com>)
+# Copyright:: Copyright (c) 2014 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 GuardInterpreter
+ class DefaultGuardInterpreter
+ include Chef::Mixin::ShellOut
+
+ protected
+
+ def initialize(command, opts)
+ @command = command
+ @command_opts = opts
+ end
+
+ public
+
+ def evaluate
+ shell_out(@command, @command_opts).status.success?
+ rescue Chef::Exceptions::CommandTimeout
+ Chef::Log.warn "Command '#{@command}' timed out"
+ false
+ end
+ end
+ end
+end
+
diff --git a/lib/chef/guard_interpreter/resource_guard_interpreter.rb b/lib/chef/guard_interpreter/resource_guard_interpreter.rb
new file mode 100644
index 0000000000..229a8502c7
--- /dev/null
+++ b/lib/chef/guard_interpreter/resource_guard_interpreter.rb
@@ -0,0 +1,122 @@
+#
+# Author:: Adam Edwards (<adamed@getchef.com>)
+# Copyright:: Copyright (c) 2014 Opscode, 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.
+#
+
+require 'chef/guard_interpreter/default_guard_interpreter'
+
+class Chef
+ class GuardInterpreter
+ class ResourceGuardInterpreter < DefaultGuardInterpreter
+
+ def initialize(parent_resource, command, opts, &block)
+ super(command, opts)
+ @parent_resource = parent_resource
+ @resource = get_interpreter_resource(parent_resource)
+ end
+
+ def evaluate
+ # Add attributes inherited from the parent class
+ # to the resource
+ merge_inherited_attributes
+
+ # Script resources have a code attribute, which is
+ # what is used to execute the command, so include
+ # that with attributes specified by caller in opts
+ block_attributes = @command_opts.merge({:code => @command})
+
+ # Handles cases like powershell_script where default
+ # attributes are different when used in a guard vs. not. For
+ # powershell_script in particular, this will go away when
+ # the one attribue that causes this changes its default to be
+ # the same after some period to prepare for deprecation
+ if @resource.class.respond_to?(:get_default_attributes)
+ block_attributes = @resource.class.send(:get_default_attributes, @command_opts).merge(block_attributes)
+ end
+
+ resource_block = block_from_attributes(block_attributes)
+ evaluate_action(nil, &resource_block)
+ end
+
+ protected
+
+ def evaluate_action(action=nil, &block)
+ @resource.instance_eval(&block)
+
+ run_action = action || @resource.action
+
+ begin
+ @resource.run_action(run_action)
+ resource_updated = @resource.updated
+ rescue Mixlib::ShellOut::ShellCommandFailed
+ resource_updated = nil
+ end
+
+ resource_updated
+ end
+
+ def get_interpreter_resource(parent_resource)
+ if parent_resource.nil? || parent_resource.node.nil?
+ raise ArgumentError, "Node for guard resource parent must not be nil"
+ end
+
+ resource_class = Chef::Resource.resource_for_node(parent_resource.guard_interpreter, parent_resource.node)
+
+ if resource_class.nil?
+ raise ArgumentError, "Specified guard_interpreter resource #{parent_resource.guard_interpreter.to_s} unknown for this platform"
+ end
+
+ if ! resource_class.ancestors.include?(Chef::Resource::Script)
+ raise ArgumentError, "Specified guard interpreter class #{resource_class} must be a kind of Chef::Resource::Script resource"
+ end
+
+ empty_events = Chef::EventDispatch::Dispatcher.new
+ anonymous_run_context = Chef::RunContext.new(parent_resource.node, {}, empty_events)
+ interpreter_resource = resource_class.new('Guard resource', anonymous_run_context)
+
+ interpreter_resource
+ end
+
+ def block_from_attributes(attributes)
+ Proc.new do
+ attributes.keys.each do |attribute_name|
+ send(attribute_name, attributes[attribute_name]) if respond_to?(attribute_name)
+ end
+ end
+ end
+
+ def merge_inherited_attributes
+ inherited_attributes = []
+
+ if @parent_resource.class.respond_to?(:guard_inherited_attributes)
+ inherited_attributes = @parent_resource.class.send(:guard_inherited_attributes)
+ end
+
+ if inherited_attributes && !inherited_attributes.empty?
+ inherited_attributes.each do |attribute|
+ if @parent_resource.respond_to?(attribute) && @resource.respond_to?(attribute)
+ parent_value = @parent_resource.send(attribute)
+ child_value = @resource.send(attribute)
+ if parent_value || child_value
+ @resource.send(attribute, parent_value)
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/platform/provider_mapping.rb b/lib/chef/platform/provider_mapping.rb
index 2f934faa3e..a773da550e 100644
--- a/lib/chef/platform/provider_mapping.rb
+++ b/lib/chef/platform/provider_mapping.rb
@@ -241,7 +241,9 @@ class Chef
:service => Chef::Provider::Service::Windows,
:user => Chef::Provider::User::Windows,
:group => Chef::Provider::Group::Windows,
- :mount => Chef::Provider::Mount::Windows
+ :mount => Chef::Provider::Mount::Windows,
+ :batch => Chef::Provider::Batch,
+ :powershell_script => Chef::Provider::PowershellScript
}
},
:mingw32 => {
@@ -250,7 +252,9 @@ class Chef
:service => Chef::Provider::Service::Windows,
:user => Chef::Provider::User::Windows,
:group => Chef::Provider::Group::Windows,
- :mount => Chef::Provider::Mount::Windows
+ :mount => Chef::Provider::Mount::Windows,
+ :batch => Chef::Provider::Batch,
+ :powershell_script => Chef::Provider::PowershellScript
}
},
:windows => {
@@ -259,7 +263,9 @@ class Chef
:service => Chef::Provider::Service::Windows,
:user => Chef::Provider::User::Windows,
:group => Chef::Provider::Group::Windows,
- :mount => Chef::Provider::Mount::Windows
+ :mount => Chef::Provider::Mount::Windows,
+ :batch => Chef::Provider::Batch,
+ :powershell_script => Chef::Provider::PowershellScript
}
},
:solaris => {},
diff --git a/lib/chef/provider/powershell_script.rb b/lib/chef/provider/powershell_script.rb
index c459cdf678..967b2d822b 100644
--- a/lib/chef/provider/powershell_script.rb
+++ b/lib/chef/provider/powershell_script.rb
@@ -23,9 +23,9 @@ class Chef
class PowershellScript < Chef::Provider::WindowsScript
protected
-
- EXIT_STATUS_NORMALIZATION_SCRIPT = "\nif ($? -eq $true) {exit 0} elseif ( $LASTEXITCODE -ne 0) {exit $LASTEXITCODE} else { exit 1 }"
- EXIT_STATUS_RESET_SCRIPT = "$LASTEXITCODE=0\n"
+ EXIT_STATUS_EXCEPTION_HANDLER = "\ntrap [Exception] {write-error -exception ($_.Exception.Message);exit 1}".freeze
+ EXIT_STATUS_NORMALIZATION_SCRIPT = "\nif ($? -ne $true) { if ( $LASTEXITCODE -ne 0) {exit $LASTEXITCODE} else { exit 1 }}".freeze
+ EXIT_STATUS_RESET_SCRIPT = "\n$LASTEXITCODE=0".freeze
# Process exit codes are strange with PowerShell. Unless you
# explicitly call exit in Powershell, the powershell.exe
@@ -36,15 +36,28 @@ class Chef
# last process run in the script if it is the last command
# executed, otherwise 0 or 1 based on whether $? is set to true
# (success, where we return 0) or false (where we return 1).
- def NormalizeScriptExitStatus( code )
- @code = (! code.nil?) ? ( EXIT_STATUS_RESET_SCRIPT + code + EXIT_STATUS_NORMALIZATION_SCRIPT ) : nil
+ def normalize_script_exit_status( code )
+ target_code = ( EXIT_STATUS_EXCEPTION_HANDLER +
+ EXIT_STATUS_RESET_SCRIPT +
+ "\n" +
+ code.to_s +
+ EXIT_STATUS_NORMALIZATION_SCRIPT )
+ convert_boolean_return = @new_resource.convert_boolean_return
+ @code = <<EOH
+new-variable -name interpolatedexitcode -visibility private -value $#{convert_boolean_return}
+new-variable -name chefscriptresult -visibility private
+$chefscriptresult = {
+#{target_code}
+}.invokereturnasis()
+if ($interpolatedexitcode -and $chefscriptresult.gettype().name -eq 'boolean') { exit [int32](!$chefscriptresult) } else { exit 0 }
+EOH
end
public
def initialize (new_resource, run_context)
super(new_resource, run_context, '.ps1')
- NormalizeScriptExitStatus(new_resource.code)
+ normalize_script_exit_status(new_resource.code)
end
def flags
diff --git a/lib/chef/resource.rb b/lib/chef/resource.rb
index bcee8f1235..7d96b26b4b 100644
--- a/lib/chef/resource.rb
+++ b/lib/chef/resource.rb
@@ -23,6 +23,7 @@ require 'chef/dsl/data_query'
require 'chef/dsl/registry_helper'
require 'chef/dsl/reboot_pending'
require 'chef/mixin/convert_to_class_name'
+require 'chef//guard_interpreter/resource_guard_interpreter'
require 'chef/resource/conditional'
require 'chef/resource/conditional_action_not_nothing'
require 'chef/resource_collection'
@@ -249,6 +250,7 @@ F
@not_if = []
@only_if = []
@source_line = nil
+ @guard_interpreter = :default
@elapsed_time = 0
@node = run_context ? deprecated_ivar(run_context.node, :node, :warn) : nil
@@ -401,6 +403,14 @@ F
ignore_failure(arg)
end
+ def guard_interpreter(arg=nil)
+ set_or_return(
+ :guard_interpreter,
+ arg,
+ :kind_of => Symbol
+ )
+ end
+
# Sets up a notification from this resource to the resource specified by +resource_spec+.
def notifies(action, resource_spec, timing=:delayed)
# when using old-style resources(:template => "/foo.txt") style, you
@@ -552,7 +562,7 @@ F
# * evaluates to false if the block is false, or if the command returns a non-zero exit code.
def only_if(command=nil, opts={}, &block)
if command || block_given?
- @only_if << Conditional.only_if(command, opts, &block)
+ @only_if << Conditional.only_if(self, command, opts, &block)
end
@only_if
end
@@ -573,7 +583,7 @@ F
# * evaluates to false if the block is true, or if the command returns a 0 exit status.
def not_if(command=nil, opts={}, &block)
if command || block_given?
- @not_if << Conditional.not_if(command, opts, &block)
+ @not_if << Conditional.not_if(self, command, opts, &block)
end
@not_if
end
@@ -819,6 +829,5 @@ F
end
end
end
-
end
end
diff --git a/lib/chef/resource/conditional.rb b/lib/chef/resource/conditional.rb
index 60f65e14e2..e6623be5dd 100644
--- a/lib/chef/resource/conditional.rb
+++ b/lib/chef/resource/conditional.rb
@@ -17,6 +17,7 @@
#
require 'chef/mixin/shell_out'
+require 'chef/guard_interpreter/resource_guard_interpreter'
class Chef
class Resource
@@ -29,12 +30,12 @@ class Chef
private :new
end
- def self.not_if(command=nil, command_opts={}, &block)
- new(:not_if, command, command_opts, &block)
+ def self.not_if(parent_resource, command=nil, command_opts={}, &block)
+ new(:not_if, parent_resource, command, command_opts, &block)
end
- def self.only_if(command=nil, command_opts={}, &block)
- new(:only_if, command, command_opts, &block)
+ def self.only_if(parent_resource, command=nil, command_opts={}, &block)
+ new(:only_if, parent_resource, command, command_opts, &block)
end
attr_reader :positivity
@@ -42,14 +43,16 @@ class Chef
attr_reader :command_opts
attr_reader :block
- def initialize(positivity, command=nil, command_opts={}, &block)
+ def initialize(positivity, parent_resource, command=nil, command_opts={}, &block)
@positivity = positivity
case command
when String
+ @guard_interpreter = new_guard_interpreter(parent_resource, command, command_opts, &block)
@command, @command_opts = command, command_opts
@block = nil
when nil
raise ArgumentError, "only_if/not_if requires either a command or a block" unless block_given?
+ @guard_interpreter = nil
@command, @command_opts = nil, nil
@block = block
else
@@ -69,11 +72,11 @@ class Chef
end
def evaluate
- @command ? evaluate_command : evaluate_block
+ @guard_interpreter ? evaluate_command : evaluate_block
end
def evaluate_command
- shell_out(@command, @command_opts).status.success?
+ @guard_interpreter.evaluate
rescue Chef::Exceptions::CommandTimeout
Chef::Log.warn "Command '#{@command}' timed out"
false
@@ -100,6 +103,16 @@ class Chef
end
end
+ private
+
+ def new_guard_interpreter(parent_resource, command, opts)
+ if parent_resource.guard_interpreter == :default
+ guard_interpreter = Chef::GuardInterpreter::DefaultGuardInterpreter.new(command, opts)
+ else
+ guard_interpreter = Chef::GuardInterpreter::ResourceGuardInterpreter.new(parent_resource, command, opts)
+ end
+ end
+
end
end
end
diff --git a/lib/chef/resource/execute.rb b/lib/chef/resource/execute.rb
index 6c07bf9352..7c4fa48c0a 100644
--- a/lib/chef/resource/execute.rb
+++ b/lib/chef/resource/execute.rb
@@ -125,8 +125,6 @@ class Chef
)
end
-
-
end
end
end
diff --git a/lib/chef/resource/powershell_script.rb b/lib/chef/resource/powershell_script.rb
index cbd81b1259..1b47e7411a 100644
--- a/lib/chef/resource/powershell_script.rb
+++ b/lib/chef/resource/powershell_script.rb
@@ -15,17 +15,39 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
-
require 'chef/resource/windows_script'
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
+ end
+
+ def convert_boolean_return(arg=nil)
+ set_or_return(
+ :convert_boolean_return,
+ arg,
+ :kind_of => [ FalseClass, TrueClass ]
+ )
end
+ protected
+
+ # Allow callers evaluating guards to request default
+ # attribute values. This is needed to allow
+ # convert_boolean_return to be true in guard context by default,
+ # and false by default otherwise. When this mode becomes the
+ # default for this resource, this method can be removed since
+ # guard context and recipe resource context will have the
+ # same behavior.
+ def self.get_default_attributes(opts)
+ {:convert_boolean_return => true}
+ end
end
end
end
diff --git a/lib/chef/resource/script.rb b/lib/chef/resource/script.rb
index 8cc9c6f0c5..6f66fb9094 100644
--- a/lib/chef/resource/script.rb
+++ b/lib/chef/resource/script.rb
@@ -58,6 +58,31 @@ class Chef
)
end
+ def self.set_guard_inherited_attributes(*inherited_attributes)
+ @class_inherited_attributes = inherited_attributes
+ end
+
+ def self.guard_inherited_attributes(*inherited_attributes)
+ # Similar to patterns elsewhere, return attributes from this
+ # class and superclasses as a form of inheritance
+ ancestor_attributes = []
+
+ if superclass.respond_to?(:guard_inherited_attributes)
+ ancestor_attributes = superclass.guard_inherited_attributes
+ end
+
+ ancestor_attributes.concat(@class_inherited_attributes ? @class_inherited_attributes : []).uniq
+ end
+
+ set_guard_inherited_attributes(
+ :cwd,
+ :environment,
+ :group,
+ :path,
+ :user,
+ :umask
+ )
+
end
end
end
diff --git a/lib/chef/resource/windows_script.rb b/lib/chef/resource/windows_script.rb
index 2b563f5bec..108891e9ba 100644
--- a/lib/chef/resource/windows_script.rb
+++ b/lib/chef/resource/windows_script.rb
@@ -52,11 +52,6 @@ class Chef
"cannot execute script with requested architecture '#{desired_architecture.to_s}' on a system with architecture '#{node_windows_architecture(node)}'"
end
end
-
- def node
- run_context && run_context.node
- end
-
end
end
end
diff --git a/spec/functional/resource/powershell_spec.rb b/spec/functional/resource/powershell_spec.rb
index 6bd3b3c1e5..5001e870a9 100644
--- a/spec/functional/resource/powershell_spec.rb
+++ b/spec/functional/resource/powershell_spec.rb
@@ -84,7 +84,7 @@ describe Chef::Resource::WindowsScript::PowershellScript, :windows_only do
# last line executed -- in this case, we return the status of the
# second to last line. This happens because Powershell gives no
# way for us to determine whether the last operation was a cmdlet
- # or Windows process. Because the latter gives more specified
+ # or Windows process. Because the latter gives more specific
# errors than 0 or 1, we return that instead, which is acceptable
# since callers can test for nonzero rather than testing for 1.
it "returns 1 if the last command was a cmdlet that failed and was preceded by an unsuccessfully executed non-cmdlet Windows binary" do
@@ -111,6 +111,32 @@ describe Chef::Resource::WindowsScript::PowershellScript, :windows_only do
resource.run_action(:run)
end
+ it "returns 0 for $false as the last line of the script when convert_boolean_return is false" do
+ resource.code "$false"
+ resource.returns(0)
+ resource.run_action(:run)
+ end
+
+ it "returns 0 for $true as the last line of the script when convert_boolean_return is false" do
+ resource.code "$true"
+ resource.returns(0)
+ resource.run_action(:run)
+ end
+
+ it "returns 1 for $false as the last line of the script when convert_boolean_return is true" do
+ resource.convert_boolean_return true
+ resource.code "$false"
+ resource.returns(1)
+ resource.run_action(:run)
+ end
+
+ it "returns 0 for $true as the last line of the script when convert_boolean_return is true" do
+ resource.convert_boolean_return true
+ resource.code "$true"
+ resource.returns(0)
+ resource.run_action(:run)
+ end
+
it "executes a script with a 64-bit process on a 64-bit OS, otherwise a 32-bit process" do
resource.code(processor_architecture_script_content + " | out-file -encoding ASCII #{script_output_path}")
resource.returns(0)
@@ -177,6 +203,241 @@ describe Chef::Resource::WindowsScript::PowershellScript, :windows_only do
end
end
+ describe "when executing guards" do
+
+ before(:each) do
+ resource.not_if.clear
+ resource.only_if.clear
+ resource.guard_interpreter :powershell_script
+ end
+
+ it "evaluates a succeeding not_if block using cmd.exe as false by default" do
+ resource.guard_interpreter :default
+ resource.not_if "exit /b 0"
+ resource.should_skip?(:run).should be_true
+ end
+
+ it "evaluates a failing not_if block using cmd.exe as true by default" do
+ resource.guard_interpreter :default
+ 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.guard_interpreter :default
+ 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.guard_interpreter :default
+ resource.only_if "exit /b 2"
+ resource.should_skip?(:run).should be_true
+ end
+
+ it "evaluates a powershell $false for a not_if block as true" do
+ resource.not_if "$false"
+ resource.should_skip?(:run).should be_false
+ end
+
+ it "evaluates a powershell $true for a not_if block as false" do
+ resource.not_if "$true"
+ resource.should_skip?(:run).should be_true
+ end
+
+ it "evaluates a powershell $false for an only_if block as false" do
+ resource.only_if "$false"
+ resource.should_skip?(:run).should be_true
+ end
+
+ it "evaluates a powershell $true for a only_if block as true" do
+ resource.only_if "$true"
+ resource.should_skip?(:run).should be_false
+ end
+
+ it "evaluates a not_if block using powershell.exe" do
+ resource.not_if "exit([int32](![System.Environment]::CommandLine.Contains('powershell.exe')))"
+ resource.should_skip?(:run).should be_true
+ end
+
+ it "evaluates an only_if block using powershell.exe" do
+ resource.only_if "exit([int32](![System.Environment]::CommandLine.Contains('powershell.exe')))"
+ resource.should_skip?(:run).should be_false
+ end
+
+ it "evaluates a not_if block as false" do
+ resource.not_if { false }
+ resource.should_skip?(:run).should be_false
+ end
+
+ it "evaluates a not_if block as true" do
+ resource.not_if { true }
+ resource.should_skip?(:run).should be_true
+ end
+
+ it "evaluates an only_if block as false" do
+ resource.only_if { false }
+ resource.should_skip?(:run).should be_true
+ end
+
+ it "evaluates an only_if block as true" do
+ resource.only_if { true }
+ resource.should_skip?(:run).should be_false
+ end
+
+ it "evaluates a non-zero powershell exit status for not_if as true" do
+ resource.not_if "exit 37"
+ resource.should_skip?(:run).should be_false
+ end
+
+ it "evaluates a zero powershell exit status for not_if as false" do
+ resource.not_if "exit 0"
+ resource.should_skip?(:run).should be_true
+ end
+
+ it "evaluates a failed executable exit status for not_if as false" do
+ resource.not_if windows_process_exit_code_not_found_content
+ resource.should_skip?(:run).should be_false
+ end
+
+ it "evaluates a successful executable exit status for not_if as true" do
+ resource.not_if windows_process_exit_code_success_content
+ resource.should_skip?(:run).should be_true
+ end
+
+ it "evaluates a failed executable exit status for only_if as false" do
+ resource.only_if windows_process_exit_code_not_found_content
+ resource.should_skip?(:run).should be_true
+ end
+
+ it "evaluates a successful executable exit status for only_if as true" do
+ resource.only_if windows_process_exit_code_success_content
+ resource.should_skip?(:run).should be_false
+ end
+
+ it "evaluates a failed cmdlet exit status for not_if as true" do
+ resource.not_if "throw 'up'"
+ resource.should_skip?(:run).should be_false
+ end
+
+ it "evaluates a successful cmdlet exit status for not_if as true" do
+ resource.not_if "cd ."
+ resource.should_skip?(:run).should be_true
+ end
+
+ it "evaluates a failed cmdlet exit status for only_if as false" do
+ resource.only_if "throw 'up'"
+ resource.should_skip?(:run).should be_true
+ end
+
+ it "evaluates a successful cmdlet exit status for only_if as true" do
+ resource.only_if "cd ."
+ resource.should_skip?(:run).should be_false
+ end
+
+ it "evaluates a not_if block using the cwd guard parameter" do
+ custom_cwd = "#{ENV['SystemRoot']}\\system32\\drivers\\etc"
+ resource.not_if "exit ! [int32]($pwd.path -eq '#{custom_cwd}')", :cwd => custom_cwd
+ resource.should_skip?(:run).should be_true
+ end
+
+ it "evaluates an only_if block using the cwd guard parameter" do
+ custom_cwd = "#{ENV['SystemRoot']}\\system32\\drivers\\etc"
+ resource.only_if "exit ! [int32]($pwd.path -eq '#{custom_cwd}')", :cwd => custom_cwd
+ resource.should_skip?(:run).should be_false
+ end
+
+ it "inherits cwd from the parent resource for only_if" do
+ custom_cwd = "#{ENV['SystemRoot']}\\system32\\drivers\\etc"
+ resource.cwd custom_cwd
+ resource.only_if "exit ! [int32]($pwd.path -eq '#{custom_cwd}')"
+ resource.should_skip?(:run).should be_false
+ end
+
+ it "inherits cwd from the parent resource for not_if" do
+ custom_cwd = "#{ENV['SystemRoot']}\\system32\\drivers\\etc"
+ resource.cwd custom_cwd
+ resource.not_if "exit ! [int32]($pwd.path -eq '#{custom_cwd}')"
+ resource.should_skip?(:run).should be_true
+ end
+
+ it "evaluates a 64-bit resource with a 64-bit guard and interprets boolean false as zero status code", :windows64_only do
+ resource.architecture :x86_64
+ resource.only_if "exit [int32]($env:PROCESSOR_ARCHITECTURE -ne 'AMD64')"
+ resource.should_skip?(:run).should be_false
+ end
+
+ it "evaluates a 64-bit resource with a 64-bit guard and interprets boolean true as nonzero status code", :windows64_only do
+ resource.architecture :x86_64
+ resource.only_if "exit [int32]($env:PROCESSOR_ARCHITECTURE -eq 'AMD64')"
+ resource.should_skip?(:run).should be_true
+ end
+
+ it "evaluates a 32-bit resource with a 32-bit guard and interprets boolean false as zero status code" do
+ resource.architecture :i386
+ resource.only_if "exit [int32]($env:PROCESSOR_ARCHITECTURE -ne 'X86')"
+ resource.should_skip?(:run).should be_false
+ end
+
+ it "evaluates a 32-bit resource with a 32-bit guard and interprets boolean true as nonzero status code" do
+ resource.architecture :i386
+ resource.only_if "exit [int32]($env:PROCESSOR_ARCHITECTURE -eq 'X86')"
+ resource.should_skip?(:run).should be_true
+ end
+
+ it "evaluates a simple boolean false as nonzero status code when convert_boolean_return is true for only_if" do
+ resource.convert_boolean_return true
+ resource.only_if "$false"
+ resource.should_skip?(:run).should be_true
+ end
+
+ it "evaluates a simple boolean false as nonzero status code when convert_boolean_return is true for not_if" do
+ resource.convert_boolean_return true
+ resource.not_if "$false"
+ resource.should_skip?(:run).should be_false
+ end
+
+ it "evaluates a simple boolean true as 0 status code when convert_boolean_return is true for only_if" do
+ resource.convert_boolean_return true
+ resource.only_if "$true"
+ resource.should_skip?(:run).should be_false
+ end
+
+ it "evaluates a simple boolean true as 0 status code when convert_boolean_return is true for not_if" do
+ resource.convert_boolean_return true
+ resource.not_if "$true"
+ resource.should_skip?(:run).should be_true
+ end
+
+ it "evaluates a 32-bit resource with a 32-bit guard and interprets boolean false as zero status code using convert_boolean_return for only_if" do
+ resource.convert_boolean_return true
+ resource.architecture :i386
+ resource.only_if "$env:PROCESSOR_ARCHITECTURE -eq 'X86'"
+ resource.should_skip?(:run).should be_false
+ end
+
+ it "evaluates a 32-bit resource with a 32-bit guard and interprets boolean false as zero status code using convert_boolean_return for not_if" do
+ resource.convert_boolean_return true
+ resource.architecture :i386
+ resource.not_if "$env:PROCESSOR_ARCHITECTURE -ne 'X86'"
+ resource.should_skip?(:run).should be_false
+ end
+
+ it "evaluates a 32-bit resource with a 32-bit guard and interprets boolean true as nonzero status code using convert_boolean_return for only_if" do
+ resource.convert_boolean_return true
+ resource.architecture :i386
+ resource.only_if "$env:PROCESSOR_ARCHITECTURE -ne 'X86'"
+ resource.should_skip?(:run).should be_true
+ end
+
+ it "evaluates a 32-bit resource with a 32-bit guard and interprets boolean true as nonzero status code using convert_boolean_return for not_if" do
+ resource.convert_boolean_return true
+ resource.architecture :i386
+ resource.not_if "$env:PROCESSOR_ARCHITECTURE -eq 'X86'"
+ resource.should_skip?(:run).should be_true
+ end
+ end
+
def get_script_output
script_output = File.read(script_output_path)
end
diff --git a/spec/support/shared/functional/windows_script.rb b/spec/support/shared/functional/windows_script.rb
index db2882e1a1..fc06fb55d0 100644
--- a/spec/support/shared/functional/windows_script.rb
+++ b/spec/support/shared/functional/windows_script.rb
@@ -38,7 +38,7 @@ shared_context Chef::Resource::WindowsScript do
end
before(:each) do
-k File.delete(script_output_path) if File.exists?(script_output_path)
+ File.delete(script_output_path) if File.exists?(script_output_path)
end
after(:each) do
diff --git a/spec/support/shared/unit/script_resource.rb b/spec/support/shared/unit/script_resource.rb
index 5f37506df6..1137958420 100644
--- a/spec/support/shared/unit/script_resource.rb
+++ b/spec/support/shared/unit/script_resource.rb
@@ -48,5 +48,43 @@ shared_examples_for "a script resource" do
@resource.flags.should eql("-f")
end
+ describe "when executing guards" do
+ let(:resource) { @resource }
+
+ before(:each) do
+ node = Chef::Node.new
+
+ node.automatic[:platform] = "debian"
+ node.automatic[:platform_version] = "6.0"
+
+ events = Chef::EventDispatch::Dispatcher.new
+ run_context = Chef::RunContext.new(node, {}, events)
+ resource.run_context = run_context
+ resource.code 'echo hi'
+ end
+
+ it "inherits exactly the :cwd, :environment, :group, :path, :user, and :umask attributes from a parent resource class" do
+ inherited_difference = Chef::Resource::Script.guard_inherited_attributes -
+ [:cwd, :environment, :group, :path, :user, :umask ]
+
+ inherited_difference.should == []
+ end
+
+ 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)
+ resource.only_if 'echo hi'
+ resource.should_skip?(:run).should == nil
+ end
+
+ it "when a valid guard_interpreter resource is specified, a block should be used to evaluate the guard" do
+ Chef::GuardInterpreter::DefaultGuardInterpreter.any_instance.should_not_receive(:evaluate)
+ Chef::GuardInterpreter::ResourceGuardInterpreter.any_instance.should_receive(:evaluate_action).and_return(true)
+ resource.guard_interpreter :script
+ resource.only_if 'echo hi'
+ resource.should_skip?(:run).should == nil
+ end
+ end
end
diff --git a/spec/unit/guard_interpreter/resource_guard_interpreter_spec.rb b/spec/unit/guard_interpreter/resource_guard_interpreter_spec.rb
new file mode 100644
index 0000000000..a016cbfeb8
--- /dev/null
+++ b/spec/unit/guard_interpreter/resource_guard_interpreter_spec.rb
@@ -0,0 +1,56 @@
+#
+# Author:: Adam Edwards (<adamed@getchef.com>)
+# Copyright:: Copyright (c) 2014 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.
+#
+
+require 'spec_helper'
+
+describe Chef::GuardInterpreter::ResourceGuardInterpreter do
+ before(:each) do
+ node = Chef::Node.new
+
+ node.default["kernel"] = Hash.new
+ node.default["kernel"][:machine] = :x86_64.to_s
+
+ run_context = Chef::RunContext.new(node, nil, nil)
+
+ @resource = Chef::Resource.new("powershell_unit_test", run_context)
+ @resource.stub(:run_action)
+ @resource.stub(:updated).and_return(true)
+ end
+
+ describe "when evaluating a guard resource" do
+ let(:resource) { @resource }
+
+ it "should allow guard interpreter to be set to Chef::Resource::Script" do
+ resource.guard_interpreter(:script)
+ allow_any_instance_of(Chef::GuardInterpreter::ResourceGuardInterpreter).to receive(:evaluate_action).and_return(false)
+ resource.only_if("echo hi")
+ end
+
+ it "should allow guard interpreter to be set to Chef::Resource::PowershellScript derived indirectly from Chef::Resource::Script" do
+ resource.guard_interpreter(:powershell_script)
+ allow_any_instance_of(Chef::GuardInterpreter::ResourceGuardInterpreter).to receive(:evaluate_action).and_return(false)
+ resource.only_if("echo hi")
+ end
+
+ it "should raise an exception if guard_interpreter is set to a resource not derived from Chef::Resource::Script" do
+ resource.guard_interpreter(:file)
+ expect { resource.only_if("echo hi") }.to raise_error ArgumentError
+ end
+ end
+end
+
diff --git a/spec/unit/resource/conditional_spec.rb b/spec/unit/resource/conditional_spec.rb
index 1be7bcea71..4df185bcd6 100644
--- a/spec/unit/resource/conditional_spec.rb
+++ b/spec/unit/resource/conditional_spec.rb
@@ -24,12 +24,13 @@ describe Chef::Resource::Conditional do
Mixlib::ShellOut.any_instance.stub(:run_command).and_return(nil)
@status = OpenStruct.new(:success? => true)
Mixlib::ShellOut.any_instance.stub(:status).and_return(@status)
+ @parent_resource = Chef::Resource.new(nil, Chef::Node.new)
end
describe "when created as an `only_if`" do
describe "after running a successful command" do
before do
- @conditional = Chef::Resource::Conditional.only_if("true")
+ @conditional = Chef::Resource::Conditional.only_if(@parent_resource, "true")
end
it "indicates that resource convergence should continue" do
@@ -40,7 +41,7 @@ describe Chef::Resource::Conditional do
describe "after running a negative/false command" do
before do
@status.send("success?=", false)
- @conditional = Chef::Resource::Conditional.only_if("false")
+ @conditional = Chef::Resource::Conditional.only_if(@parent_resource, "false")
end
it "indicates that resource convergence should not continue" do
@@ -50,8 +51,8 @@ describe Chef::Resource::Conditional do
describe 'after running a command which timed out' do
before do
- @conditional = Chef::Resource::Conditional.only_if("false")
- @conditional.stub(:shell_out).and_raise(Chef::Exceptions::CommandTimeout)
+ @conditional = Chef::Resource::Conditional.only_if(@parent_resource, "false")
+ Chef::GuardInterpreter::DefaultGuardInterpreter.any_instance.stub(:shell_out).and_raise(Chef::Exceptions::CommandTimeout)
end
it 'indicates that resource convergence should not continue' do
@@ -66,7 +67,7 @@ describe Chef::Resource::Conditional do
describe "after running a block that returns a truthy value" do
before do
- @conditional = Chef::Resource::Conditional.only_if { Object.new }
+ @conditional = Chef::Resource::Conditional.only_if(@parent_resource) { Object.new }
end
it "indicates that resource convergence should continue" do
@@ -76,7 +77,7 @@ describe Chef::Resource::Conditional do
describe "after running a block that returns a falsey value" do
before do
- @conditional = Chef::Resource::Conditional.only_if { nil }
+ @conditional = Chef::Resource::Conditional.only_if(@parent_resource) { nil }
end
it "indicates that resource convergence should not continue" do
@@ -88,7 +89,7 @@ describe Chef::Resource::Conditional do
describe "when created as a `not_if`" do
describe "after running a successful/true command" do
before do
- @conditional = Chef::Resource::Conditional.not_if("true")
+ @conditional = Chef::Resource::Conditional.not_if(@parent_resource, "true")
end
it "indicates that resource convergence should not continue" do
@@ -99,7 +100,7 @@ describe Chef::Resource::Conditional do
describe "after running a failed/false command" do
before do
@status.send("success?=", false)
- @conditional = Chef::Resource::Conditional.not_if("false")
+ @conditional = Chef::Resource::Conditional.not_if(@parent_resource, "false")
end
it "indicates that resource convergence should continue" do
@@ -109,8 +110,8 @@ describe Chef::Resource::Conditional do
describe 'after running a command which timed out' do
before do
- @conditional = Chef::Resource::Conditional.not_if("false")
- @conditional.stub(:shell_out).and_raise(Chef::Exceptions::CommandTimeout)
+ @conditional = Chef::Resource::Conditional.not_if(@parent_resource, "false")
+ Chef::GuardInterpreter::DefaultGuardInterpreter.any_instance.stub(:shell_out).and_raise(Chef::Exceptions::CommandTimeout)
end
it 'indicates that resource convergence should continue' do
@@ -125,7 +126,7 @@ describe Chef::Resource::Conditional do
describe "after running a block that returns a truthy value" do
before do
- @conditional = Chef::Resource::Conditional.not_if { Object.new }
+ @conditional = Chef::Resource::Conditional.not_if(@parent_resource) { Object.new }
end
it "indicates that resource convergence should not continue" do
@@ -135,7 +136,7 @@ describe Chef::Resource::Conditional do
describe "after running a block that returns a falsey value" do
before do
- @conditional = Chef::Resource::Conditional.not_if { nil }
+ @conditional = Chef::Resource::Conditional.not_if(@parent_resource) { nil }
end
it "indicates that resource convergence should continue" do
diff --git a/spec/unit/resource/powershell_spec.rb b/spec/unit/resource/powershell_spec.rb
index a35e37c696..da20c4f0bf 100644
--- a/spec/unit/resource/powershell_spec.rb
+++ b/spec/unit/resource/powershell_spec.rb
@@ -36,7 +36,91 @@ describe Chef::Resource::PowershellScript do
@resource.should be_a_kind_of(Chef::Resource::PowershellScript)
end
- context "windowsscript" do
+ it "should set convert_boolean_return to false by default" do
+ @resource.convert_boolean_return.should == false
+ end
+
+ it "should return the value for convert_boolean_return that was set" do
+ @resource.convert_boolean_return true
+ @resource.convert_boolean_return.should == true
+ @resource.convert_boolean_return false
+ @resource.convert_boolean_return.should == false
+ end
+
+ context "when using guards" do
+ let(:resource) { @resource }
+ before(:each) do
+ resource.stub(:run_action)
+ resource.stub(:updated).and_return(true)
+ end
+
+ it "inherits exactly the :cwd, :environment, :group, :path, :user, :umask, and :architecture attributes from a parent resource class" do
+ inherited_difference = Chef::Resource::PowershellScript.guard_inherited_attributes -
+ [:cwd, :environment, :group, :path, :user, :umask, :architecture ]
+
+ inherited_difference.should == []
+ end
+
+ it "should allow guard interpreter to be set to Chef::Resource::Script" do
+ resource.guard_interpreter(:script)
+ allow_any_instance_of(Chef::GuardInterpreter::ResourceGuardInterpreter).to receive(:evaluate_action).and_return(false)
+ resource.only_if("echo hi")
+ end
+
+ it "should allow guard interpreter to be set to Chef::Resource::Bash derived from Chef::Resource::Script" do
+ resource.guard_interpreter(:bash)
+ allow_any_instance_of(Chef::GuardInterpreter::ResourceGuardInterpreter).to receive(:evaluate_action).and_return(false)
+ resource.only_if("echo hi")
+ end
+
+ it "should allow guard interpreter to be set to Chef::Resource::PowershellScript derived indirectly from Chef::Resource::Script" do
+ resource.guard_interpreter(:powershell_script)
+ allow_any_instance_of(Chef::GuardInterpreter::ResourceGuardInterpreter).to receive(:evaluate_action).and_return(false)
+ resource.only_if("echo hi")
+ end
+
+ it "should enable convert_boolean_return by default for guards in the context of powershell_script when no guard params are specified" do
+ allow_any_instance_of(Chef::GuardInterpreter::ResourceGuardInterpreter).to receive(:evaluate_action).and_return(true)
+ allow_any_instance_of(Chef::GuardInterpreter::ResourceGuardInterpreter).to receive(:block_from_attributes).with(
+ {:convert_boolean_return => true, :code => "$true"}).and_return(Proc.new {})
+ resource.only_if("$true")
+ end
+
+ it "should enable convert_boolean_return by default for guards in non-Chef::Resource::Script derived resources when no guard params are specified" do
+ node = Chef::Node.new
+ run_context = Chef::RunContext.new(node, nil, nil)
+ file_resource = Chef::Resource::File.new('idontexist', run_context)
+ file_resource.guard_interpreter :powershell_script
+
+ allow_any_instance_of(Chef::GuardInterpreter::ResourceGuardInterpreter).to receive(:block_from_attributes).with(
+ {:convert_boolean_return => true, :code => "$true"}).and_return(Proc.new {})
+ resource.only_if("$true")
+ end
+
+ it "should enable convert_boolean_return by default for guards in the context of powershell_script when guard params are specified" do
+ guard_parameters = {:cwd => '/etc/chef', :architecture => :x86_64}
+ allow_any_instance_of(Chef::GuardInterpreter::ResourceGuardInterpreter).to receive(:block_from_attributes).with(
+ {:convert_boolean_return => true, :code => "$true"}.merge(guard_parameters)).and_return(Proc.new {})
+ resource.only_if("$true", guard_parameters)
+ end
+
+ it "should pass convert_boolean_return as true if it was specified as true in a guard parameter" do
+ guard_parameters = {:cwd => '/etc/chef', :convert_boolean_return => true, :architecture => :x86_64}
+ allow_any_instance_of(Chef::GuardInterpreter::ResourceGuardInterpreter).to receive(:block_from_attributes).with(
+ {:convert_boolean_return => true, :code => "$true"}.merge(guard_parameters)).and_return(Proc.new {})
+ resource.only_if("$true", guard_parameters)
+ end
+
+ it "should pass convert_boolean_return as false if it was specified as true in a guard parameter" do
+ other_guard_parameters = {:cwd => '/etc/chef', :architecture => :x86_64}
+ parameters_with_boolean_disabled = other_guard_parameters.merge({:convert_boolean_return => false, :code => "$true"})
+ allow_any_instance_of(Chef::GuardInterpreter::ResourceGuardInterpreter).to receive(:block_from_attributes).with(
+ parameters_with_boolean_disabled).and_return(Proc.new {})
+ resource.only_if("$true", parameters_with_boolean_disabled)
+ end
+ end
+
+ context "as a script running in Windows-based scripting language" do
let(:resource_instance) { @resource }
let(:resource_instance_name ) { @resource.command }
let(:resource_name) { :powershell_script }
@@ -44,5 +128,4 @@ describe Chef::Resource::PowershellScript do
it_should_behave_like "a Windows script resource"
end
-
end
diff --git a/spec/unit/resource_spec.rb b/spec/unit/resource_spec.rb
index e9a60c9861..60f3bdb8ea 100644
--- a/spec/unit/resource_spec.rb
+++ b/spec/unit/resource_spec.rb
@@ -344,7 +344,7 @@ describe Chef::Resource do
expected_keys = [ :allowed_actions, :params, :provider, :updated,
:updated_by_last_action, :before, :supports,
:noop, :ignore_failure, :name, :source_line,
- :action, :retries, :retry_delay, :elapsed_time]
+ :action, :retries, :retry_delay, :elapsed_time, :guard_interpreter]
(hash.keys - expected_keys).should == []
(expected_keys - hash.keys).should == []
hash[:name].should eql("funk")
@@ -526,6 +526,28 @@ describe Chef::Resource do
snitch_var2.should be_false
end
+ describe "guard_interpreter attribute" do
+ let(:resource) { @resource }
+
+ it "should be set to :default by default" do
+ resource.guard_interpreter.should == :default
+ end
+
+ it "if set to :default should return :default when read" do
+ resource.guard_interpreter(:default)
+ resource.guard_interpreter.should == :default
+ end
+
+ it "should raise Chef::Exceptions::ValidationFailed on an attempt to set the guard_interpreter attribute to something other than a Symbol" do
+ expect { resource.guard_interpreter('command_dot_com') }.to raise_error(Chef::Exceptions::ValidationFailed)
+ end
+
+ it "should not raise an exception when setting the guard interpreter attribute to a Symbol" do
+ Chef::GuardInterpreter::ResourceGuardInterpreter.stub(:new).and_return(nil)
+ expect { resource.guard_interpreter(:command_dot_com) }.not_to raise_error
+ end
+ end
+
end
describe "should_skip?" do