October 25, 2012 | DevOps
A common issue with Puppet manifests is a clash of resource definitions that appears in the Puppet log file as: ‘Duplicate definition: File[resource-name] is already defined; cannot redefine at…’
This issue is likely to occur when building a service stack from reusable Puppet modules and you try to alter a resource that has already been defined in modules that the service depends on.
Building a service from reusable Puppet modules has a couple of benefits. It saves you time when you don’t have to write new modules every time you build a new service. It also improves the quality when you build your service from modules that are trusted, tried and tested.
WRITTEN BY
But reusing modules can sometimes cause a bit of a headache and bad temper. Especially when building a service from modules that were written by someone else (as in http://forge.puppetlabs.com), you may notice that the module does almost everything the service is required to do but then the module does just a tiny bit too much, e.g. tweaks a start-up script that your service manifests are going to modify.
In a situation like this you want to keep the majority of the module’s functionality and only alter some of its behaviour. In Puppet this can be done through class inheritance.
class apache::proxy { file { '/etc/httpd/conf/mods-enabled/proxy.conf': source => "puppet:///modules/apache/proxy.conf" }
Your service called myapp requires a special configuration for mod_proxy so you decide to define your own version of the same file:
class myapp { include apache::proxy file { '/etc/httpd/conf/mods-enabled/proxy.conf': source => "puppet:///services/myapp/proxy.conf" }
This is confusing for Puppet because it cannot decide which of the two resource declarations it should be using and this causes an error ‘File[/etc/httpd/conf/mods-enabled/proxy.conf] is already defined; cannot redefine at…”
class myapp::apache inherits apache::proxy { file { '/etc/httpd/conf/mods-enabled/proxy.conf': source => "puppet:///services/myapp/proxy.conf" }
class apache::proxy (var1, var2) { file { '/etc/httpd/conf/mods-enabled/proxy.conf': source => "puppet:///modules/apache/proxy.conf" }
This class is otherwise the same as the non-parameterised apache::proxy class except that you have to pass values for parameters var1 and var2 when calling the class. Calling the class with parameters uses the following syntax:
class { 'apache::proxy': var1 => 'param1', var2 => 'param2'; }
But when you want to override the resource in parameterised class apache::proxy and resource override is defined in myapp::apache as per the example above (see Workaround: non-parameterised class) you must call both classes apache::proxy and myapp::apache separately and let Puppet merge these classes.
In service manifests of myapp I’d call both class apache::proxy with parameters and class myapp::apache without parameters:
class { 'apache::proxy': var1 => 'param1', var2 => 'param2'; } include myapp::apache
The above would be implemented in Puppet manifests something like the following.
file:///puppet/modules/apache/manifests/apache-proxy.pp:
class apache::proxy (var1, var2) { file { '/etc/httpd/conf/mods-enabled/proxy.conf': source => "puppet:///modules/apache/proxy.conf" }
file:///puppet/services/myapp/manifests/myapp.pp:
class myapp::apache inherits apache::proxy { file { '/etc/httpd/conf/mods-enabled/proxy.conf': source => "puppet:///services/myapp/proxy.conf" }
class myapp { class { 'apache::proxy': var1 => 'param1', var2 => 'param2'; } include myapp::apache } This blog is written exclusively by the OpenCredo team. We do not accept external contributions.
GOTOpia 2021 – Platform Engineering as a (Community) Service
Watch Nicki Watt’s talk on Platform Engineering as a (Community) Service at GOTOpia to learn what it takes to build a platform that is fit…