0

I'm trying to configure global settings for the Puppet postgresql resource. I want to do it conditionally. I'm using an if statement to configure it depending on the contents of a variable.

define setup_postgresql_globals (
  $datadir,
) {
  if $datadir {
    class { 'postgresql::globals':
      datadir         => "${datadir}",
      needs_initdb    => true,
      version         => '9.3',
    }
  }
  else {
    class { 'postgresql::globals':
      needs_initdb    => true,
      version         => '9.3',
    }
  }
}

I'm getting this error.

Error: Duplicate declaration: Class[Postgresql::Globals] is already declared

I only want to pass the datadir argument to postgresql::globals if it contains a value.

How can I use a conditional statement, and not get a duplicate declaration error?

Christian Long
  • 10,385
  • 6
  • 60
  • 58

2 Answers2

2

How can I use a conditional statement, and not get a duplicate declaration error?

The problem is not the conditional statement, but rather the fact that you declare more than one instance of setup_postgresql_globals. The same class is declared each time, and although classes are idempotent and can be declared multiple times, only the first evaluation of a declaration of a given class can use the resource-like syntax. All others must use one of the variations of include-like syntax.

In part for this reason, it is poor form to put a resource-like class declaration into a defined type. Doing so is asking for trouble. If you need to use a resource-like class declaration then put it in another class. That possibly solves the problem altogether, but at worst pushes it up to a higher level. It looks like this is probably a reasonable approach in your case, because a setup_postgresql_globals sure sounds like something that should itself be idempotent.

Consider also that classes -- but not defines -- can make use of automatic data binding. Relying on external data to be automatically bound to your class parameters is often a good way to replace resource-like class declarations with include-like ones. When applied in appropriate moderation, that can yield cleaner, simpler, easier to use and maintain manifests.

John Bollinger
  • 160,171
  • 8
  • 81
  • 157
2

I only want to pass the datadir argument to postgresql::globals if it contains a value.

You should not worry about not passing datadir if it does not contain a value. Just pass the paramater to the postgresql::globals along with your other paramaters. If the datadir has a value the class will use it, and if it does not then postgresql::globals will handle it just fine and use its own defaults. You need to modify the way your passing the paramater though, this will not work: datadir => "${datadir}", you must pass the it like this datadir => $datadir,. Your define could be as simple as this:

define setup_postgresql_globals (
  $datadir = undef,
) {
  class { 'postgresql::globals':
    datadir         => $datadir,
    needs_initdb    => true,
    version         => '9.3',
  }
}

The reason your getting Error: Duplicate declaration: Class[Postgresql::Globals] is already declared, is becuase you are instantiating your define multipule times. By nature a define can be instantiated mutluple time so it can be problematic to do a class like include within one since you can only include a class like this once. There are a couple of ways you can resolve this issue.

1) Evaluate your need for this define at all. It seams like you created this define just to handle the datadir paramater being a undef value. As I have shown obove you might not need this define since your logic is not needed.

2) You should only do basic includes of classes within a define. You can include a class like this multuple times saftly. You would have to handle setting datadir via the automatic paramater lookup system from hiera. For example:

define setup_postgresql_globals (
  $datadir = undef,
) {
  include 'postgresql::globals'
}

Again you should probably remove this define and handle including postgresql::globals from where your instantiating setup_postgresql_globals.