chef 11.12.0.alpha.1 → 11.12.0.rc.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (121) hide show
  1. checksums.yaml +4 -4
  2. data/lib/chef/api_client/registration.rb +46 -9
  3. data/lib/chef/application.rb +1 -0
  4. data/lib/chef/application/client.rb +25 -24
  5. data/lib/chef/client.rb +34 -0
  6. data/lib/chef/config.rb +11 -0
  7. data/lib/chef/cookbook/chefignore.rb +10 -2
  8. data/lib/chef/cookbook/metadata.rb +31 -3
  9. data/lib/chef/cookbook/synchronizer.rb +2 -2
  10. data/lib/chef/cookbook/syntax_check.rb +4 -4
  11. data/lib/chef/encrypted_data_bag_item.rb +37 -1
  12. data/lib/chef/exceptions.rb +1 -0
  13. data/lib/chef/guard_interpreter/default_guard_interpreter.rb +42 -0
  14. data/lib/chef/guard_interpreter/resource_guard_interpreter.rb +122 -0
  15. data/lib/chef/http.rb +0 -1
  16. data/lib/chef/http/decompressor.rb +7 -4
  17. data/lib/chef/http/simple.rb +5 -0
  18. data/lib/chef/http/validate_content_length.rb +28 -12
  19. data/lib/chef/knife.rb +1 -0
  20. data/lib/chef/knife/client_bulk_delete.rb +48 -9
  21. data/lib/chef/knife/client_delete.rb +4 -4
  22. data/lib/chef/knife/cookbook_bulk_delete.rb +1 -1
  23. data/lib/chef/knife/cookbook_upload.rb +17 -7
  24. data/lib/chef/knife/core/bootstrap_context.rb +1 -1
  25. data/lib/chef/knife/core/ui.rb +42 -5
  26. data/lib/chef/knife/node_run_list_add.rb +31 -2
  27. data/lib/chef/knife/ssh.rb +44 -31
  28. data/lib/chef/knife/ssl_check.rb +213 -0
  29. data/lib/chef/knife/ssl_fetch.rb +145 -0
  30. data/lib/chef/mixin/deep_merge.rb +13 -5
  31. data/lib/chef/mixin/shell_out.rb +9 -3
  32. data/lib/chef/node.rb +23 -4
  33. data/lib/chef/node/immutable_collections.rb +32 -0
  34. data/lib/chef/platform/provider_mapping.rb +21 -18
  35. data/lib/chef/platform/query_helpers.rb +10 -2
  36. data/lib/chef/policy_builder/expand_node_object.rb +3 -6
  37. data/lib/chef/provider/cron.rb +25 -3
  38. data/lib/chef/provider/mount/mount.rb +1 -1
  39. data/lib/chef/provider/package/dpkg.rb +2 -1
  40. data/lib/chef/provider/package/windows.rb +80 -0
  41. data/lib/chef/provider/package/windows/msi.rb +69 -0
  42. data/lib/chef/provider/powershell_script.rb +19 -6
  43. data/lib/chef/provider/service/solaris.rb +11 -7
  44. data/lib/chef/resource.rb +18 -5
  45. data/lib/chef/resource/conditional.rb +20 -7
  46. data/lib/chef/resource/cron.rb +18 -2
  47. data/lib/chef/resource/execute.rb +0 -2
  48. data/lib/chef/resource/powershell_script.rb +23 -1
  49. data/lib/chef/resource/script.rb +25 -0
  50. data/lib/chef/resource/subversion.rb +4 -0
  51. data/lib/chef/resource/windows_package.rb +79 -0
  52. data/lib/chef/resource/windows_script.rb +0 -5
  53. data/lib/chef/resources.rb +1 -0
  54. data/lib/chef/rest.rb +6 -1
  55. data/lib/chef/run_context.rb +22 -2
  56. data/lib/chef/run_context/cookbook_compiler.rb +12 -0
  57. data/lib/chef/util/editor.rb +92 -0
  58. data/lib/chef/util/file_edit.rb +22 -54
  59. data/lib/chef/version.rb +2 -2
  60. data/lib/chef/win32/api/installer.rb +166 -0
  61. data/lib/chef/win32/version.rb +8 -0
  62. data/spec/data/standalone_cookbook/Gemfile +1 -0
  63. data/spec/data/standalone_cookbook/chefignore +9 -0
  64. data/spec/data/standalone_cookbook/recipes/default.rb +3 -0
  65. data/spec/data/standalone_cookbook/vendor/bundle/ruby/2.0.0/gems/multi_json-1.9.0/lib/multi_json.rb +1 -0
  66. data/spec/functional/resource/powershell_spec.rb +262 -1
  67. data/spec/functional/win32/versions_spec.rb +3 -3
  68. data/spec/integration/knife/chefignore_spec.rb +1 -2
  69. data/spec/integration/knife/raw_spec.rb +8 -13
  70. data/spec/integration/knife/redirection_spec.rb +6 -14
  71. data/spec/integration/solo/solo_spec.rb +19 -0
  72. data/spec/support/shared/functional/windows_script.rb +1 -1
  73. data/spec/support/shared/integration/app_server_support.rb +42 -0
  74. data/spec/support/shared/integration/integration_helper.rb +1 -0
  75. data/spec/support/shared/unit/script_resource.rb +38 -0
  76. data/spec/unit/api_client/registration_spec.rb +109 -38
  77. data/spec/unit/application/client_spec.rb +48 -1
  78. data/spec/unit/cookbook/chefignore_spec.rb +10 -0
  79. data/spec/unit/cookbook/metadata_spec.rb +45 -1
  80. data/spec/unit/cookbook/syntax_check_spec.rb +28 -0
  81. data/spec/unit/cookbook_spec.rb +0 -10
  82. data/spec/unit/guard_interpreter/resource_guard_interpreter_spec.rb +56 -0
  83. data/spec/unit/http/simple_spec.rb +32 -0
  84. data/spec/unit/http/validate_content_length_spec.rb +187 -0
  85. data/spec/unit/knife/bootstrap_spec.rb +13 -4
  86. data/spec/unit/knife/client_bulk_delete_spec.rb +123 -38
  87. data/spec/unit/knife/client_delete_spec.rb +4 -4
  88. data/spec/unit/knife/cookbook_upload_spec.rb +181 -88
  89. data/spec/unit/knife/core/bootstrap_context_spec.rb +11 -1
  90. data/spec/unit/knife/core/ui_spec.rb +109 -38
  91. data/spec/unit/knife/node_run_list_add_spec.rb +24 -1
  92. data/spec/unit/knife/ssh_spec.rb +17 -6
  93. data/spec/unit/knife/ssl_check_spec.rb +187 -0
  94. data/spec/unit/knife/ssl_fetch_spec.rb +151 -0
  95. data/spec/unit/mixin/deep_merge_spec.rb +17 -0
  96. data/spec/unit/node/immutable_collections_spec.rb +55 -0
  97. data/spec/unit/node_spec.rb +9 -0
  98. data/spec/unit/platform/query_helpers_spec.rb +32 -0
  99. data/spec/unit/platform_spec.rb +193 -175
  100. data/spec/unit/policy_builder/expand_node_object_spec.rb +1 -1
  101. data/spec/unit/provider/cron_spec.rb +175 -1
  102. data/spec/unit/provider/mount/mount_spec.rb +33 -3
  103. data/spec/unit/provider/package/dpkg_spec.rb +4 -0
  104. data/spec/unit/provider/package/windows/msi_spec.rb +60 -0
  105. data/spec/unit/provider/package/windows_spec.rb +80 -0
  106. data/spec/unit/provider/service/macosx_spec.rb +3 -3
  107. data/spec/unit/provider/service/solaris_smf_service_spec.rb +35 -10
  108. data/spec/unit/pure_application_spec.rb +32 -0
  109. data/spec/unit/recipe_spec.rb +4 -0
  110. data/spec/unit/resource/conditional_spec.rb +13 -12
  111. data/spec/unit/resource/cron_spec.rb +7 -2
  112. data/spec/unit/resource/powershell_spec.rb +85 -2
  113. data/spec/unit/resource/subversion_spec.rb +5 -0
  114. data/spec/unit/resource/windows_package_spec.rb +74 -0
  115. data/spec/unit/resource_spec.rb +23 -1
  116. data/spec/unit/rest_spec.rb +15 -0
  117. data/spec/unit/run_context/cookbook_compiler_spec.rb +12 -0
  118. data/spec/unit/run_context_spec.rb +7 -0
  119. data/spec/unit/util/editor_spec.rb +152 -0
  120. data/spec/unit/util/file_edit_spec.rb +37 -1
  121. metadata +41 -30
