homeblogclass containment puppet

Class Containment in Puppet

Class containment is an advanced topic in Puppet, and something that new users often find confusing. In the past, elaborate systems of anchor resources needed to be created to order classes effectively, and this sometimes led to difficult-to-diagnose dependency cycles.

The purpose of containment, in general, is to let you control where and when certain parts of your Puppet code are executed. Containment offers granular control by operating on the level of individual resources, and it also offers tremendous power by operating at the class level.

But let’s step back for a minute.

What Is Containment?

Containment is the policy of Puppet Labs to prevent the spread of “ssh-and-a-for-loop” in IT shops throughout the world by rogue sysadmins.

Just kidding.

Containment, put simply, is the relationships that resources have to the classes and defined types that "contain" them.

As per the docs:

Classes and defined type instances contain the resources they declare. Any contained resources will not be applied before the container is begun, and will be finished before the container is finished.

So for example, if we run puppet apply with the following manifest:

we receive the following output:

You'll notice that the begin notify resource fires before all the notify resources in the class myklass, just as the end notify resource is applied after all of the notify resources in the class myklass. This is because the relationships that the begin and end notify resources have with the class myklass apply to the resources within myklass as well. This is "containment".

If you're using a version of Puppet Enterprise prior to 3.2.0, or Open Source Puppet earlier than 3.4.0, please refer to the section lower down in this post with the subhead, "Class Containment Prior to Puppet Enterprise 3.2.0 (or Open Source Puppet 3.4.0)."

What is Class Containment?

Where this gets more complicated, and often leads to confusion is with class declaration.

Given the following example, what would you expect to happen?

It looks like the notify { 'foobarbaz': } resource will be applied before notify { 'bazbarfoo': } due to the class ordering, but this turns out to not be the case:

This is because classes never contain the classes they include. So even though classa and classb are ordered, the classes they include are not.

It can be a little confusing, because the words "contain" and "include" are so similar, but they have different meanings in Puppet syntax.

Why is this the case? Well, for one thing, classes can be included multiple times. Imagine for example the following scenario:

If classes were contained each time they were included, Puppet would have no way of knowing what order to apply the resources within the ntp class, in the above example. Therefore, in order to allow classes to be included an unlimited number of times per node, classes never contain the classes they include. Because classes don't contain other classes, Puppet will happily apply the catalog generated from the manifest above, including the ntp class.

When Should I Care About Class Containment?

Whenever you care about forming ordering relationships against a module that contains subclasses, you likely care about class containment. For example, if you are building an Apache module that you plan to reuse multiple times, and it contains a top level apache class that can be declared directly, as well as several subclasses (think apache::package, apache::file, apache::service, but it can often be much more complex than that). If you want those subclasses to be affected by ordering against the main class, you'll need to deal with class containment.

This comes into play especially when you're writing modules you'll want to re-use, like a generic MySQL module. For example, if you look at the puppetlabs/mysql module on GitHub, you'll see that the class that manages the installation of the server components uses the "anchor pattern" (more on that below), to contain its subclasses. This allows you to write other modules that can reliably depend on MySQL being configured in a certain order during the Puppet run, either before or after other resources and classes.

Dealing with Class Containment

Let's imagine that we're spinning up a web stack on a single node, that includes a database like MySQL and a web server like Apache.

We've written manifests to manage all this, but it's critical in our infrastructure that the database be operational before the web server is brought online, so that the web application can actually function.

To that end, we've written the following puppet code:

In the above example, we've not successfully ordered the MySQL and Apache classes, even though we've ordered the classes that include them.

There are two ways to achieve succesful ordering, depending on which version of Puppet you're running.

Class Containment in Puppet Enterprise 3.2.0 (Puppet 3.4.0) and later

If you're using Puppet Enterprise 3.2.0 (or POSS 3.4.0) or later, you're in luck! As of 3.2.0, we've implemented the contain function.

If we replaced include mysql and include apache, with contain mysql and contain apache, the ordering in the roles::ecommerce_app class from our previous example would extend to the resources in the "contained" classes:

It's important to be extremely careful with the contain function. For example, the following code:

Will generate a catalog successfully:

But once modified to include ordering:

Catalog compilation will fail:

Because we've "contained" class a and class b in multiple locations, and then set order between those locations (classes) containing them, Puppet isn't sure what we're trying to do, and we've got a dependency cycle.

Class Containment Prior to Puppet Enterprise 3.2.0 (or POSS 3.4.0)

If you're using a version of Puppet Enterprise prior to 3.2.0 (or POSS 3.4.0), you'll need to use the anchor pattern.

Basically this is the same as contain, but a bit more verbose, in that it requires you to essentially "bookend" classes you'd like to contain using dummy anchor resources.

That's about it for class containment, including both the anchor pattern and the contain function.

Zee Alexander is a support engineer at Puppet Labs.

Learn More