Module of the Week: puppetlabs/stdlib – Puppetlabs standard library – Part 3

Purpose Standard library for creating Puppet modules
Module puppetlabs/stdlib
Puppet Version 2.6+
Platforms Redhat, Debian, Solaris, Mac OS X, Windows
Welcome back to the Puppet Module of the Week! Previously we covered the validation functions provided by puppetlabs/stdlib. This week we're switching our focus to facter-dot-d; a simple way of pulling facts from external sources. From the docs:
A simple little framework to get facts from external sources. You can create files in /etc/facter/facts.d which can be text, yaml, json or executables. In the case of executables it supports caching so you only need to run your expensive scripts every now and then.
facter-dot-d is great for people who already have information about their nodes and would like to reuse that data without writing custom facts. facter-dot-d makes this possible by scanning the /etc/facter/facts.d directory for data files(YAML, JSON, or plain text) and scripts and then processes them to create new facts. This may seem like magic, but facter-dot-d is really just a custom fact. By using the Facter API, facter-dot-d converts external data into custom facts instead of returning a single fact. Neat huh? We thought so too, so it'll be built into the next major release of Facter 1.7.0.

Example usage

Before we get started with the examples we need to create the /etc/facter/facts.d directory:
$ mkdir /etc/facter/facts.d
Next we need to ensure the node uses the stdlib class:
node 'pmotw.puppetlabs.com' {
  include stdlib
}
Then we use pluginsync to sync the facter-dot-d fact to the agent:
$ puppet agent -t --pluginsync
…
info: Loading downloaded plugin /var/lib/puppet/lib/facter/facter_dot_d.rb
…
info: Loading facts in facter_dot_d
info: Loading facts in root_home
info: Loading facts in facter_dot_d
info: Loading facts in root_home
info: Caching catalog for pmotw.puppetlabs.com
info: Applying configuration version '1331932291'
notice: Finished catalog run in 0.07 seconds

Adding a custom fact using JSON

In order to use JSON files as a source for facter-dot-d you need to ensure the json gem is installed on your system. On my test system, Debian 6, the json gem is not installed by default:
$ gem list

*** LOCAL GEMS ***

puppet-lint (0.1.12)
puppet-module (0.3.4)
We can use the gem command to install it:
$ gem install json
Building native extensions.  This could take a while...
Successfully installed json-1.6.5
1 gem installed
Installing ri documentation for json-1.6.5...
Installing RDoc documentation for json-1.6.5...

$ gem list

*** LOCAL GEMS ***

json (1.6.5)
puppet-lint (0.1.12)
puppet-module (0.3.4)
Now that we have the json gem installed we can simply drop our JSON file into the /etc/facter/facts.d directory:
$ cat /etc/facter/facts.d/datacenter.json
{
  "location": "Portland",
  "cluster": "Web"
}
On the next use of Facter, the datacenter.json file will be processed by facter-dot-d adding custom Facts for the location and cluster keys as a result. It should also be noted that since facter-dot-d was distributed via pluginsync, we must use the --puppet flag. The following will not work:
$ facter location
Using the --puppet flag produces the desired results:
$ facter --puppet location cluster
cluster => web
location => portland
We can achieve the same results using a YAML or TXT file:
$ cat /etc/facter/facts.d/datacenter.yaml
---
location: portland
cluster: web

$ facter -p location cluster
cluster => web
location => portland
$ cat /etc/facter/facts.d/datacenter.txt
location=portland
cluster=web

$ facter -p location cluster
cluster => web
location => portland

Staying out of trouble

Be sure to use either JSON, YAML, or TXT files consistently. While the following will work:
$ cat /etc/facter/facts.d/datacenter.yaml
---
location: portland
cluster: web

$ cat /etc/facter/facts.d/web.json
{
  "api_version": 2.1,
  "web_port": 8080
}

$ facter -p location cluster api_version web_port
api_version => 2.1
cluster => web
location => portland
web_port => 8080
The follow will lead to unexpected behavior:
$ cat /etc/facter/facts.d/datacenter.json
{
  "location": "atlanta",
  "cluster": "database"
}

$ cat /etc/facter/facts.d/datacenter.yaml
---
location: portland
cluster: web
With both datacenter.json and datacenter.yaml specifying the same data, there is no reliable way to tell which values cluster and location will be set to.
$ facter -p location cluster
cluster => database
location => atlanta

Silent failures

Invalid JSON, YAML, and TXT files will silently fail:
$ cat /etc/facter/facts.d/web.json
{
  "api_version": 2.1,
  "web_port": 8080,
}

$ facter -p web_port
Notice we do not get the expected output of 8080 here. We can get more insight into what’s going on by using the --debug flag:
$ facter -p --debug
...
Parsing /etc/facter/facts.d/datacenter.json using json_parser
Failed to handle /etc/facter/facts.d/datacenter.json as json facts: JSON::ParserError: 618: unexpected token at '{

  "api_version": 2.1,

  "web_port": 8080,

}

'
Though not clear from this error message, datacenter.json is not a valid JSON file. Notice the extra ',' after 8080. Fixing this yields the expected result:
$ facter -p web_port
web_port => 8080

Conclusion

The Facter Ruby API is the standard way of creating custom facts, which sets the bar pretty high for creating new facts. facter-dot-d lowers the bar significantly by adding the ability to use flat files as well as scripts in any language to produce custom facts. This is a huge win for people just getting started and for people who have existing data they would like to use as Facts. Join us next week as we wrap up our coverage of the puppetlabs/stdlib module. We’ll be taking a look at the remaining functionality by exploring some data functions that allow you to do things like load external data from YAML files and merge parameter values inside of Puppet manifests.

Additional Resources

Puppet sites use proprietary and third-party cookies. By using our sites, you agree to our cookie policy.