Class: Chef::Mixin::WhyRun::ResourceRequirements

Inherits:
Object
  • Object
show all
Defined in:
lib/chef/mixin/why_run.rb

Overview

== ResourceRequirements ResourceRequirements provides a framework for making assertions about the host system's state. It also provides a mechanism for making assumptions about what the system's state might have been when running in why run mode.

For example, consider a recipe that consists of a package resource and a service resource. If the service's init script is installed by the package, and Chef is running in why run mode, then the service resource would fail when attempting to run /etc/init.d/software-name status. In order to provide a more useful approximation of what would happen in a real chef run, we want to instead assume that the service was created but isn't running. The logic would look like this:

# Hypothetical service provider demonstrating why run assumption logic.
# This isn't the actual API, it just shows the logic.
class HypotheticalServiceProvider < Chef::Provider

  def load_current_resource
    # Make sure we have the init script available:
    if ::File.exist?("/etc/init.d/some-service"
      # If the init script exists, proceed as normal:
      status_cmd = shell_out("/etc/init.d/some-service status")
      if status_cmd.success?
        @current_resource.status(:running)
      else
        @current_resource.status(:stopped)
      end
    else
      if whyrun_mode?
        # If the init script is not available, and we're in why run mode,
        # assume that some previous action would've created it:
        log("warning: init script '/etc/init.d/some-service' is not available")
        log("warning: assuming that the init script would have been created, assuming the state of 'some-service' is 'stopped'")
        @current_resource.status(:stopped)
      else
        raise "expected init script /etc/init.d/some-service doesn't exist"
      end
    end
  end

end

In short, the code above does the following:

  • runs a test to determine if a requirement is met: ::File.exist?("/etc/init.d/some-service"
  • raises an error if the requirement is not met, and we're not in why run mode.
  • if we are in why run mode, print a message explaining the situation, and run some code that makes an assumption about what the state of the system would be. In this case, we also skip the normal load_current_resource logic
  • when the requirement is met, we run the normal load_current_resource logic

ResourceRequirements encapsulates the above logic in a more declarative API.

=== Examples Assertions and assumptions should be created through the WhyRun#assert method, which gets mixed in to providers. See that method's documentation for examples.

Defined Under Namespace

Classes: Assertion

Instance Method Summary collapse

Constructor Details

#initialize(resource, run_context) ⇒ ResourceRequirements

Returns a new instance of ResourceRequirements


247
248
249
250
251
# File 'lib/chef/mixin/why_run.rb', line 247

def initialize(resource, run_context)
  @resource, @run_context = resource, run_context
  @assertions = Hash.new { |h, k| h[k] = [] }
  @blocked_actions = []
end

Instance Method Details

#action_blocked?(action) ⇒ Boolean

Check to see if a given action is blocked by a failed assertion

Takes the action name to be verified.

Returns:

  • (Boolean)

260
261
262
# File 'lib/chef/mixin/why_run.rb', line 260

def action_blocked?(action)
  @blocked_actions.include?(action)
end

#assert(*actions) {|assertion| ... } ⇒ Object

Define a new Assertion.

Takes a list of action names for which the assertion should be made. ==== Examples: A File provider that requires the parent directory to exist:

assert(:create, :create_if_missing) do |a| parent_dir = File.basename(@new_resource.path) a.assertion { ::File.directory?(parent_dir) } a.failure_message(Exceptions::ParentDirectoryDoesNotExist, "Can't create file #Chef::Mixin::WhyRun::[email protected][email protected]_resource.path: parent directory #parent_dir doesn't exist") a.why_run("assuming parent directory #parent_dir would have been previously created" end

A service provider that requires the init script to exist:

assert(:start, :restart) do |a| a.assertion { ::File.exist?(@new_resource.init_script) } a.failure_message(Exceptions::MissingInitScript, "Can't check status of #@new_resource: init script #Chef::Mixin::WhyRun::[email protected][email protected]_resource.init_script is missing") a.why_run("Assuming init script would have been created and service is stopped") do @current_resource.status(:stopped) end end

A File provider that will error out if you don't have permissions do delete the file, even in why run mode:

assert(:delete) do |a| a.assertion { ::File.writable?(@new_resource.path) } a.failure_message(Exceptions::InsufficientPrivileges, "You don't have sufficient privileges to delete #Chef::Mixin::WhyRun::[email protected][email protected]_resource.path") end

A Template provider that will prevent action execution but continue the run in whyrun mode if the template source is not available. assert(:create, :create_if_missing) do |a| a.assertion { File::exist?(@new_resource.source) } a.failure_message Chef::Exceptions::TemplateError, "Template #Chef::Mixin::WhyRun::[email protected][email protected]_resource.source could not be found exist." a.whyrun "Template source #Chef::Mixin::WhyRun::[email protected][email protected]_resource.source does not exist. Assuming it would have been created." a.block_action! end

assert(:delete) do |a| a.assertion { ::File.writable?(@new_resource.path) } a.failure_message(Exceptions::InsufficientPrivileges, "You don't have sufficient privileges to delete #Chef::Mixin::WhyRun::[email protected][email protected]_resource.path") end

Yields:

  • (assertion)

312
313
314
315
316
# File 'lib/chef/mixin/why_run.rb', line 312

def assert(*actions)
  assertion = Assertion.new
  yield assertion
  actions.each { |action| @assertions[action] << assertion }
end

#eventsObject


253
254
255
# File 'lib/chef/mixin/why_run.rb', line 253

def events
  @run_context.events
end

#run(action) ⇒ Object

Run the assertion and assumption logic.


319
320
321
322
323
324
325
326
327
# File 'lib/chef/mixin/why_run.rb', line 319

def run(action)
  @assertions[action.to_sym].each do |a|
    a.run(action, events, @resource)
    if a.assertion_failed? && a.block_action?
      @blocked_actions << action
      break
    end
  end
end