Puppet, setting dependencies

I am starting to setup puppet. I want to set up a dependencies that a mail transfer agent must be installed before that class tries to install or start anything. With puppet, the standard method seems to set a dependency seems to be require blah. The challenge is I don't use the same MTA on all of my systems. Some of the systems which are actual mail servers I have a full MTA (exim), but the vast majority of my systems have ssmtp installed. What I want do is setup a requirement so that either one of those MTAs is installed and functional before the the foo class is processed.

Here is a configuration that somewhat demonstrates what i am trying to do.

node default {
  if $fqdn in ["mail1.example.org", 
               "mail2.example.org", 
               "mail3.example.org"] {

    include fullmta  # mailhub, and so on
  } else {
    include ssmtp    # really basic send-only mta.
  }
  include foo        # class that requries an mta be installed
}

class foo {
  require MTA # FIXME, A valid mta is required.

  package { foo: ensure => present, }
  ... # also a service, and some files, and so on...
}

So in my foo class, how do I require that one of the possible MTAs classes has been processed?


Solution 1:

If you split the MTA logic to a separate class, you can handle the logic there - and your resources can require the MTA class to enforce the dependency relationship.

node default {
  include mta
  include foo        # class that requries an mta be installed
}

class mta {
  if $fqdn in ["mail1.example.org", 
               "mail2.example.org", 
               "mail3.example.org"] {

    include fullmta  # mailhub, and so on
  } else {
    include ssmtp    # really basic send-only mta.
  }
}

class foo {
  package { foo: ensure => present,
                 require => Class['mta'], }
  ... # also a service, and some files, and so on...
}

Solution 2:

Use an alias. Something like this:

service { "ssmtp":
    ...
    alias => "MTA",
}

service { "fullmta":
    ...
    alias => "MTA",
}

class foo {
    package { foo:
        ensure  => present,
        require => Service["mta"],
        ...
    }
    ...
}

Solution 3:

You can specify require dependencies as arrays, in which case Puppet will make sure all dependencies are met before proceeding. In this situation I usually do something like the following:

node default {
  include mta
  include foo        # class that requries an mta be installed
}

class mta {
  if $fqdn in ["mail1.example.org", 
               "mail2.example.org", 
               "mail3.example.org"] {
    package { "conflicting-package-A": ensure => present, }
    package { "conflicting-package-B": ensure => absent, }
  } else {
    package { "conflicting-package-A": ensure => absent, }
    package { "conflicting-package-B": ensure => present, }
  }
} 

class foo {
  package { foo: ensure => present,
                 require => [Package["conflicting-package-A",
                                     "conflicting-package-B"], }
  ... # also a service, and some files, and so on...
}

This way, you not only make sure that the foo package is explicitly dependent on both other packages, but you set things up so that if you remove a host from the mail*.example.org list in the future, that "conflicting-package-A" will be replaced with "conflicting-package-B" automatically.