Class: ChefSpec::SoloRunner

Inherits:
Object
  • Object
show all
Includes:
Normalize
Defined in:
lib/chefspec/solo_runner.rb,
lib/chefspec/deprecations.rb

Direct Known Subclasses

ServerRunner

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Normalize

#resource_name

Constructor Details

#initialize(options = {}) {|node| ... } ⇒ SoloRunner

Instantiate a new SoloRunner to run examples with.

Examples:

Instantiate a new Runner

ChefSpec::SoloRunner.new

Specifying the platform and version

ChefSpec::SoloRunner.new(platform: 'ubuntu', version: '18.04')

Specifying the cookbook path

ChefSpec::SoloRunner.new(cookbook_path: ['/cookbooks'])

Specifying the log level

ChefSpec::SoloRunner.new(log_level: :info)

Parameters:

  • options (Hash) (defaults to: {})

    The options for the new runner

Options Hash (options):

  • :log_level (Symbol)

    The log level to use (default is :warn)

  • :platform (String)

    The platform to load Ohai attributes from (must be present in fauxhai)

  • :version (String)

    The version of the platform to load Ohai attributes from (must be present in fauxhai)

  • :path (String)

    Path of a json file that will be passed to fauxhai as :path option

  • :step_into (Array<String>)

    The list of LWRPs to evaluate

  • String (])

    tring] :file_cache_path File caching path, if absent ChefSpec will use a temporary directory generated on the fly

Yields:

  • (node)

    Configuration block for Chef::Node


65
66
67
68
69
# File 'lib/chefspec/solo_runner.rb', line 65

def initialize(options = {})
  @options = with_default_options(options)
  apply_chef_config!
  yield node if block_given?
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(m, *args, &block) ⇒ Object

Respond to custom matchers defined by the user.


296
297
298
299
300
301
302
303
# File 'lib/chefspec/solo_runner.rb', line 296

def method_missing(m, *args, &block)
  block = ChefSpec.matchers[resource_name(m.to_sym)]
  if block
    instance_exec(args.first, &block)
  else
    super
  end
end

Instance Attribute Details

#optionsHash (readonly)

Returns:

  • (Hash)

26
27
28
# File 'lib/chefspec/solo_runner.rb', line 26

def options
  @options
end

#run_contextChef::RunContext (readonly)

Returns:

  • (Chef::RunContext)

29
30
31
# File 'lib/chefspec/solo_runner.rb', line 29

def run_context
  @run_context
end

Class Method Details

.converge(*recipe_names) ⇒ Object

Handy class method for just converging a runner if you do not care about initializing the runner with custom options.

Examples:

ChefSpec::SoloRunner.converge('cookbook::recipe')

17
18
19
20
21
# File 'lib/chefspec/solo_runner.rb', line 17

def self.converge(*recipe_names)
  new.tap do |instance|
    instance.converge(*recipe_names)
  end
end

.define_runner_method(resource_name) ⇒ Object

Deprecated.

define_runner_method is deprecated. Please use ChefSpec.define_matcher instead.


25
26
27
28
29
30
31
# File 'lib/chefspec/deprecations.rb', line 25

def self.define_runner_method(resource_name)
  deprecated "`ChefSpec::Runner.define_runner_method' is deprecated." \
    " It is being used in the #{resource_name} resource matcher." \
    " Please use `ChefSpec.define_matcher' instead."

  ChefSpec.define_matcher(resource_name)
end

Instance Method Details

#compiling?true, false

Boolean method to determine the current phase of the Chef run (compiling or converging)

Returns:

  • (true, false)

240
241
242
# File 'lib/chefspec/solo_runner.rb', line 240

def compiling?
  !@converging
end

#converge(*recipe_names) {|node| ... } ⇒ ChefSpec::SoloRunner

Execute the given `run_list` on the node, without actually converging the node. Each time #converge is called, the `run_list` is reset to the new value (it is not additive).

Examples:

Converging a single recipe

chef_run.converge('example::default')

Converging multiple recipes

chef_run.converge('example::default', 'example::secondary')

Parameters:

  • recipe_names (Array)

    The names of the recipe or recipes to converge

Yields:

Returns:


89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
# File 'lib/chefspec/solo_runner.rb', line 89

def converge(*recipe_names)
  # Re-apply the Chef config before converging in case something else
  # called Config.reset too.
  apply_chef_config!
  @converging = false
  node.run_list.reset!
  recipe_names.each { |recipe_name| node.run_list.add(recipe_name) }

  return self if dry_run?

  # Expand the run_list
  expand_run_list!

  # Merge in provided node attributes. Default and override use the role_
  # levels so they win over the relevant bits from cookbooks since otherwise
  # they would not and that would be confusing.
  node.attributes.role_default = Chef::Mixin::DeepMerge.merge(node.attributes.role_default, options[:default_attributes]) if options[:default_attributes]
  node.attributes.normal = Chef::Mixin::DeepMerge.merge(node.attributes.normal, options[:normal_attributes]) if options[:normal_attributes]
  node.attributes.role_override = Chef::Mixin::DeepMerge.merge(node.attributes.role_override, options[:override_attributes]) if options[:override_attributes]
  node.attributes.automatic = Chef::Mixin::DeepMerge.merge(node.attributes.automatic, options[:automatic_attributes]) if options[:automatic_attributes]

  # Setup the run_context, rescuing the exception that happens when a
  # resource is not defined on a particular platform
  begin
    @run_context = client.setup_run_context
  rescue Chef::Exceptions::NoSuchResourceType => e
    raise Error::MayNeedToSpecifyPlatform.new(original_error: e.message)
  end

  # Allow stubbing/mocking after the cookbook has been compiled but before the converge
  yield node if block_given?

  @converging = true
  converge_val = @client.converge(@run_context)
  if converge_val.is_a?(Exception)
    raise converge_val
  end

  self
