4

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?

2 Answers 2

2

Not sure if it will work, but maybe you could try to define the dependencies on a node level scope. For example:

class X {
    ...
}

class Y {
    ...
}

node N {
   include Y
}

node M {
    Class['X'] -> Class['Y']
    include X
    include Y
}
1
  • Thanks. I'm trying something similar. Making progress. I'll share the details if it works. Sep 20, 2012 at 16:37
1

It looks like golja's answer might work. Here's the pattern I landed on myself. It seems to be working so far.

class yum::reposareready {

    # The sole and critical purpose of this class is to act as an
    # intermediary between various repositories and the package
    # resources. It lets us do things like:

    # class yum::repofoo { ... }

    # class applicationbar {
    #     package { 'bazfromtherightrepo': ..., require => yum::reposareready, }
    # }

    # node n {
    #     class { 'yum::repofoo': before => Class['yum::reposareready'] }
    # }

    # With this pattern:

    # 1. The repository resource doesn't need to know about its ordering.

    # 2. Nodes can mix in repository resources, including and excluding
    #    repositories as needed.

    # 3. Classes that declare package resources need only require a
    #    generic "repos are ready" class rather than the knowing the
    #    specific repository from which to get a package.

    # DO NOT DO THIS:

    # class yum::repofoo { before => Class['yum::reposareready'] }
    # class yum::repobar { before => Class['yum::reposareready'] }
    #
    # node n {
    #     include yum::repofoo
    # }
    #
    # node m {
    #     include yum::repobar
    # }

    # The former scopes the ordering dependency to the node, whereas the
    # latter does not. The latter would make Puppet apply yum::repofoo
    # to both nodes n and m, whereas the former only applies
    # yum::repofoo to node n.

}

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .