Containment
Containment is what controls the order in which the various parts of your Puppet code are executed. Containment is the relationship that resources have to classes and defined types, determining what has to happen before other things can happen.
Classes and defined type instances contain the resources they declare. Contained resources are not applied before the container begins, and they finish before the container finishes.
This means that if any resource or class forms a relationship with the container, it forms the same relationship with every resource inside the container.
class ntp {
file { '/etc/ntp.conf':
...
require => Package['ntp'],
notify => Service['ntp'],
}
service { 'ntp':
...
}
package { 'ntp':
...
}
}
include ntp
exec {'/usr/local/bin/update_custom_timestamps.sh':
require => Class['ntp'],
}
Here,
exec['/usr/local/bin/update_custom_timestamps.sh']
would
happen after every resource in the ntp
class, including the
package, file, and service.Containment allows you to notify and subscribe to classes and defined resource types as though they were a single resource.
Containment of resources
Resources of both native and defined resource types are automatically contained by the class or defined type in which they are declared.
Containment of classes
Unlike with resources, Puppet does not automatically
contain classes when they are declared inside another class (by using the
include
function or resource-like declaration). But in certain situations,
having classes contain other classes can be useful, especially in larger modules where you want
to improve code readability by moving chunks of implementation into separate files.
You can declare a class in any number of places in the code, allowing classes to announce their needs without worrying about whether other code also needs the same classes at the same time. Puppet includes the declared class only one time, regardless of how many times it's declared (that is, the include function is idempotent). Usually, this is fine, and code shouldn't attempt to strictly contain the class. However, there are ways to explicitly set more strict containment relationships of contained classes when it is called for.
-
include
: When you need to declare a class and nothing in it is required for the enforcement of the current class you're working on, use theinclude
function. It ensures that the named class is included. It sets no ordering relationships. Useinclude
as your default choice for declaring classes. Use the other functions only if they meet specific criteria. -
require
: When resources from another class should be enforced before the current class you're working on can be enforced properly, use therequire
function to declare classes. It ensures that the named class is included. It sets relationships so that everything in the named class is enforced before the current class. -
contain
: When you are writing a class in which users should be able to set relationships, use thecontain
function to declare classes. It ensures that the named class is included. It sets relationships so that any relationships specified on the current class also apply to the class you're containing.
The require
function
The require
function is useful when the class you're writing needs another
class to be successfully enforced before it can be enforced properly.
chocolatey
. Both classes require that Chocolatey be properly managed
before they can each proceed. Instead of using include
, which won't ensure
Chocolatey's resources are managed before it installs each app, use
require
.class myapp::install {
# works just like include, but also creates a relationship
# Class['chocolatey'] -> Class['myapp::install']
require chocolatey
package { 'myapp':
ensure => present,
}
}
class my_other_app::install {
require chocolatey
package { 'my_other_app':
ensure => present,
}
}
The contain
function
Use the contain
function to declare that a class is
contained. This is useful for when you're writing a class in which other users should be
able to express relationships. Any classes contained in your class will have containment
relationships with any other classes that declare your class. The contain
function uses include-like behavior, containing a class within a
surrounding class.
myapp::install
), creating its configuration file
(myapp::config
), and managing its service
(myapp::service
). Using the contain
function explicitly
tells Puppet that the internal classes should be contained
within the class that declares them. The contain
function works like
include
, but also adds class relationships that ensure that relationships
made on the parent class also propagate inside, just like they do with
resources.class myapp {
# Using the contain function ensures that relationships on myapp also apply to these classes
contain myapp::install
contain myapp::config
contain myapp::service
Class['myapp::install'] -> Class['myapp::config'] ~> Class['myapp::service']
}
Although it may be tempting to use contain
everywhere, it's
better to use include
unless there's an explicit reason why it won't
work.