About Faces, until we go in the right direction

This follows on from my previous post on Faces and continues the explanation of what Faces are and how they work. Last time I talked about Facades, the generic mechanisms for accessing Faces. The fundamental components here are:

  1. Facades – the ways you can interact with your Faces and Actions.
  2. Faces – these represent a coherent subsystem, or modelled object or entity.
  3. Actions – these represent things you can do to the modelled object or entity.
  4. Options – these modify the way that actions work

Faces: a subsystem, or a modelled object or entity

Each Face represents one coherent thing. Mostly, these are objects or entities that we manage—catalogs, nodes, certificates, and other parts of the coherent data model that Puppet maintains about the nodes on your network. Many of those wrap our existing indirection and terminus system, including:

  • Catalog: provide access to the catalog compiler and cache.
  • Certificate: provide access to the certificate authority.
  • Facts: provide access to the fact discovery or cache systems.
  • Status: provide access to the Puppet status terminus.

Generally, we don't expect too many people (you, or us) to write Faces that get back into core: something like that usually means a fairly fundamental addition or modification to the code data model in Puppet. Since that data model moves relatively slowly, Faces will also be created and destroyed relatively slowly. Most of the growth around Faces will come from two areas: one is a collection of "functional" Faces rather than data model Faces. These will replaces the current set of applications, either spreading their actions over the appropriate Faces that form the underlying model, or by adding new Faces focused on their actions. We also expect to see a lot of people use Faces to help integrate their own model and function into Puppet: being able to say puppet helpdesk report ... in order to file a bug report on a system is a great extension, and something that you can naturally integrate into your own platform with Faces.

Extending and Creating Faces

There are two ways that you will interact with Faces: extending an existing Face by adding a new Action, or creating a new Face from scratch. I will cover adding Actions in my next post; for now, to create a new Face you need to start with an idea of what you are modeling. In this case we are going to create the outline of a backup Face, to demonstrate how you might go ahead and build something like that in your own system. Our overall goal is to produce a tool that will create a backup copy of our Puppet modules, ready for archiving. This is NOT going to be a complete backup solution, but it will be a complete example of building a Face and action. In this episode we start by creating the stub for the Face and getting it to the point that it can be used: We start by creating the Face itself, in lib/puppet/face/backup.rb. This example will only contain documentation, no actions; we will add those next round.

# encoding: utf-8
# Dear Ruby and Emacs, this is -*- ruby -*- in lib/puppet/face/backup.rb

# We need to load the Face support library...
require 'puppet/face'

# ...and declare our Face. We have to give it a version; I chose version
# 0.1.0 because Face is still under heavy development. See http://semver.org/
# for the details of why that is an appropriate version number.
Puppet::Face.define(:backup, '0.1.0') do
  # First, the annoying bits that make our lawyers happy. You should probably
  # include this information in your Faces as well, but our defaults are
  # restrictive so your code is not redistributable unless you make an effort
  # to achieve the same.
  #
  # We just include the literal string directly in the output; use whatever
  # you want here. It isn't like the computer cares much.
  license "Apache 2"

  # Copyright takes one or more authors as a string, then a sensible set of
  # years. It should pretty much do what you expect for any input (or fail
  # with a clear error message. ;)
  copyright "Puppet Labs", 2011

  # You can call out the authors of your Face, too, and Actions. They will be
  # displayed in the help output, which can help folks track you down when
  # they need to talk to you about something.
  author "Daniel Pittman <daniel@puppetlabs.com>"
  # You can add more authors with more 'author' statements:
  # author "U.N.Owen <anon@example.com>"


  ########################################################################
  # Now, on to more interesting things. We should document what our Face
  # is all about so that 'puppet help backup' does the right thing.
  #
  # The summary is a single, brief, line describing what the Face is for.
  # It gets used when displaying lists of Faces, and similar places.
  summary "Manage backups of the Puppet installation"

  # We also give a description; this is a block of text used to give broader
  # context to the Face, like the description in a manual page.
  #
  # We automatically trim away whitespace used for gross indentation in the
  # description, but preserve semantic indentation. So, you don't need to
  # write your text aligned hard-left to get it output nicely.
  #
  # Note that we don't describe options or actions here: they are documented
  # separately, and that is automatically extracted from the appropriate files
  # when the time comes to display it.
  description <<-DESCRIPTION
    The 'backup' Face provides tools to help manage backups of the Puppet
    installation; it focuses on making it easier for the backup system to
    work reliably with our deployment without needing to bake in specific
    knowledge of Puppet to our backup system – just fire this up instead.
  DESCRIPTION

  # We can give some examples; these make it easier for the user to understand
  # how to use the Face. They won't work yet, but they should eventually...
  examples <<-EXAMPLES
    # Just create a new backup under the current directory:
    ] puppet backup

    # The same thing, explicitly naming that we are creating a backup:
    ] puppet backup create

    # Name the target directory explicitly:
    ] puppet backup create --target=saved_copy
  EXAMPLES

  # Also, some notes; these are displayed under a notes header, and are things
  # that the user might care about, but not important enough to call out in
  # the description.
  notes <<-NOTES
    * This is **NOT** a complete backup solution.
    * NO WARRANTY IS PROVIDED OR IMPLIED!
  NOTES
end

Wiring it all up

Once you have written your Face you are almost there. Unfortunately, you need to wire it up to the Puppet::Application system so that it can be invoked from the command line. We have a plan (#6753) to clear this up, but for the moment we need to wire things together by creating lib/puppet/application/backup.rb to match our Face:

# Dear Ruby and Emacs, this is -*- ruby -*- in lib/puppet/application/backup.rb
require 'puppet/application/face_base'

# Create our placeholder class. The naming convention is simple, but not
# entirely clear: take the name of the Face as a symbol, convert in all to
# lower-case except the first letter, and name your class that way.
#
# For example:
# :backup    => Backup
# :two_words => Two_words
#
# ...and, yes, we are away that this isn't really the Ruby way. ;)
class Puppet::Application::Backup < Puppet::Application::FaceBase
  # This class is deliberately left empty. It exists as a placeholder to
  # signal that this application is implemented as a Face, nothing more.
end

Next Time...

Stay tuned for the next episode, where we add an Action to the Backup Face, and illuminate the more interesting parts of the system. The Faces blog series thusfar:

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