rubypuppetfactfacter

Creating nested Puppet fact (Ruby) by iterating over gem query output


I have working Ruby code to query DNS details and create Puppet custom facts (puppet 5, Facter 3.11.6) however I am trying to modify it to create nested facts from the key/value pairs that the query obtains.

Code that works to set individual facts with the key name is:

  require 'resolv'
  Resolv::DNS::Config.default_config_hash.each do | key, value |
    if !value.nil?
      Facter.add("dns_#{key}") do
        if value.is_a?(Array)
          setcode { value.join(',') }
        else
          setcode { value }
        end
      end
    end
  end

which creates individual facts thus:

dns_nameserver => 192.168.1.1,192.168.1.2
dns_ndots => 1
dns_search => test.domain

My failed attempt so far to create a nested fact under the parent fact of 'DNS' is:

require 'resolv'
Facter.add("dns") do
  value ={}
  Resolv::DNS::Config.default_config_hash.each do | key, result |
    if !result.nil?
      if result.is_a?(Array)
        setcode { value['#{key}'] = result.join(',') }
      else
        setcode { value['#{key}'] = result }
      end
    end
  end
end

which gives a limited result of just:

dns => 1

Other code I have tried seems to put an array output into the string and multiple IPs are quoted inside square brackets over 2 lines instead of being output as per the first code block at top of page.

The fact structure I am TRYING to achieve (by modifying the top of page code) is:

dns => {
  nameserver => 192.168.1.1,192.168.1.2,
  ndots => 1,
  search => test.domain,
}

Thanks in advance for any assistance.


Solution

  • I finally got this with the assistance from a poster who put some great code leads here, but unfortunately removed it soon afterward. Here is the code that works:

    require 'resolv'
    Facter.add(:networking_dns) do
      setcode do
        Resolv::DNS::Config.default_config_hash.each_with_object({}) do | (key, value), sub|
          if !value.nil?
            sub[key] = value
            sub
          end
        end
      end
    end
    

    Now for some explanatory notes (please feel free to correct me or offer any optimisations to this):

    # the resolv gem is required
    require 'resolv'
    # create the parent fact (has no value of its own)
    Facter.add(:networking_dns) do
    # start building instructions in the fact
      setcode do
    # use the resolv gem to lookup values in /etc/resolv.conf and add .each to process all key/value pairs returned
    # also add _with_object({}) and sub in the variables to set a blank value for sub.  Saves doing it separately.  Sub can be any name but denotes the declaration for the nested facts
        Resolv::DNS::Config.default_config_hash.each_with_object({}) do | (key, value), sub|
    # create facts only when the value is not nil
          if !value.nil?
            sub[key] = value
            sub
    # using a closing blank entry for a nested fact is critical or they won't create!  Place this outside of the case statement to prevent blank values
          end
        end
      end
    end
    # use the appropriate number of ends and indent for readability
    

    Thanks to the person who posted their guidance here before removing it. I would like to upvote you if you post again.

    Any tips on optimisation to the able solution are welcome, as I'm still grasping Ruby (spent hours on this!)