Skip to content

Instantly share code, notes, and snippets.

@kwilczynski
Last active July 6, 2023 08:35
Show Gist options
  • Star 39 You must be signed in to star a gist
  • Fork 7 You must be signed in to fork a gist
  • Save kwilczynski/c271f251ea7f48ba99b3 to your computer and use it in GitHub Desktop.
Save kwilczynski/c271f251ea7f48ba99b3 to your computer and use it in GitHub Desktop.
Recipe / Role check in Chef

If you want to check whether a node run_list includes a specific role (upon expansion), then you could use role? method on the Node object:

node.role?('name')

Alternatively, you can see whether either would work for you:

node.roles.include?('name')

node.run_list?('role[name]')

There exist also node.run_state[:seen_recipes], but it often has different use case.

Methods on the Node object:

recipe? https://github.com/opscode/chef/blob/master/lib/chef/node.rb#L247

role? https://github.com/opscode/chef/blob/master/lib/chef/node.rb#L259

run_list? https://github.com/opscode/chef/blob/master/lib/chef/node.rb#L288

All of the above should be present in reasonably up-to-date version of the Chef Client.

Let's see whether they work ... Details about the node:

[chef@C3394864441 ~]$ knife node show ChefDK
Node Name:   ChefDK
Environment: _default
FQDN:        C3394864441.domain
IP:          10.160.200.69
Run List:    role[base_workstation]
Roles:       base_workstation
Recipes:     workstation, workstation::default
Platform:    centos 6.5
Tags:

Details about the role:

[chef@C3394864441 ~]$ knife role show base_workstation
chef_type:           role
default_attributes:
  chef_client:
    interval: 60
    splay:    30
  push_jobs:
    package_url: https://s3.amazonaws.com/opscode-private-chef/el/6/x86_64/opscode-push-jobs-client-1.0.2-1.el6.x86_64.rpm
description:         Base role for workstation nodes
env_run_lists:
json_class:          Chef::Role
name:                base_workstation
override_attributes:
run_list:            recipe[workstation]
[chef@C3394864441 ~]$

Testing to see what is included in the run_list (running chef-shell in client mode):

[chef@C3394864441 ~]$ sudo chef-shell -z
loading configuration: /etc/chef/client.rb
Session type: client
Loading....resolving cookbooks for run list: ["workstation"]
Synchronizing Cookbooks:
  - workstation
.done.

This is the chef-shell.
 Chef Version: 11.14.0.rc.2
 http://www.opscode.com/chef
 http://docs.opscode.com/

run `help' for help, `exit' or ^D to quit.

Ohai2u chef@C3394864441.domain!
chef > node.recipe?('workstation')
 => true
chef > node.recipe?('base_workstation')
 => false
chef > node.role?('workstation')
 => false
chef > node.role?('base_workstation')
 => true
chef > node.run_list?('workstation')
 => false
chef > node.run_list?('base_workstation')
 => false
chef > node.run_list?('recipe[workstation]')
 => false
chef > node.run_list?('role[workstation]')
 => false
chef > node.run_list?('role[base_workstation]')
 => true
chef >

We can see that all methods work, but only recipe? looks for given recipes in the expanded run_list. For instance, base_workstation will expand into workstation at the beginning of the Chef client run, so all the recipes it includes will be applied accordingly and in order during the node convergence.

Other ideas and/or explanation can be found here: http://stackoverflow.com/questions/22865769/how-to-check-if-recipe-has-been-included-using-include-recipe

@spuder
Copy link

spuder commented Oct 1, 2015

I find inconsistent results when doing node.recipe?('foo').
I have better luck with the following:

 if node['recipes'].include?('bar') || node['recipes'].include?('foo::bar')

Credit to coderanger in irc for pointing out this syntax.

@kwilczynski
Copy link
Author

@spuder thank you for taking the time to add this! Really nice!

By the way, what problems have you had with node.recipe?? I am curious whether it would be worth documenting it.

@neurogenesis
Copy link

@spuder, also curious about cases where node.recipe? doesn't work as expected.

tests with chef-shell:

chef > node.run_list.add "recipe[mycookbook]"
chef > node.run_list.add "recipe[acme::anvil]"

# test for cookbook default recipe
chef > node.recipe? "mycookbook"
 => true
chef > node.recipe? "mycookbook::default"
 => false
chef > node.run_list.add "recipe[mycookbook::default]"
chef > node.recipe? "mycookbook::default"
 => true

# test for cookbook + recipe
chef > node.recipe? "acme::anvil"
 => true

# confirm exact matches
chef > node.recipe? "mycook"
 => false
chef > node.recipe? "acme::anvi"
 => false

# confirm role vs. recipe
chef > node.run_list.add "role[acme_web_server]"
chef > node.role? "acme_web_server"
 => true
chef > node.recipe? "acme_web_server"
 => false

Based on your test against node['recipes'], it would seem that recipe would fail when you're testing for a cookbook's default recipe when not explicitly specified in the runlist.

Since the default recipe is run if only the cookbook is specified, node.recipe? "mycookbook::default" should return true when the runlist specifies only "mycookbook".

@lamont-granquist
Copy link

lamont-granquist commented Jul 27, 2016

node['recipes'] used to have inconsistent semantics as to if acme or acme::default was added to it. some time ago (12.6 to 12.8 ish timeframe?) the semantics were changed so that both were added to it so that node.recipe?("acme") or node.recipe?("acme::default") should both work fine.

note that instead of:

 if node['recipes'].include?('bar') || node['recipes'].include?('bar::default')

this could always have been used equivalently:

 if node.recipes?('bar') || node.recipes?('bar::default')

i would strongly recommend being explicit now however:

 if  node.recipes?('bar::default')

@shortdudey123
Copy link

@lamont-granquist do you mean node.recipe??
I don't see anything in the codebase called recipes?

    NoMethodError
    -------------
    Undefined node attribute or method `recipes?' on `node'

Chef 12.6.0

Copy link

ghost commented Oct 21, 2016

node.read('roles').include?('role_name')

@revanthreddy36
Copy link

revanthreddy36 commented Dec 15, 2016

I have a block of code in my recipe like this :

if (node.chef_environemnt == "QA" && node.role?('rolename'))
  group 'greoupname' do
  action :create
 end 
end

How do I test this condition while using chefspec tests..The following code works for testing with environemnts.
my recipe_spec.rb has this

require 'spec_helper'

 describe 'cookbook::recipe' do
  let(:chef_run) do
  runner = ChefSpec::SoloRunner.new(platform: 'redhat',version:'6.5') do |node| 
    env = Chef::Environment.new
    env.name environment             # using a variable here so we can change it in other contexts
    allow(node).to receive(:chef_environment).and_return(env.name)
    allow(Chef::Environment).to receive(:load).and_return(env)
    end.converge 'cookbook::recipe'
end

context 'in QA' do
  let(:environment) { 'QA' }         # change the environment set up above in let(:chef_run)
it 'creates group solr' do
    expect(chef_run).to create_group('groupname')
  end
end 

the above test case works perfectly. Can somebody help me how to mock a role and use that in conjunction with an environemnt. Like I want to test a block of code that runs in QA environemnt and only on those nodes which have a particular role.

Thanks in advance !

@kwilczynski
Copy link
Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment