Recently I have been working on a puppet module which defines a new resource which in turn requires a certain directory to exist, as so:

mything/init.pp

define mything ($log_dir='/var/log/mythings') {

  notify { "${name} installed!": }

  file { $log_dir:
    ensure => directory
  }

  file { "${log_dir}/${name}":
    ensure => directory,
    require => File[$log_dir]
  }
}

As you can see the log directory is parameterised with a default, combining flexibility with ease of use.

As it happens there’s no reason why multiple of these mythings shouldn’t be installed on the same host, as so:

mything { "thing1": }
mything { "thing2": }

But of course that causes puppet to bomb out: Duplicate definition: File[/var/log/mythings] is already defined

The solution I’ve found is to realise a virtual resource defined in an unparameterised class, as so:

mything/init.pp

define mything ($log_dir='/var/log/mythings') {

  notify { "${name} installed!": }

  include mything::defaultlogging

  File <| title == $log_dir |>

  file { "${log_dir}/${name}":
    ensure => directory,
    require => File[$log_dir]
  }
}

mything/defaultlogging.pp

class mything::defaultlogging {
  @file { '/var/log/mythings':
    ensure => directory
  }
}

Now the following works:

mything { "thing1": }
mything { "thing2": }

If we want to override and use a different log directory as follows:

mything { "thing3":
  log_dir => '/var/log/otherthing'
}

we get this error: Could not find dependency File[/var/log/otherthing] for File[/var/log/otherthing/thing3] at /etc/puppet/modules/mything/manifests/init.pp:12

This just means we need to define the new log directory as so:

$other_log_dir='/var/log/otherthing'
@file { $other_log_dir:
  ensure => directory
}
mything { "thing3":
  log_dir => $other_log_dir
}

and all is good. Importantly, applying this manifest will not create the default /var/log/mythings directory.