Writing Custom Puppet Facts

Hands-On Lab

 

Photo of Elle Krout

Elle Krout

Content Team Lead in Content

Length

00:30:00

Difficulty

Intermediate

There are multiple ways we can provide our own facts to our Puppet infrastructure, including plain-text eternal facts files, executable scripts, and via writing Ruby fact plugins. Custom facts are plugins that leverage Ruby to either provide static fact data or evaluate and command as well as provide the output for the value of the fact. These custom facts can be written so they are as simple as providing us a key-value pair or as complicated as some of the structured data sets we see when we query for our os-based facts. In this lab, we'll create a custom aggregate fact that provides us with data about our host's web server (should it exist), learn how to create structured facts, and use various coding techniques to ensure our facts work on all systems.

What are Hands-On Labs?

Hands-On Labs are scenario-based learning environments where learners can practice without consequences. Don't compromise a system or waste money on expensive downloads. Practice real-world skills without the real-world risk, no assembly required.

Writing Custom Puppet Facts

Introduction

In this lab, we'll create a custom aggregate fact that provides us with data about our host's web server (should it exist), learn how to create structured facts, and use various coding techniques to ensure our facts work on all systems.

Solution

  1. Begin by logging in to the lab server using the credentials provided on the hands-on lab page:

    ssh cloud_user@PUBLIC_IP_ADDRESS

Set the Web Server Fact

  1. Move into the nginx module directory:

    cd /etc/puppetlabs/code/environments/production/modules/nginx/

  2. Create the plugin library location for Facter:

    sudo mkdir -p lib/facter

  3. Create and open a file called web.rb in the plugin location:

    sudo vim lib/facter/web.rb

  4. At the most basic level, if we wanted to write a single, non-structured fact that output our web server information, we could write something like:

    Facter.add(:web) do setcode do 'Nginx' end end

  5. Define the overall web fact as an aggregate:

    Facter.add(:web, :type => :aggregate) do

  6. Replace our setcode statement with chunk:

    Facter.add(:web, :type => :aggregate) do chunk(:server) do

  7. We now need to update the structure of our output, so it resembles the JSON output we expected when we query for facts. To this, let's create a variable, web, then add our Nginx value as a structured key-value pair:

    Facter.add(:web, :type => :aggregate) do chunk(:server) do web = {:server => 'Nginx'} web end end

    Save and exit.

  8. Test out our fact:

    sudo puppet agent -t sudo facter -p web sudo facter -p web.server

Set the Version Fact

  1. Back in the lib/facter/web.rb file, create a second chunk line, this one called version; include a line at the end calling the web variable:

     chunk(:version) do
       web
     end
  2. We're going to check our version by using a slightly edited version of the nginx -v command — which means we need nginx for this fact to work. Let's add an if statement checking for nginx:

     chunk(:version) do
       if Facter::Core::Execution.which('nginx') != nil
       end
       web
     end
  3. Finally, we want to execute the appropriate command and have the output sent to a structured line mapped to web. Because our command is on the long side, you may want to create a variable for its output:

     chunk(:version) do
       if Facter::Core::Execution.which('nginx') != nil
         version = Facter::Core::Execution.execute('nginx -v 2>&1 | grep -Po [0-9]+.[0-9]+.[0-9]+')
         web = {:version => version}
       end
       web
     end
  4. If desired, save and test your code thus far.

Set the Fact for the Amount of Running Processes

Mimic the same structures as the previous chunks to create the stanza that provides the amount of running processes:

  chunk(:processes) do
   processes = Facter::Core::Execution.execute('pgrep nginx | wc -l')
   web = {:processes => processes }
   web
  end

The full file should look like:

Facter.add(:web, :type => :aggregate) do
  chunk(:server) do
   web = {:server => 'Nginx'}
   web
  end

  chunk(:version) do
   if Facter::Core::Execution.which('nginx') != nil
     version = Facter::Core::Execution.execute('nginx -v 2>&1 | grep -Po [0-9]+.[0-9]+.[0-9]+')
     web = {:version => version}
   end
   web
  end

  chunk(:processes) do
   processes = Facter::Core::Execution.execute('pgrep nginx | wc -l')
   web = {:processes => processes }
   web
  end
end

Test the New Facts

Test that each fact works, as well as the overall web fact. Note that the second server is provided so you can test on a node with Nginx, as well as one without it (remember, our Puppet master does not have Nginx installed):

sudo puppet agent -t
sudo facter -p web
sudo facter -p web.server
sudo facter -p web.version
sudo facter -p web.processes

Conclusion

Congratulations — you've completed this hands-on lab!