Yes, this is normal. Puppet only allows resources to be declared once. In general, if you have code like:
class aaa {
notify { 'xxx': message => 'yyy' }
}
class bbb {
notify { 'xxx': message => 'yyy' }
}
include aaa
include bbb
Puppet apply that and you will see an error like this:
Error: Evaluation Error: Error while evaluating a Resource Statement,
Duplicate declaration: Notify[xxx] is already declared at (file: ...test.pp,
line: 2); cannot redeclare (file: ...test.pp, line: 6) (file: ...test.pp, line: 6,
column: 3) on node ...
Workarounds
Solution 1 Refactor so both classes inherit a third class
Generally, the best way to resolve this is to refactor your code so that there is a third class that contains the duplicated resource, and other classes include that using the include
function, like this:
class ccc {
notify { 'xxx': message => 'yyy' }
}
class aaa {
include ccc
}
class bbb {
include ccc
}
include aaa
include bbb
That works fine.
Note that this only works because the include
function can be called any number of times, unlike a resource declaration - also unlike the resource-like class declarations.
You can read more about "include-like v resource-like class declarations" here.
Solution 2 Use virtual resources
You can also use virtual resources. Refactor like this:
class ccc {
@notify { 'xxx': message => 'yyy' }
}
class aaa {
include ccc
realize Notify['xxx']
}
class bbb {
include ccc
realize Notify['xxx']
}
include aaa
include bbb
An added advantage of this one is you can use resource collectors and select only specific resources from a set of virtual resources, like this:
class ccc {
@notify { 'ppp': message => 'xxx' }
@notify { 'qqq': message => 'yyy' }
@notify { 'rrr': message => 'zzz' }
}
class aaa {
include ccc
Notify <| message == 'xxx' |>
}
class bbb {
include ccc
Notify <| message == 'xxx' or message == 'yyy' |>
}
include aaa
include bbb
If you don't need this functionality here, as appears the case, you probably should use the first proposal.
Solution 3 Use ensure resource
Another option is the ensure_resources
function in stdlib:
class aaa {
ensure_resources('notify', {'xxx' => {'message' => 'yyy'}})
}
class bbb {
ensure_resources('notify', {'xxx' => {'message' => 'yyy'}})
}
include aaa
include bbb
Solution 4 Use defined
Historically this is strongly advised against, although the docs do not mention any reason to not use it. It is possible to use the defined
like this:
class aaa {
if ! defined(Notify['xxx']) {
notify { 'xxx': message => 'yyy' }
}
}
class bbb {
if ! defined(Notify['xxx']) {
notify { 'xxx': message => 'yyy' }
}
}
include aaa
include bbb
This way, the resource is added to the catalog only if it is not already in there.