end

#converge_block(&block) ⇒ ChefSpec::SoloRunner

Execute a block of recipe code.

Parameters:

  • block (Proc)

    A block containing Chef recipe code

Returns:


138
139
140
141
142
143
# File 'lib/chefspec/solo_runner.rb', line 138

def converge_block(&block)
  converge do
    recipe = Chef::Recipe.new(cookbook_name, "_test", run_context)
    recipe.instance_exec(&block)
  end
end

#dry_run?true, false

Boolean method to determine if this Runner is in `dry_run` mode.

Returns:

  • (true, false)

268
269
270
# File 'lib/chefspec/solo_runner.rb', line 268

def dry_run?
  !!options[:dry_run]
end

#find_resource(type, name, action = nil) ⇒ Chef::Resource?

Find the resource with the declared type and resource name, and optionally match a performed action.

If multiples match it returns the last (which more or less matches the chef last-inserter-wins semantics)

Examples:

Find a template at `/etc/foo`

chef_run.find_resource(:template, '/etc/foo') #=> #<Chef::Resource::Template>

Parameters:

  • type (Symbol)

    The type of resource (sometimes called `resource_name`) such as `file` or `directory`.

  • name (String, Regexp)

    The value of the name attribute or identity attribute for the resource.

  • action (Symbol) (defaults to: nil)

    (optional) match only resources that performed the action.

Returns:

  • (Chef::Resource, nil)

    The matching resource, or nil if one is not found


209
210
211
212
213
# File 'lib/chefspec/solo_runner.rb', line 209

def find_resource(type, name, action = nil)
  resource_collection.all_resources.reverse_each.find do |resource|
    resource.declared_type == type.to_sym && (name === resource.identity || name === resource.name) && (action.nil? || resource.performed_action?(action))
  end
end

#find_resources(type) ⇒ Array<Chef::Resource>

Find the resource with the declared type.

Examples:

Find all template resources

chef_run.find_resources(:template) #=> [#<Chef::Resource::Template>, #...]

Parameters:

  • type (Symbol)

    The type of resource such as `:file` or `:directory`.

Returns:

  • (Array<Chef::Resource>)

    The matching resources


228
229
230
231
232
# File 'lib/chefspec/solo_runner.rb', line 228

def find_resources(type)
  resource_collection.all_resources.select do |resource|
    resource_name(resource) == type.to_sym
  end
end

#inspectString

The runner as a String with helpful output.

Returns:

  • (String)

287
288
289
290
291
# File 'lib/chefspec/solo_runner.rb', line 287

def inspect
  "#<#{self.class.name}" \
  " options: #{options.inspect}," \
  " run_list: [#{node.run_list}]>"
end

#nodeChef::Node

The Chef::Node corresponding to this Runner.

Returns:

  • (Chef::Node)

170
171
172
173
174
175
176
177
178
# File 'lib/chefspec/solo_runner.rb', line 170

def node
  runner = self
  @node ||= begin
    apply_chef_config!
    client.build_node.tap do |node|
      node.define_singleton_method(:runner) { runner }
    end
  end
end

#preload!void

This method returns an undefined value.

Run a static preload of the cookbook under test. This will load libraries and resources, but not attributes or recipes.


151
152
153
154
155
156
157
158
159
160
161
162
163
# File 'lib/chefspec/solo_runner.rb', line 151

def preload!
  # Flag to disable preloading for situations where it doesn't make sense.
  return if ENV["CHEFSPEC_NO_PRELOAD"]

  begin
    old_preload = $CHEFSPEC_PRELOAD
    $CHEFSPEC_PRELOAD = true
    converge("recipe[#{cookbook_name}]")
    node.run_list.reset!
  ensure
    $CHEFSPEC_PRELOAD = old_preload
  end
end

#resource_collectionHash<String, Chef::Resource>

The full collection of resources for this Runner.

Returns:

  • (Hash<String, Chef::Resource>)

185
186
187
# File 'lib/chefspec/solo_runner.rb', line 185

def resource_collection
  @resource_collection ||= @run_context.resource_collection
end

#respond_to_missing?(m, include_private = false) ⇒ Boolean

Inform Ruby that we respond to methods that are defined as custom matchers.

Returns:

  • (Boolean)

309
310
311
# File 'lib/chefspec/solo_runner.rb', line 309

def respond_to_missing?(m, include_private = false)
  ChefSpec.matchers.key?(m.to_sym) || super
end

#step_into?(resource) ⇒ true, false

Determines if the runner should step into the given resource. The step_into option takes a string, but this method coerces everything to symbols for safety.

This method also substitutes any dashes (-) with underscores (_), because that's what Chef does under the hood. (See GitHub issue #254 for more background)

Parameters:

  • resource (Chef::Resource)

    the Chef resource to try and step in to

Returns:

  • (true, false)

258
259
260
261
# File 'lib/chefspec/solo_runner.rb', line 258

def step_into?(resource)
  key = resource_name(resource)
  Array(options[:step_into]).map(&method(:resource_name)).include?(key)
end

#to_sString

This runner as a string.

may change between versions of this gem.

Returns:

  • (String)

    Currently includes the run_list. Format of the string


278
279
280
# File 'lib/chefspec/solo_runner.rb', line 278

def to_s
  "#<#{self.class.name} run_list: [#{node.run_list}]>"
end