Chef Advanced Debugging: Detecting bad attributes earlier

Shows an advanced Chef debugging technique that monkey-patches attribute access to fail fast on nil values. The approach helps surface bad configuration data earlier with clearer error messages.

Coveros Staff

February 15, 2015

As I talked about last month when trying to demystify  “UNDEFINED METHOD `[]’ FOR NIL:NILCLASS” error, it can be challenging to make sure all your attributes are set the way you want, or even at all.

One thing that should be detectable is anytime you get nothing (nil) back from a Node[] call, we might as well throw an error now.  Some might want to

use

abuse the nil by conditional transformed to ‘false’:

 

puts “More info here” if node[:options][:verbose]

or transformed into empty string:

mypath = “#{node[:path_prefix]}/#{node[:destination_folder]}/”

But, you should instead use those classes directly instead:

defaults[:options][:verbose] = false
defaults[:path_prefix] = “”
defaults[:destination_folder] = “”

So, if you agree we should never be using nil as a valid Node[] return- then we can simply automatically detect it in the node[] function itself!  For this we are going make a Chef library by including some Ruby and add functionality to Chef directly.

This is advanced dark-art in Ruby.  We can combing monkey-patching with alias_method to effectively add any code to the beginning or end of anyone else’s function.  Specifically, we want to check the return of Node[] and thrown an error before anyone tried to access a resulting nil.  In this way, we factor out a lot of error checking by putting it into the Node[] function call, and relieving the cookbook & recipe writers from having to write it themselves.

 


safety.rb
1    #Chef::Node.strict_retrieval defaults to true
2    #Chef::Node.debug defaults to true
3    #Mash.strict_retrieval defaults to true
4    #Mash.debug defaults to false
5
6    class Chef
7      class Node
          # alias_method allows us to make a copy of a function
          # This will copy the old [] operator to function "old_squarebraket"
8        alias_method :old_squarebracket, "[]".to_sym
9        @@strict_retrieval=true
10       def self.strict_retrieval=(v)
11         @@strict_retrieval=v
12       end
13       def self.strict_retrieval
14         @@strict_retrieval
15       end
16
17       @@debug=true
18       def self.debug=(v)
19         @@debug=v
20       end
21       def self.debug
22         @@debug
23       end
24
           # This redefinition of the [] operator, I call the original copy operator
           # then add any error checking.
25       def [](v)
26         oret = self.old_squarebracket(v)
27         if @@strict_retrieval
28           raise "Attribute key(#{v}) returned nil. (This error can be over written by setting the Chef::Node.strict_retrieval=false)" if oret.nil?
29         end
30         puts "Debug: Attribute key(#{v}) accessed, returning: #{oret.inspect}" if @@debug
31         oret
32       end
33     end #class Node
34   end #class Chef
35
36   class Mash
37     alias_method :old_squarebracket, "[]".to_sym
38
39     @@strict_retrieval=true
40     def self.strict_retrieval=(v)
41       @@strict_retrieval=v
42     end
43     def self.strict_retrieval
44       @@strict_retrieval
45     end
46
47     @@debug=false
48     def self.debug=(v)
49       @@debug=v
50     end
51     def self.debug
52       @@debug
53     end
54
55     def [](v)
56       oret = self.old_squarebracket(v)
57       if @@strict_retrieval
58         raise "Attribute key(#{v}) returned nil. (This error can be over written by setting the Mash.strict_retrieval=false)" if oret.nil?
59       end
60       puts "Debug: Attribute key(#{v}) accessed, returning: #{oret.inspect}" if @@debug
61       oret
62     end #[]
63   end #class Mash
64

 

The previous code allows us to get this:

RuntimeError

————

Attribute key(my_attribute) returned nil. (This error can be over written by setting the Mash.strict_retrieval=false)

instead of this:

NoMethodError

————-

undefined method `[]’ for nil:NilClass

Which error would you rather get?

Coveros Staff

Coveros Staff

This post represents the collective insights of the Coveros team. Our staff consists of software experts who bring deep experience in secure agile development, DevOps, testing, and software quality. Over the past 20 years, Coveros has trained more than 30,000 professionals and worked with half of the Fortune 100 companies on mission-critical software development challenges. We draw on this extensive experience to share practical insights, proven strategies, and real-world solutions that help organizations build better software faster and more securely.