What is the correct way to check for the existence of a nested attribute in Chef?
Asked Answered
S

5

15

There are multiple ways to check for the existence of a nested attribute in chef, and I'm not sure which is correct/best, and if any will result in empty attributes being stored on the node:

node[:parent] and node[:parent][:child]

node.attribute?(:parent) and node[:parent].attribute?(:child))

node[:parent].nil? and node[:parent][:child].nil?

It'd be greatly preferred to be able to check for the parent and child at the same time, but I don't know if that's possible. I am using Chef 10, not Chef 11, though answers explaining either are welcome.

Sensibility answered 24/9, 2013 at 15:39 Comment(0)
P
8

The correct "modern" way to do this is to use the exist?() helper:

if node.exist?('foo', 'bar', 'baz')
  # do stuff with node['foo']['bar']['baz']
end

This supersedes the old chef-sugar deep_fetch mechanism and has been built into chef-client for a very long time at this point.

There is also a read() helper to get the value out, which will return nil if any intermediate key does not exist:

fizz = node.read('foo', 'bar', 'baz')

It is identical to the Hash#dig method which was later added to ruby which is also supported as an alias:

fizz = node.dig('foo', 'bar', 'baz')
Para answered 21/8, 2020 at 0:42 Comment(0)
U
15

Node attribute object is HashMap. You can use ruby native API to lookup nested attributes.

Chef Node Object provides a number of helper methods like:

node.attribute?()
node[:foo].attribute?(:bar)
node[:foo].member?(:bar)

There is also a new method node.debug_value() in chef 11 to help you debug node attributes which is also helpful:

node.debug_value(:foo, :bar)

Details can be found from the article Chef 11 In-Depth: Attributes Changes

Undenominational answered 25/9, 2013 at 0:58 Comment(0)
P
8

The correct "modern" way to do this is to use the exist?() helper:

if node.exist?('foo', 'bar', 'baz')
  # do stuff with node['foo']['bar']['baz']
end

This supersedes the old chef-sugar deep_fetch mechanism and has been built into chef-client for a very long time at this point.

There is also a read() helper to get the value out, which will return nil if any intermediate key does not exist:

fizz = node.read('foo', 'bar', 'baz')

It is identical to the Hash#dig method which was later added to ruby which is also supported as an alias:

fizz = node.dig('foo', 'bar', 'baz')
Para answered 21/8, 2020 at 0:42 Comment(0)
S
5

The way I've solved this more recently has been to always set default values for attributes used in a cookbook where possible.

For example, cookbook/attributes/default.rb would contain:

default[:parent][:child] = nil

And the in the recipe, any check for the attributes value can be reduced to:

node[:parent][:child].nil?

Of course, it's usually far more useful to have a usable default value and to not have to check at all.

Sensibility answered 9/7, 2014 at 22:13 Comment(0)
T
2

Check out the chef-sugar cookbook deep_fetch extension that allows for safe references to deep attributes.

Tailband answered 8/7, 2014 at 23:58 Comment(0)
R
1

I found a really elegant solution here that uses rescue NoMethodError at the end of the if conditional.

if node['foo']['bar']['baz']
   do the stuff I want
end rescue NoMethodError
Recaption answered 2/7, 2020 at 20:49 Comment(2)
That will also rescue any NoMethodError caused by any other clause added to the conditional (so under extension to if node['foo']['bar']['baz'] && shoulddoit? will gobble up any programming errors in def shoulddoit? that raise NoMethodError). And it will also rescue any NoMethodError raised by the "do the stuff i want" block. For the absolutely trivial case this works, but once the trivial case becomes complicated this greatly hinders the ability to debug anything. This solution should not be used.Para
In that linked web page if the author had typo'd include_recipie "ganglia::default" (extra i in recipe, which is a reasonably common typo) then the rescue swallows the error about the typo.Para

© 2022 - 2024 — McMap. All rights reserved.