Language: Containment of resources

Containment

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.

This effectively 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.

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'],
}

In this example, Exec['/usr/local/bin/update_custom_timestamps.sh'] would happen after every resource in the ntp class, including the package, the file, and the service.

This feature also 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 very useful, especially in larger modules where you want to improve code readability by moving chunks of implementation into separate files.

However, 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 via 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

Use the contain function when a class should be contained. The contain function will declare the class with include-like behavior if it isn’t already declared, and will cause it to be contained by the surrounding class.

In an example NTP module where 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 ≤ 3.4.0)

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, the puppetlabs/stdlib module must be installed. This module includes the dummy anchor resource type.

To use the anchor pattern:

  • The containing class must include two uniquely-named anchor resources. (Anchor resources 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 an example NTP module where 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.