The Puppet RAL: Types, Parameters, and Properties

This is the second post on a series on the Puppet RAL.

The RAL needs to be able to model any sort of thing you would want to manage. Because of this it’s broken into a number of different classes to be able to model any sort of resource you could need. This blog post is going to discuss the puppet Type, Parameter, and Property classes and what they do.

Types

A Puppet Type represents a category of things you can manage. Things like users, services, files, and so forth are examples of common types. Types provide the structure for managing things and define what you can do with them.

The Type
   |
   v
service { 'my-service':
  ensure  => running,
  restart => '/usr/local/bin/restart-my-service',
}

An important detail is that Types are (mostly) abstract, and only define what instances of that Type can do. This is one of the killer features of the RAL for a number of reasons.

First off, it’s trivial to extend the core behavior of Puppet. If you want to add support for a new platform it’s not necessary to add extensions to core Puppet; you can implement a provider for that type and use it immediately.

Secondly, since Types are abstract it provides a clear distinction between what you can do with an instance of that Type, and how it’s implemented under the hood.

Types themselves define a few behaviors like autorequire and provider_features, but the actual definitions of what a Type can do are offloaded to Parameters and Properties.

Parameters and Properties

The actual nuts and bolts of defining a Type are handled by Parameters and Properties.

service { 'my-service':

  Parameters & Properties ---------------------------+
                                                     |
  ensure  => running,                             <--+
  restart => '/usr/local/bin/restart-my-service', <--+
}

Collectively, Parameters and Properties determine what a Type actually is and how instances of that Type actually behave. For instance, the ensure property on a service determines if the service should be running or if it should be stopped, and the restart parameter controls how the service will be restarted.

Parameters vs Properties

I’ve been throwing around the terms ‘Parameter’ and ‘Property’, but haven’t defined what they mean or what the difference between the two are. After all, in the example service above one’s a parameter while the other is a property - what’s the difference? To answer that, we need to look at what both of those values actually mean.

In general, a Parameter is just a ‘thing’ that you can adjust on a Type. It can be set, it can be validated, it can be munged and unmunged, and things like that. However, Parameters don’t have state, which is where Properties come in.

A Property is a subclass of a Parameter with one major difference - Properties have state that exists on the system. You can still get and set a Property value, but you can also see what that set value compares with the current system state. You can see what the Property value is, what the current system is, and if the Property value is in sync with the current system state.

An easy way of understanding this is that changing a Property will always affect the system state, while changing a Parameter will not. Going back to the above service example, the ensure field is a property while the restart field is a parameter. If you change the ensure value then that service will be started or stopped. If you change the restart field, absolutely nothing will happen by itself - you can change that value all day long and it won’t change anything. The restart parameter only comes into play when something else necessitates a restart of the service.

service { 'my-service':

  Changing this will change the system state
          |
          v
  ensure  => running,

  Changing this only matters _when_ the system state is changed.
          |
          v
  restart => '/usr/local/bin/restart-my-service',
}

Addendum

Thanks to all of the wonderful people who continue to proofread my blog posts and catch innumerable errors.

Abstract types

I lied. While Types should be abstract, the reality is that you can cram a lot of behavior into Types, and a number of existing Types do just that. This is bad and completely breaks the abstraction behaviors that the RAL provides.