
Substantiating the model with providers
At the start of this chapter, you learned about Facter and how it works like a layer of abstraction over the supported platforms. This unified information base is one of Puppet's most important means to achieve its goal of operating system independence. Another one is the DSL, of course. Finally, Puppet also needs a method to transparently adapt its behavior to the respective platform on which each agent runs.
In other words, depending on the characteristics of the computing environment, the agent needs to switch between different implementations for its resources. This is not unlike object-oriented programming—the type system provides a unified interface, not unlike an abstract base class. The programmer need not worry what specific class is being referenced, as long as it correctly implements all the required methods. In this analogy, Puppet's providers are the concrete classes that implement the abstract interface.
For a practical example, look at package management. Different flavors of UNIX-like operating systems have their own implementation. The most prevalent Puppet platforms use apt
and yum
, respectively, but can (and sometimes must) also manage their packages through dpkg
and rpm
. Other platforms use tools such as emerge
, zypper
, fink
, and a slew of other things. There are even packages that exist apart from the operating system software base, handled through gem
, pip
, and other language-specific package management tools. For each of these management tools, there is a provider for the package type.
Many of these tools allow the same set of operations—installing and uninstalling a package, and updating a package to a specific version. The latter is not universally possible though. For example, dpkg
can only ever install the local package that is specified on the command line, with no other version to choose.
There are also some distinct features that are unique to specific tools, or are supported by only a few. Some management systems can hold packages at specific versions. Some use different states for uninstalled versus purged packages. Some have a notion of virtual packages. The list of examples can go on and on.
Because of this potential diversity (which is not limited to package management systems), Puppet providers can opt for features. The set of features is resource-type specific. All providers for a type can support one or more of the same group of features. For the package
type, there are features such as versionable
, purgeable
, holdable
, and so forth. You can set ensure => purged
on any package resource like so:
package { 'haproxy': ensure => 'purged' }
However, if you are managing the HAproxy
package through rpm
, Puppet will fail to make any sense of it because rpm
has no notion of a purged
state, and therefore, the purgeable
feature is missing from the rpm
provider. Trying to use an unsupported feature will usually produce an error message. Some attributes, such as install_options
, might just result in a warning by Puppet instead.
Note
The official documentation on the Puppet Labs website holds a complete list of the core resource types and all their built-in providers, along with the respective feature matrices. It is very easy to find suitable providers and their capabilities. The documentation can be found at https://docs.puppetlabs.com/references/latest/type.html.
Resource types with generic providers
There are some resource types that use no providers, but they are rare among the core types. Most of the interesting management tasks that Puppet makes easy just work differently among operating systems, and providers enable this in a most elegant fashion.
Even for straightforward tasks that are the same on all platforms, there might be a provider. For example, there is a host
type to manage entries in the /etc/hosts
file. Its syntax is universal, so the code can technically just be implemented in the type. However, there are actual abstract base classes for certain kinds of providers in the Puppet code base. One of them makes it very easy to build providers that edit files, if those files consist of single-line records with ordered fields. Therefore, it makes sense to implement a provider for the host
type and base it on this provider class.
Summarizing types and providers
Puppet's resource types and their providers work together to form a solid abstraction layer over software configuration details. The type system is an extendable basis for Puppet's powerful DSL. It forms an elaborate interface for the polymorphous provider layer.
The providers flexibly implement the actual management actions that Puppet is supposed to perform. They map the necessary synchronization steps to commands and system interactions. Many providers cannot satisfy every nuance that the resource type models. The feature system takes care of these disparities in a transparent fashion.