
Putting it all together
Reading this far, you might have gotten the impression that this chapter is a rather odd mix of topics. While types and providers do belong closely together, the whole introduction to Facter might seem out of place in this context. This is deceptive, however; facts do play a vital role in the type/provider structure. They are essential for Puppet to make good choices among providers.
Let's look at an example from the Extending Facter with custom facts section once more. It was about fstab
entries and the difference of Solaris, which uses /etc/vfstab
instead of /etc/fstab
. That section suggested a manifest that adapts according to a fact value. As you have learned, Puppet has a resource type to manage fstab
content: the mount
type. However, for the small deviation of a different file path, there is no dedicated mount
provider for Solaris. There is actually just one provider for all platforms, but on Solaris, it behaves differently. It does this by resolving Facter's osfamily
value. The following code example was adapted from the actual provider code:
case Facter.value(:osfamily) when"Solaris" fstab = "/etc/vfstab" else fstab = "/etc/fstab" end
In other cases, Puppet should use thoroughly different providers on different platforms, though. Package management is a classic example. On a Red Hat-like platform, you will want Puppet to use the yum
provider in virtually all cases. It can be sensible to use rpm
, and even apt
might be available. However, if you tell Puppet to make sure a package is installed, you expect it to install it using yum
, if necessary.
This is obviously a common theme. Certain management tasks need to be performed in different environments, with very different toolchains. In such cases, it is quite clear which provider would be best suited. To make this happen, a provider can declare itself the default if a condition is met. In the case of yum
, it is the following:
defaultfor :operatingsystem => [:fedora, :centos, :redhat]
The conditions are based around fact values. If the operatingsystem
value for a given agent is among the listed, yum
will consider itself the default package provider.
In addition to marking themselves as being default, there is more filtering of providers that relies on fact values. Providers can also confine themselves to certain combinations of values. For example, the yum
alternative, zypper
, confines itself to SUSE Linux distributions:
confine :operatingsystem => [:suse, :sles, :sled, :opensuse]
This provider method works just like the confine
method in Facter, which was discussed earlier in this chapter. The provider will not even be seen as valid if the respective facts on the agent machine have none of the white-listed values.
Note
If you find yourself looking through code for some core providers, you will notice confinement (and even declaring default providers) on feature values, although there is no Facter fact of that name. These features are not related to provider features either. They are from another layer of introspection similar to Facter, but hardcoded into the Puppet agent. These agent features are a number of flags that identify some system properties that need not be made available to manifests in the form of facts. For example, the posix
provider for the exec type becomes the default in the presence of the corresponding feature:
defaultfor :feature => :posix
You will find that some providers forgo the confine
method altogether, as it is not mandatory for correct agent operation. Puppet will also identify unsuitable providers when looking for their necessary operating system commands. For example, the pw
provider for certain BSD flavors does not bother with a confine
statement. It only declares its one required command:
commands :pw => "pw"
Agents that find no pw
binary in their search path will not try and use this provider at all.
This concludes the little tour of the inner workings of types and providers with the help of Facter. For a complete example of building a provider for a type, and using the internal tools that you have now learned about, you can refer to Chapter 5, Extending Your Puppet Infrastructure with Modules.