Open source Puppet documentation

Containment enables you to control where and when specific parts of your Puppet code are executed. Containment is the relationship that resources have to classes and defined types.

Containment

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 will form the same relationship with every resource inside the container.

Consider this example:
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.

This feature allows you to notify and subscribe to classes and defined resource types as though they were a single resource.

Containing resources

Resources of both native and defined resource types are automatically contained by the class or defined type in which they are declared.

Containing classes

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.

Unlike resources, Puppet does not automatically contain classes when they are declared inside another class. This is because classes can be declared in several places using include and similar functions. Most of these places shouldn’t contain the class, and trying to contain it everywhere would cause huge problems. Instead, you must manually contain any classes that need to be contained.

The contain function

When a class should be contained, and if it isn’t already declared, use the contain function to declare the class with include-like behavior, causing it to be contained by the surrounding class.

In this NTP module example, the service configuration is moved out into its own class:

class ntp {
  file { '/etc/ntp.conf':
    ...
    require => Package['ntp'],
    notify  => Class['ntp::service'],
  }
  contain ntp::service
  package { 'ntp':
    ...
  }
}

include ntp
exec { '/usr/local/bin/update_custom_timestamps.sh':
  require => Class['ntp'],
}

This will ensure that the exec will happen after all the resources in both class ntp and class ntp::service. If ntp::service had been declared with include instead of contain, the exec would happen after the file and the package, but wouldn’t necessarily happen after the service.

To contain classes that are declared with the resource-like declaration syntax, use the contain function after declaring the class:

class ntp {
  # ...
  class { 'ntp::service':
    enable => true,
  }
  contain 'ntp::service'
  # ...
}

Anchor pattern containment (for compatibility with Puppet version 3.4.0 and earlier)

Versions prior to Puppet 3.4.0 and Puppet Enterprise 3.2 do not ship with the contain function. If your code needs to support these versions, it should contain classes with the anchor pattern.

Note: To use the anchor pattern, install the puppetlabs/stdlib module. which includes the dummy anchor resource type.
To use the anchor pattern:
  • The containing class must include two uniquely-named anchor resources, which are resources that don’t have any effect on the target system, and only exist to form relationships with.

  • Any contained classes must have relationships ensuring they happen after one anchor and before the other.

In this NTP module example, the service configuration is moved out into its own class:

class ntp {
  file { '/etc/ntp.conf':
    ...
    require => Package['ntp'],
    notify  => Class['ntp::service'],
  }
  include ntp::service

  # roughly equivalent to "contain ntp::service":
  anchor { 'ntp_first': } -> Class['ntp::service'] -> anchor { 'ntp_last': }

  package { 'ntp':
    ...
  }
}

include ntp
exec { '/usr/local/bin/update_custom_timestamps.sh':
  require => Class['ntp'],
}

In this case, the ntp::service class will behave like it’s contained by the ntp class. Resources like the timestamp exec can form relationships with the ntp class and be assured that no relevant resources will float out of order.

Back to top