class Chef::Provider
Attributes
Public Class Methods
Defines an action method on the provider, running the block to compile the resources, converging them, and then checking if any were updated (and updating new-resource if so)
@since 13.0 @param name [String, Symbol] Name of the action to define. @param block [Proc] Body of the action. @return [void]
# File lib/chef/provider.rb, line 63 def self.action(name, &block) # We need the block directly in a method so that `super` works. define_method("compile_action_#{name}", &block) class_eval <<-EOM def action_#{name} compile_and_converge_action { compile_action_#{name} } end EOM end
Include attributes, public and protected methods from this Resource in the provider.
If this is set to true, delegate methods are included in the provider so that you can call (for example) `attrname` and it will call `new_resource.attrname`.
The actual include does not happen until the first time the Provider is instantiated (so that we don't have to worry about load order issues).
@param include_resource_dsl [Boolean] Whether to include resource DSL or
not (defaults to `false`).
# File lib/chef/provider.rb, line 343 def self.include_resource_dsl? false end
Create the resource DSL module that forwards resource methods to #new_resource
@api private
# File lib/chef/provider.rb, line 350 def self.include_resource_dsl_module(resource) if include_resource_dsl? && !defined?(@included_resource_dsl_module) provider_class = self @included_resource_dsl_module = Module.new do extend Forwardable define_singleton_method(:to_s) { "forwarder module for #{provider_class}" } define_singleton_method(:inspect) { to_s } # this magic, stated simply, is that any instance method declared directly on # the resource we are building, will be accessible from the action_class(provider) # instance. methods declared on Chef::Resource and properties are not inherited. dsl_methods = resource.class.public_instance_methods + resource.class.protected_instance_methods - provider_class.instance_methods - resource.class.properties.keys - resource.class.properties.keys.map { |k| "#{k}=".to_sym } - Chef::Resource.instance_methods def_delegators(:new_resource, *dsl_methods) end include @included_resource_dsl_module end end
# File lib/chef/provider.rb, line 92 def initialize(new_resource, run_context) @new_resource = new_resource @action = action @current_resource = nil @run_context = run_context @converge_actions = nil @logger = if run_context run_context.logger.with_child({ resource: new_resource.name, cookbook: cookbook_name, recipe: recipe_name }) else Chef::Log.with_child({ resource: new_resource.name, cookbook: cookbook_name, recipe: recipe_name }) end @recipe_name = nil @cookbook_name = nil self.class.include_resource_dsl_module(new_resource) end
# File lib/chef/provider.rb, line 321 def self.provides(short_name, opts = {}, &block) Chef.provider_handler_map.set(short_name, self, opts, &block) end
# File lib/chef/provider.rb, line 325 def self.provides?(node, resource) Chef::ProviderResolver.new(node, resource, :nothing).provided_by?(self) end
supports the given resource and action (late binding)
# File lib/chef/provider.rb, line 51 def self.supports?(resource, action) true end
Deprecation stub for the old ::use_inline_resources mode.
@return [void]
# File lib/chef/provider.rb, line 76 def self.use_inline_resources # Uncomment this in Chef 13.6. # Chef.deprecated(:use_inline_resources, "The use_inline_resources mode is no longer optional and the line enabling it can be removed") end
Public Instance Methods
# File lib/chef/provider.rb, line 144 def action_nothing logger.trace("Doing nothing for #{@new_resource}") true end
# File lib/chef/provider.rb, line 131 def check_resource_semantics! end
# File lib/chef/provider.rb, line 141 def cleanup_after_converge end
Create a child #run_context, compile the block, and converge it.
@api private
# File lib/chef/provider.rb, line 233 def compile_and_converge_action(&block) old_run_context = run_context @run_context = run_context.create_child return_value = instance_eval(&block) Chef::Runner.new(run_context).converge return_value ensure if run_context.resource_collection.any? { |r| r.updated? } new_resource.updated_by_last_action(true) end @run_context = old_run_context end
# File lib/chef/provider.rb, line 226 def converge_by(descriptions, &block) converge_actions.add_action(descriptions, &block) end
Handle patchy convergence safely.
-
Does not call the block if the #current_resource's properties match the properties the user specified on the resource.
-
Calls the block if #current_resource does not exist
-
Calls the block if the user has specified any properties in the resource whose values are different from current_resource.
-
Does not call the block if why-run is enabled (just prints out text).
-
Prints out automatic green text saying what properties have changed.
@param properties An optional list of property names (symbols). If not
specified, `new_resource.class.state_properties` will be used.
@param converge_block The block to do the converging in.
@return [Boolean] whether the block was executed.
# File lib/chef/provider.rb, line 263 def converge_if_changed(*properties, &converge_block) if !converge_block raise ArgumentError, "converge_if_changed must be passed a block!" end properties = new_resource.class.state_properties.map { |p| p.name } if properties.empty? properties = properties.map { |p| p.to_sym } if current_resource # Collect the list of modified properties specified_properties = properties.select { |property| new_resource.property_is_set?(property) } modified = specified_properties.select { |p| new_resource.send(p) != current_resource.send(p) } if modified.empty? properties_str = if new_resource.sensitive specified_properties.join(", ") else specified_properties.map do |property| "#{property}=" << if new_resource.class.properties[property].sensitive? "(suppressed sensitive property)" else new_resource.send(property).inspect end end.join(", ") end logger.debug("Skipping update of #{new_resource}: has not changed any of the specified properties #{properties_str}.") return false end # Print the pretty green text and run the block property_size = modified.map { |p| p.size }.max modified.map! do |p| properties_str = if new_resource.sensitive || new_resource.class.properties[p].sensitive? "(suppressed sensitive property)" else "#{new_resource.send(p).inspect} (was #{current_resource.send(p).inspect})" end " set #{p.to_s.ljust(property_size)} to #{properties_str}" end converge_by([ "update #{current_resource.identity}" ] + modified, &converge_block) else # The resource doesn't exist. Mark that we are *creating* this, and # write down any properties we are setting. property_size = properties.map { |p| p.size }.max created = properties.map do |property| default = " (default value)" unless new_resource.property_is_set?(property) properties_str = if new_resource.sensitive || new_resource.class.properties[property].sensitive? "(suppressed sensitive property)" else new_resource.send(property).inspect end " set #{property.to_s.ljust(property_size)} to #{properties_str}#{default}" end converge_by([ "create #{new_resource.identity}" ] + created, &converge_block) end true end
# File lib/chef/provider.rb, line 127 def cookbook_name new_resource.cookbook_name end
# File lib/chef/provider.rb, line 138 def define_resource_requirements end
# File lib/chef/provider.rb, line 212 def description(description = "NOT_PASSED") if description != "NOT_PASSED" @description = description end @description end
# File lib/chef/provider.rb, line 149 def events run_context.events end
# File lib/chef/provider.rb, line 219 def introduced(introduced = "NOT_PASSED") if introduced != "NOT_PASSED" @introduced = introduced end @introduced end
# File lib/chef/provider.rb, line 134 def load_current_resource raise Chef::Exceptions::Override, "You must override load_current_resource in #{self}" end
# File lib/chef/provider.rb, line 118 def node run_context && run_context.node end
# File lib/chef/provider.rb, line 190 def process_resource_requirements requirements.run(:all_actions) unless @action == :nothing requirements.run(@action) end
# File lib/chef/provider.rb, line 208 def requirements @requirements ||= ResourceRequirements.new(@new_resource, run_context) end
Used by providers supporting embedded recipes
# File lib/chef/provider.rb, line 123 def resource_collection run_context && run_context.resource_collection end
# File lib/chef/provider.rb, line 195 def resource_updated? !converge_actions.empty? || @new_resource.updated_by_last_action? end
# File lib/chef/provider.rb, line 153 def run_action(action = nil) @action = action unless action.nil? # TODO: it would be preferable to get the action to be executed in the # constructor... check_resource_semantics! # user-defined LWRPs may include unsafe load_current_resource methods that cannot be run in whyrun mode if whyrun_mode? && !whyrun_supported? events.resource_current_state_load_bypassed(@new_resource, @action, @current_resource) else load_current_resource events.resource_current_state_loaded(@new_resource, @action, @current_resource) end define_resource_requirements process_resource_requirements # user-defined providers including LWRPs may # not include whyrun support - if they don't support it # we can't execute any actions while we're running in # whyrun mode. Instead we 'fake' whyrun by documenting that # we can't execute the action. # in non-whyrun mode, this will still cause the action to be # executed normally. if whyrun_mode? && (!whyrun_supported? || requirements.action_blocked?(@action)) events.resource_bypassed(@new_resource, @action, self) else send("action_#{@action}") end set_updated_status cleanup_after_converge end
# File lib/chef/provider.rb, line 199 def set_updated_status if !resource_updated? events.resource_up_to_date(@new_resource, @action) else events.resource_updated(@new_resource, @action) new_resource.updated_by_last_action(true) end end
# File lib/chef/provider.rb, line 110 def whyrun_mode? Chef::Config[:why_run] end
# File lib/chef/provider.rb, line 114 def whyrun_supported? true end
Protected Instance Methods
# File lib/chef/provider.rb, line 375 def converge_actions @converge_actions ||= ConvergeActions.new(@new_resource, run_context, @action) end
# File lib/chef/provider.rb, line 379 def recipe_eval(&block) # This block has new resource definitions within it, which # essentially makes it an in-line Chef run. Save our current # run_context and create one anew, so the new Chef run only # executes the embedded resources. # # TODO: timh,cw: 2010-5-14: This means that the resources within # this block cannot interact with resources outside, e.g., # manipulating notifies. converge_by ("evaluate block and run any associated actions") do saved_run_context = run_context begin @run_context = run_context.create_child instance_eval(&block) Chef::Runner.new(run_context).converge ensure @run_context = saved_run_context end end end