The Puppetlabs docs state that in order for one class to require another class you should use the relationship chaining syntax and declare both classes in your outer nodes.
I have a repo class which creates the yum repo definition that many packages in each modole depend on. In each module I have a Class['repo'] -> Class['modulename'] statement and both classes are declared in the node. However, when puppet runs it doesn't always execute the repo class before the module class as expected. Why not? Example below (puppet 2.6.16):
EDIT: It appears there are 3 basic solutions to this problem.
So which of these approaches is best, considering Puppet v3 and the desire to keep refactoring to a minimum going forward'?
Manifest puppettest.pp
:
class { 'repo': }
class { 'maradns': }
class repo {
class { 'repo::custom': }
}
class repo::custom {
yumrepo {'custom':
enabled => 1,
gpgcheck => 0,
descr => "Local respository - ${::architecture}",
baseurl => 'http://repo.nike.local/CentOS/\$releasever/\$basearch';
}
}
class maradns {
Class['repo'] -> Class['maradns::install']
Class['maradns::install'] -> Class['maradns::config']
Class['maradns::config'] ~> Class['maradns::service']
class { 'maradns::install': }
class { 'maradns::config': }
class { 'maradns::service': }
}
class maradns::install {
package { 'maradns':
ensure => present,
}
}
class maradns::config {
file { 'mararc':
ensure => present,
path => '/etc/mararc',
mode => '0644',
owner => root,
group => root,
}
}
class maradns::service {
service { 'maradns':
ensure => running,
enable => true,
hasrestart => true,
}
}
Output:
puppet apply puppettest.pp
err: /Stage[main]/Maradns::Install/Package[maradns]/ensure: change from absent to present failed: Execution of '/usr/bin/yum -d 0 -e 0 -y install maradns' returned 1: Error: Nothing to do
notice: /Stage[main]/Maradns::Config/File[mararc]: Dependency Package[maradns] has failures: true
warning: /Stage[main]/Maradns::Config/File[mararc]: Skipping because of failed dependencies
notice: /Stage[main]/Maradns::Service/Service[maradns]: Dependency Package[maradns] has failures: true
warning: /Stage[main]/Maradns::Service/Service[maradns]: Skipping because of failed dependencies
notice: /Stage[main]/Repo::Custom/Yumrepo[custom]/descr: descr changed '' to 'Local respository - x86_64'
notice: /Stage[main]/Repo::Custom/Yumrepo[custom]/baseurl: baseurl changed '' to 'http://repo.test.com/CentOS/\$releasever/\$basearch'
notice: /Stage[main]/Repo::Custom/Yumrepo[custom]/enabled: enabled changed '' to '1'
notice: /Stage[main]/Repo::Custom/Yumrepo[custom]/gpgcheck: gpgcheck changed '' to '0'
notice: Finished catalog run in 2.15 seconds
In Puppet, classes are code blocks that can be called in a code elsewhere. Using classes allows you reuse Puppet code, and can make reading manifests easier.
Each parameter can be preceeded by an optional data type. If you include one, Puppet will check the parameter's value at runtime to make sure that it has the right data type, and raise an error if the value is illegal. If no data type is provided, the parameter will accept values of any data type.
You can declare classes in node definitions, at top scope in the site manifest, and in other classes or defined types.
The init. pp manifest is the main class of a module and, unlike other classes or defined types, it is referred to only by the name of the module itself. For example, the class in init. pp in the puppetlabs-motd module is the mot d class.
A good starting point for debugging dependency issues is to instruct puppet to generate a dependency graph.
puppet apply --graph --noop manifest.pp
dot -Tpng /var/lib/puppet/state/graphs/relationships.dot -o relationships.png
By doing this you would see that the class repo:custom
has no dependency information at all.
maradns::install
sure has a dependency on the repo
class but not on the repo::custom
class, because repo::custom
has no dependency on repo
.
The new class declaration syntax class {'classname':}
does not set any dependencies, it behaves just like the include classname
syntax.
So either you set a dependency from repo::custom
to repo
or you instruct the maradns::install
class to directly depend on the repo:custom
class.
But you will run into more trouble. A dependency on class will only make sure that this class is applied. However, there will be no dependencies set on containing resources.
I would model your case like this:
class { 'repo:custom': }
class { 'maradns': }
class repo {
}
class repo::custom {
yumrepo {'custom':
enabled => 1,
gpgcheck => 0,
descr => "Local respository - ${::architecture}",
baseurl => 'http://repo.nike.local/CentOS/\$releasever/\$basearch';
}
}
class maradns {
class{[
'maradns::package',
'maradns::config',
'maradns::service',
]:}
}
class maradns::package {
package { 'maradns':
ensure => present,
require => Yumrepo['custom'],
}
}
class maradns::config {
file { 'marac:config':
ensure => present,
mode => '0644',
owner => root,
group => root,
}
}
class maradns::service {
service { 'maradns':
ensure => running,
enable => true,
hasrestart => true,
require => [
Package['maradns'],
File['mararc:config'],
],
}
}
From the puppetlabs stdlib documentation
In Puppet 2.6, when a class declares another class, the resources in the interior class are not contained by the exterior class. This interacts badly with the pattern of composing complex modules from smaller classes, as it makes it impossible for end users to specify order relationships between the exterior class and other modules.
The anchor type lets you work around this. By sandwiching any interior classes between two no-op resources that are contained by the exterior class, you can ensure that all resources in the module are contained.
Based on the posted manifest, an example would be:
Manifest puppettest.pp
:
class { 'repo': }
class { 'maradns': }
class repo {
anchor { 'repo::begin': } ->
class { 'repo::custom': } ->
anchor { 'repo::end': }
}
class repo::custom {
yumrepo {'custom':
enabled => 1,
gpgcheck => 0,
descr => "Local respository - ${::architecture}",
baseurl => 'http://repo.nike.local/CentOS/\$releasever/\$basearch';
}
}
class maradns {
Class['repo'] -> Class['maradns::install']
Class['maradns::install'] -> Class['maradns::config']
Class['maradns::config'] ~> Class['maradns::service']
class { 'maradns::install': }
class { 'maradns::config': }
class { 'maradns::service': }
}
class maradns::install {
package { 'maradns':
ensure => present,
}
}
class maradns::config {
file { 'mararc':
ensure => present,
path => '/etc/mararc',
mode => '0644',
owner => root,
group => root,
}
}
class maradns::service {
service { 'maradns':
ensure => running,
enable => true,
hasrestart => true,
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With