In my Puppet configuration I want to say "If I declare class X, apply its resources before class Y." In other words, I want to declare an ordering, but remain silent about whether or not to apply class X.
If I understand the "before" metaparameter correctly, saying:
class X {
...
before => Class['Y'],
}
class Y {
...
}
node N {
include Y
}
node M {
include X
include Y
}
will include X's and Y's resources on both nodes M and N. Instead, what I want is to express separately, "Apply just Y," or "Apply X and Y, and apply X before Y."
For context, what I want to do more concretely is to ensure that my Yum repositories are configured before Puppet applies package resources. I want to omit some repositories from some nodes. I want my package resource definitions to remain naive about the corresponding repositories; I don't want to litter my package resource definitions with dependencies on specific repositories.
I tried using Run Stages, but beyond the simplest configuration, they seem to cause dependency cycles. For example, this would work:
stage { 'first': before => Stage['main'] }
class X {
...
before => Class['Y'],
}
class Y {
...
}
node N {
include Y
}
node M {
class { "X": stage => first }
include Y
}
But this wouldn't:
stage { 'first': before => Stage['main'] }
class X {
...
class { "Z": stage => first } # to avoid Z "floating off"
before => Class['Y'],
}
class Y {
...
include Z
}
class Z { ... }
node N {
include Y
}
node M {
class { "X": stage => first }
include Y
}
In the latter, Puppet thinks there is a dependency cycle. I presume that's due to Z being declared and its resources being managed in two different stages. I could potentially simplify the classes I need in stage "first" to avoid the dependency cycle problems I'm seeing in practice, but the Puppet documentation spreads FUD about Run Stages.
Here's the specific bit of configuration that makes Run Stages really unappealing to me. In development VMs I'm bootstrapping a Yum server locally via a "devrepo".
class yum::devrepo {
# Ensures httpd is running as a Yum server before anything else
# tries to install packages from it.
exec { 'httpd-for-yum':
command => '/sbin/service httpd restart',
require => Class['yum::server'],
}
yumrepo {
"devrepo":
require => [Exec['httpd-for-yum'],],
descr => "Local Dev YUM Repo",
baseurl => "http://localhost/repos/redhat/5/x86_64/",
gpgcheck => "0",
enabled => "1",
}
}
class yum::server {
include httpd
package { ['createrepo']:
ensure => present;
}
exec { 'update-repo-metadata':
require => [ Package['createrepo']],
cwd => '/var/www/html/yum',
command => '/usr/bin/createrepo --update -d repos/redhat/5/x86_64/',
creates => '/var/www/html/yum/repos/redhat/5/x86_64/repodata/repomd.xml',
}
file {'/etc/httpd/conf.d/yum.conf':
ensure => file,
mode => 0644,
source => "puppet:///modules/yum/yum_httpd.conf",
require => Package['httpd'],
notify => Service['httpd'],
}
}
I want to include my production Yum repo on some nodes and the dev repo on others.
If I put yum::server
and yum::devrepo
in stage "first", then later httpd declarations in other classes cause problems later, because the other classes put httpd in the main stage.
If Puppet can express "If I declare class X, apply its resources before class Y", how? If not, how can I get the ordering I want without dependency cycles and without including Yum repository resources that I want to omit?
Not sure if it will work, but maybe you could try to define the dependencies on a node level scope. For example:
It looks like golja's answer might work. Here's the pattern I landed on myself. It seems to be working so far.