@@ -25,11 +25,14 @@ class Chef
25
25
  class Cron < Chef::Provider
26
26
  include Chef::Mixin::Command
27
27
 
28
+ SPECIAL_TIME_VALUES = [:reboot, :yearly, :annually, :monthly, :weekly, :daily, :midnight, :hourly]
29
+ CRON_ATTRIBUTES = [:minute, :hour, :day, :month, :weekday, :time, :command, :mailto, :path, :shell, :home, :environment]
30
+ WEEKDAY_SYMBOLS = [:sunday, :monday, :tuesday, :wednesday, :thursday, :friday, :saturday]
31
+
28
32
  CRON_PATTERN = /\A([-0-9*,\/]+)\s([-0-9*,\/]+)\s([-0-9*,\/]+)\s([-0-9*,\/]+|[a-zA-Z]{3})\s([-0-9*,\/]+|[a-zA-Z]{3})\s(.*)/
33
+ SPECIAL_PATTERN = /\A(@(#{SPECIAL_TIME_VALUES.join('|')}))\s(.*)/
29
34
  ENV_PATTERN = /\A(\S+)=(\S*)/
30
35
 
31
- CRON_ATTRIBUTES = [:minute, :hour, :day, :month, :weekday, :command, :mailto, :path, :shell, :home, :environment]
32
-
33
36
  def initialize(new_resource, run_context)
34
37
  super(new_resource, run_context)
35
38
  @cron_exists = false
@@ -58,6 +61,12 @@ class Chef
58
61
  when ENV_PATTERN
59
62
  set_environment_var($1, $2) if cron_found
60
63
  next
64
+ when SPECIAL_PATTERN
65
+ if cron_found
66
+ @current_resource.time($2.to_sym)
67
+ @current_resource.command($3)
68
+ cron_found=false
69
+ end
61
70
  when CRON_PATTERN
62
71
  if cron_found
63
72
  @current_resource.minute($1)
@@ -220,9 +229,22 @@ class Chef
220
229
  @new_resource.environment.each do |name, value|
221
230
  newcron << "#{name}=#{value}\n"
222
231
  end
223
- newcron << "#{@new_resource.minute} #{@new_resource.hour} #{@new_resource.day} #{@new_resource.month} #{@new_resource.weekday} #{@new_resource.command}\n"
232
+ if @new_resource.time
233
+ newcron << "@#{@new_resource.time} #{@new_resource.command}\n"
234
+ else
235
+ newcron << "#{@new_resource.minute} #{@new_resource.hour} #{@new_resource.day} #{@new_resource.month} #{@new_resource.weekday} #{@new_resource.command}\n"
236
+ end
224
237
  newcron
225
238
  end
239
+
240
+ def weekday_in_crontab
241
+ weekday_in_crontab = WEEKDAY_SYMBOLS.index(@new_resource.weekday)
242
+ if weekday_in_crontab.nil?
243
+ @new_resource.weekday
244
+ else
245
+ weekday_in_crontab.to_s
246
+ end
247
+ end
226
248
  end
227
249
  end
228
250
  end
@@ -244,7 +244,7 @@ class Chef
244
244
  # So given a symlink like this:
245
245
  # /dev/mapper/vgroot-tmp.vol -> /dev/dm-9
246
246
  # First it will try to match "/dev/mapper/vgroot-tmp.vol". If there is no match it will try matching for "/dev/dm-9".
247
- "(?:#{Regexp.escape(device_real)}|#{Regexp.escape(::File.readlink(device_real))})"
247
+ "(?:#{Regexp.escape(device_real)}|#{Regexp.escape(::File.expand_path(::File.readlink(device_real),::File.dirname(device_real)))})"
248
248
  else
249
249
  Regexp.escape(device_real)
250
250
  end
@@ -25,7 +25,8 @@ class Chef
25
25
  class Provider
26
26
  class Package
27
27
  class Dpkg < Chef::Provider::Package::Apt
28
- DPKG_INFO = /([a-z\d\-\+\.]+)\t([\w\d.~-]+)/
28
+ # http://www.debian.org/doc/debian-policy/ch-controlfields.html#s-f-Version
29
+ DPKG_INFO = /([a-z\d\-\+\.]+)\t([\w\d.~:-]+)/
29
30
  DPKG_INSTALLED = /^Status: install ok installed/
30
31
  DPKG_VERSION = /^Version: (.+)$/
31
32
 
@@ -0,0 +1,80 @@
1
+ #
2
+ # Author:: Bryan McLellan <btm@loftninjas.org>
3
+ # Copyright:: Copyright (c) 2014 Chef Software, Inc.
4
+ # License:: Apache License, Version 2.0
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+
19
+ require 'chef/resource/windows_package'
20
+ require 'chef/provider/package'
21
+
22
+ class Chef
23
+ class Provider
24
+ class Package
25
+ class Windows < Chef::Provider::Package
26
+
27
+ # Depending on the installer, we may need to examine installer_type or
28
+ # source attributes, or search for text strings in the installer file
29
+ # binary to determine the installer type for the user. Since the file
30
+ # must be on disk to do so, we have to make this choice in the provider.
31
+ require 'chef/provider/package/windows/msi.rb'
32
+
33
+ # load_current_resource is run in Chef::Provider#run_action when not in whyrun_mode?
34
+ def load_current_resource
35
+ @current_resource = Chef::Resource::WindowsPackage.new(@new_resource.name)
36
+ @current_resource.version(package_provider.installed_version)
37
+ @new_resource.version(package_provider.package_version)
38
+ @current_resource
39
+ end
40
+
41
+ def package_provider
42
+ @package_provider ||= begin
43
+ case installer_type
44
+ when :msi
45
+ Chef::Provider::Package::Windows::MSI.new(@new_resource)
46
+ else
47
+ raise "Unable to find a Chef::Provider::Package::Windows provider for installer_type '#{installer_type}'"
48
+ end
49
+ end
50
+ end
51
+
52
+ def installer_type
53
+ @installer_type ||= begin
54
+ if @new_resource.installer_type
55
+ @new_resource.installer_type
56
+ else
57
+ file_extension = ::File.basename(@new_resource.source).split(".").last.downcase
58
+
59
+ if file_extension == "msi"
60
+ :msi
61
+ else
62
+ raise ArgumentError, "Installer type for Windows Package '#{@new_resource.name}' not specified and cannot be determined from file extension '#{file_extension}'"
63
+ end
64
+ end
65
+ end
66
+ end
67
+
68
+ # Chef::Provider::Package action_install + action_remove call install_package + remove_package
69
+ # Pass those calls to the correct sub-provider
70
+ def install_package(name, version)
71
+ package_provider.install_package(name, version)
72
+ end
73
+
74
+ def remove_package(name, version)
75
+ package_provider.remove_package(name, version)
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,69 @@
1
+ #
2
+ # Author:: Bryan McLellan <btm@loftninjas.org>
3
+ # Copyright:: Copyright (c) 2014 Chef Software, Inc.
4
+ # License:: Apache License, Version 2.0
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+
19
+ # TODO: Allow @new_resource.source to be a Product Code as a GUID for uninstall / network install
20
+
21
+ require 'chef/mixin/shell_out'
22
+ require 'chef/win32/api/installer' if RUBY_PLATFORM =~ /mswin|mingw32|windows/
23
+
24
+ class Chef
25
+ class Provider
26
+ class Package
27
+ class Windows
28
+ class MSI
29
+ include Chef::ReservedNames::Win32::API::Installer if RUBY_PLATFORM =~ /mswin|mingw32|windows/
30
+ include Chef::Mixin::ShellOut
31
+
32
+ def initialize(resource)
33
+ @new_resource = resource
34
+ end
35
+
36
+ # From Chef::Provider::Package
37
+ def expand_options(options)
38
+ options ? " #{options}" : ""
39
+ end
40
+
41
+ # Returns a version if the package is installed or nil if it is not.
42
+ def installed_version
43
+ Chef::Log.debug("#{@new_resource} getting product code for package at #{@new_resource.source}")
44
+ product_code = get_product_property(@new_resource.source, "ProductCode")
45
+ Chef::Log.debug("#{@new_resource} checking package status and verion for #{product_code}")
46
+ get_installed_version(product_code)
47
+ end
48
+
49
+ def package_version
50
+ Chef::Log.debug("#{@new_resource} getting product version for package at #{@new_resource.source}")
51
+ get_product_property(@new_resource.source, "ProductVersion")
52
+ end
53
+
54
+ def install_package(name, version)
55
+ # We could use MsiConfigureProduct here, but we'll start off with msiexec
56
+ Chef::Log.debug("#{@new_resource} installing MSI package '#{@new_resource.source}'")
57
+ shell_out!("msiexec /qn /i \"#{@new_resource.source}\" #{expand_options(@new_resource.options)}", {:timeout => @new_resource.timeout, :returns => @new_resource.returns})
58
+ end
59
+
60
+ def remove_package(name, version)
61
+ # We could use MsiConfigureProduct here, but we'll start off with msiexec
62
+ Chef::Log.debug("#{@new_resource} removing MSI package '#{@new_resource.source}'")
63
+ shell_out!("msiexec /qn /x \"#{@new_resource.source}\" #{expand_options(@new_resource.options)}", {:timeout => @new_resource.timeout, :returns => @new_resource.returns})
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
@@ -23,9 +23,9 @@ class Chef
23
23
  class PowershellScript < Chef::Provider::WindowsScript
24
24
 
25
25
  protected
26
-
27
- EXIT_STATUS_NORMALIZATION_SCRIPT = "\nif ($? -eq $true) {exit 0} elseif ( $LASTEXITCODE -ne 0) {exit $LASTEXITCODE} else { exit 1 }"
28
- EXIT_STATUS_RESET_SCRIPT = "$LASTEXITCODE=0\n"
26
+ EXIT_STATUS_EXCEPTION_HANDLER = "\ntrap [Exception] {write-error -exception ($_.Exception.Message);exit 1}".freeze
27
+ EXIT_STATUS_NORMALIZATION_SCRIPT = "\nif ($? -ne $true) { if ( $LASTEXITCODE -ne 0) {exit $LASTEXITCODE} else { exit 1 }}".freeze
28
+ EXIT_STATUS_RESET_SCRIPT = "\n$LASTEXITCODE=0".freeze
29
29
 
30
30
  # Process exit codes are strange with PowerShell. Unless you
31
31
  # explicitly call exit in Powershell, the powershell.exe
@@ -36,15 +36,28 @@ class Chef
36
36
  # last process run in the script if it is the last command
37
37
  # executed, otherwise 0 or 1 based on whether $? is set to true
38
38
  # (success, where we return 0) or false (where we return 1).
39
- def NormalizeScriptExitStatus( code )
40
- @code = (! code.nil?) ? ( EXIT_STATUS_RESET_SCRIPT + code + EXIT_STATUS_NORMALIZATION_SCRIPT ) : nil
39
+ def normalize_script_exit_status( code )
40
+ target_code = ( EXIT_STATUS_EXCEPTION_HANDLER +
41
+ EXIT_STATUS_RESET_SCRIPT +
42
+ "\n" +
43
+ code.to_s +
44
+ EXIT_STATUS_NORMALIZATION_SCRIPT )
45
+ convert_boolean_return = @new_resource.convert_boolean_return
46
+ @code = <<EOH
47
+ new-variable -name interpolatedexitcode -visibility private -value $#{convert_boolean_return}
48
+ new-variable -name chefscriptresult -visibility private
49
+ $chefscriptresult = {
50
+ #{target_code}
51
+ }.invokereturnasis()
52
+ if ($interpolatedexitcode -and $chefscriptresult.gettype().name -eq 'boolean') { exit [int32](!$chefscriptresult) } else { exit 0 }
53
+ EOH
41
54
  end
42
55
 
43
56
  public
44
57
 
45
58
  def initialize (new_resource, run_context)
46
59
  super(new_resource, run_context, '.ps1')
47
- NormalizeScriptExitStatus(new_resource.code)
60
+ normalize_script_exit_status(new_resource.code)
48
61
  end
49
62
 
50
63
  def flags
@@ -25,11 +25,13 @@ class Chef
25
25
  class Service
26
26
  class Solaris < Chef::Provider::Service
27
27
  include Chef::Mixin::ShellOut
28
+ attr_reader :maintenance
28
29
 
29
30
  def initialize(new_resource, run_context=nil)
30
31
  super
31
32
  @init_command = "/usr/sbin/svcadm"
32
33
  @status_command = "/bin/svcs -l"
34
+ @maintenace = false
33
35
  end
34
36
 
35
37
 
@@ -44,6 +46,7 @@ class Chef
44
46
  end
45
47
 
46
48
  def enable_service
49
+ shell_out!("#{default_init_command} clear #{@new_resource.service_name}") if @maintenance
47
50
  shell_out!("#{default_init_command} enable -s #{@new_resource.service_name}")
48
51
  end
49
52
 
@@ -65,13 +68,14 @@ class Chef
65
68
  end
66
69
 
67
70
  def service_status
68
- status = popen4("#{@status_command} #{@current_resource.service_name}") do |pid, stdin, stdout, stderr|
69
- stdout.each do |line|
70
- case line
71
- when /state\s+online/
72
- @current_resource.enabled(true)
73
- @current_resource.running(true)
74
- end
71
+ status = shell_out!("#{@status_command} #{@current_resource.service_name}")
72
+ status.stdout.each_line do |line|
73
+ case line
74
+ when /state\s+online/
75
+ @current_resource.enabled(true)
76
+ @current_resource.running(true)
77
+ when /state\s+maintenance/
78
+ @maintenance = true
75
79
  end
76
80
  end
77
81
  unless @current_resource.enabled
@@ -23,6 +23,7 @@ require 'chef/dsl/data_query'
23
23
  require 'chef/dsl/registry_helper'
24
24
  require 'chef/dsl/reboot_pending'
25
25
  require 'chef/mixin/convert_to_class_name'
26
+ require 'chef//guard_interpreter/resource_guard_interpreter'
26
27
  require 'chef/resource/conditional'
27
28
  require 'chef/resource/conditional_action_not_nothing'
28
29
  require 'chef/resource_collection'
@@ -249,6 +250,7 @@ F
249
250
  @not_if = []
250
251
  @only_if = []
251
252
  @source_line = nil
253
+ @guard_interpreter = :default
252
254
  @elapsed_time = 0
253
255
 
254
256
  @node = run_context ? deprecated_ivar(run_context.node, :node, :warn) : nil
@@ -401,6 +403,14 @@ F
401
403
  ignore_failure(arg)
402
404
  end
403
405
 
406
+ def guard_interpreter(arg=nil)
407
+ set_or_return(
408
+ :guard_interpreter,
409
+ arg,
410
+ :kind_of => Symbol
411
+ )
412
+ end
413
+
404
414
  # Sets up a notification from this resource to the resource specified by +resource_spec+.
405
415
  def notifies(action, resource_spec, timing=:delayed)
406
416
  # when using old-style resources(:template => "/foo.txt") style, you
@@ -552,7 +562,7 @@ F
552
562
  # * evaluates to false if the block is false, or if the command returns a non-zero exit code.
553
563
  def only_if(command=nil, opts={}, &block)
554
564
  if command || block_given?
555
- @only_if << Conditional.only_if(command, opts, &block)
565
+ @only_if << Conditional.only_if(self, command, opts, &block)
556
566
  end
557
567
  @only_if
558
568
  end
@@ -573,7 +583,7 @@ F
573
583
  # * evaluates to false if the block is true, or if the command returns a 0 exit status.
574
584
  def not_if(command=nil, opts={}, &block)
575
585
  if command || block_given?
576
- @not_if << Conditional.not_if(command, opts, &block)
586
+ @not_if << Conditional.not_if(self, command, opts, &block)
577
587
  end
578
588
  @not_if
579
589
  end
@@ -627,7 +637,7 @@ F
627
637
  provider_for_action(action).run_action
628
638
  rescue Exception => e
629
639
  if ignore_failure
630
- Chef::Log.error("#{self} (#{defined_at}) had an error: #{e.message}; ignore_failure is set, continuing")
640
+ Chef::Log.error("#{custom_exception_message(e)}; ignore_failure is set, continuing")
631
641
  events.resource_failed(self, action, e)
632
642
  elsif retries > 0
633
643
  events.resource_failed_retriable(self, action, retries, e)
@@ -662,8 +672,12 @@ F
662
672
  end
663
673
  end
664
674
 
675
+ def custom_exception_message(e)
676
+ "#{self} (#{defined_at}) had an error: #{e.class.name}: #{e.message}"
677
+ end
678
+
665
679
  def customize_exception(e)
666
- new_exception = e.exception("#{self} (#{defined_at}) had an error: #{e.class.name}: #{e.message}")
680
+ new_exception = e.exception(custom_exception_message(e))
667
681
  new_exception.set_backtrace(e.backtrace)
668
682
  new_exception
669
683
  end
@@ -815,6 +829,5 @@ F
815
829
  end
816
830
  end
817
831
  end
818
-
819
832
  end
820
833
  end
@@ -17,6 +17,7 @@
17
17
  #
18
18
 
19
19
  require 'chef/mixin/shell_out'
20
+ require 'chef/guard_interpreter/resource_guard_interpreter'
20
21
 
21
22
  class Chef
22
23
  class Resource
@@ -29,12 +30,12 @@ class Chef
29
30
  private :new
30
31
  end
31
32
 
32
- def self.not_if(command=nil, command_opts={}, &block)
33
- new(:not_if, command, command_opts, &block)
33
+ def self.not_if(parent_resource, command=nil, command_opts={}, &block)
34
+ new(:not_if, parent_resource, command, command_opts, &block)
34
35
  end
35
36
 
36
- def self.only_if(command=nil, command_opts={}, &block)
37
- new(:only_if, command, command_opts, &block)
37
+ def self.only_if(parent_resource, command=nil, command_opts={}, &block)
38
+ new(:only_if, parent_resource, command, command_opts, &block)
38
39
  end
39
40
 
40
41
  attr_reader :positivity
@@ -42,14 +43,16 @@ class Chef
42
43
  attr_reader :command_opts
43
44
  attr_reader :block
44
45
 
45
- def initialize(positivity, command=nil, command_opts={}, &block)
46
+ def initialize(positivity, parent_resource, command=nil, command_opts={}, &block)
46
47
  @positivity = positivity
47
48
  case command
48
49
  when String
50
+ @guard_interpreter = new_guard_interpreter(parent_resource, command, command_opts, &block)
49
51
  @command, @command_opts = command, command_opts
50
52
  @block = nil
51
53
  when nil
52
54
  raise ArgumentError, "only_if/not_if requires either a command or a block" unless block_given?
55
+ @guard_interpreter = nil
53
56
  @command, @command_opts = nil, nil
54
57
  @block = block
55
58
  else
@@ -69,11 +72,11 @@ class Chef
69
72
  end
70
73
 
71
74
  def evaluate
72
- @command ? evaluate_command : evaluate_block
75
+ @guard_interpreter ? evaluate_command : evaluate_block
73
76
  end
74
77
 
75
78
  def evaluate_command
76
- shell_out(@command, @command_opts).status.success?
79
+ @guard_interpreter.evaluate
77
80
  rescue Chef::Exceptions::CommandTimeout
78
81
  Chef::Log.warn "Command '#{@command}' timed out"
79
82
  false
@@ -100,6 +103,16 @@ class Chef
100
103
  end
101
104
  end
102
105
 
106
+ private
107
+
108
+ def new_guard_interpreter(parent_resource, command, opts)
109
+ if parent_resource.guard_interpreter == :default
110
+ guard_interpreter = Chef::GuardInterpreter::DefaultGuardInterpreter.new(command, opts)
111
+ else
112
+ guard_interpreter = Chef::GuardInterpreter::ResourceGuardInterpreter.new(parent_resource, command, opts)
113
+ end
114
+ end
115
+
103
116
  end
104
117
  end
105
118
  end