Resolve conflicting package resource declarations - puppet

We are attempting to use the camptocamp/puppet-nagios module, but we're running into a packaging naming conflict between vanilla CentOS repositories and RPMForge/RepoForge. The nsca daemon in CentOS provides the same service as the nagios-nsca package in RepoForge. In attempt to install the RepoForge package yet satisify the Package requirement for nsca resource, I've added this to my node definition:
include ::nagios
package { 'nagios-nsca': ensure => installed, alias => 'nsca', }
include ::nagios::nsca::server
The resulting error is:
Error: Duplicate declaration: Package[nsca] is already declared in
file /tmp/vagrant-puppet-1/modules-0/role/manifests/nagios.pp:45;
cannot redeclare at
/tmp/vagrant-puppet-1/modules-2/nagios/manifests/nsca/server.pp:24
The next test was to use order and calling the class directly from the node:
include ::nagios
package { 'nagios-nsca': ensure => installed, alias => 'nsca', } ->
class {'::nagios::nsca::server' : }
The code in question inside the nagios/manifests/nsca/server.pp file is:
class nagios::nsca::server(
$decryption_method = pick($nagios_nsca_decryption_method, '0'),
) {
include ::nagios::params
# variables used in ERB template
$basename = $nagios::params::basename
if !defined (Package['nsca']) {
package {'nsca':
ensure => installed;
}
}
Any insight as to what's happening here? I can always fork the camptocamp/puppet-nagios code and force the behavior we want, but I'd rather not.

Due to ! defined(Package['title']) not working as expected. I fixed this by giving nagios::nsca::server an additional parameter of nsca_package, including a default value of nsca to preserve current behavior:
--- a/manifests/nsca/server.pp
+++ b/manifests/nsca/server.pp
## -11,6 +11,7 ##
#
class nagios::nsca::server(
$decryption_method = pick($nagios_nsca_decryption_method, '0'),
+ $nsca_package = 'nsca'
) {
include ::nagios::params
## -20,6 +21,7 ## class nagios::nsca::server(
if !defined (Package['nsca']) {
package {'nsca':
+ name => $nsca_package,
ensure => installed;
}
}
Use for this new parameter would be:
node 'my-nagios-server.local' {
include ::nagios
class {'::nagios::nsca::server': nsca_package => 'nagios-nsca', }
}

Related

How to work around vcsrepo "duplicate declaration" evaluation error?

I am installing from github using puppet-vcsrepo. The code looks something like this:
class demo_class(
$my_repo = undef,
$my_tag = undef,
){
vcsrepo { "$my_repo",
path => "/home/user/$my_repo",
source => 'git#github.com:7yl4r/$my_repo.git',
ensure => latest,
provider => git,
}
# then declare resources specific to my_tag
}
This works just fine when called only once, but I am iterating over a list and installing dependencies so this resource sometimes gets declared twice. I think this is roughly equivalent to the code below.
class {"demo_class":
my_repo => test_repo,
my_tag => test_tag1,
}
class {"demo_class":
my_repo => test_repo,
my_tag => test_tag2,
}
Doing this yields a server-side "Duplicate declaration" error because vcsrepo is trying to map the the same path twice. However, this is exactly the behavior I want: for both resources declared by demo_class to require the same repo in the same location. This is so that I can declare one or more resources using demo_class and be sure the repo given by my_repo (which may be common to multiple my_tags) is there.
How can I modify this class so that I can call it twice without hitting an error?
I see the problem.
I reproduced the issue using this code:
define my_vcs_repo ($myRepo, $myTag) {
vcsrepo { "$myRepo-$myTag":
path => "/home/user/$myRepo",
source => "git#github.com:7yl4r/$myRepo.git",
revision => $myTag,
ensure => latest,
provider => git,
}
}
$data = [
{
myRepo => testRepo,
myTag => testTag1,
},
{
myRepo => testRepo,
myTag => testTag2,
},
]
$data.each |$i, $ref| {
$myRepo = $ref['myRepo']
$myTag = $ref['myTag']
my_vcs_repo { "$myRepo-$i":
myRepo => $myRepo,
myTag => $myTag,
}
}
That then results in:
Puppet::PreformattedError:
Evaluation Error: Error while evaluating a Resource Statement, Evaluation Error: Error while evaluating a Resource Statement, Cannot alias Vcsrepo[testRepo-testTag2] to ["/home/user/testRepo"] at /
Users/alexharvey/git/modules/foo/spec/fixtures/modules/foo/manifests/init.pp:3; resource ["Vcsrepo", "/home/user/testRepo"] already declared at /Users/alexharvey/git/modules/foo/spec/fixtures/modules/foo/
manifests/init.pp:3 at /Users/alexharvey/git/modules/foo/spec/fixtures/modules/foo/manifests/init.pp:3:5 at /Users/alexharvey/git/modules/foo/spec/fixtures/modules/foo/manifests/init.pp:26 on node alexs-macbook-pro.local
The problem is that you are asking Puppet to clone the same Git module to a directory but with two different tags checked out. That does not make sense.
The fix is that you need to specify a unique path in the vcsrepo path attribute, e.g.:
vcsrepo { "$myRepo-$myTag":
path => "/home/user/$myRepo-$myTag",
source => "git#github.com:7yl4r/$myRepo.git",
revision => 'production',
ensure => latest,
provider => git,
}
By the way, I notice you are using camelCase for your variables. Don't do that. Aside from the fact that it is not idiomatic for Puppet, there are things that will break in some versions of Puppet/Rspec puppet that I have seen.
Use snake_case for your variable names and class parameter names.
Update
The question has been edited, and it is now a question about how to declare the same vcsrepo in more than one class.
In general, try to refactor so that you do not need to do this in the first place. In other words, just move it out of this class and put it somewhere that is only expected to be declared once.
If you cannot do this for some reason, then you can also use virtual resources, which will allow you to declare it in multiple classes that will be declared on the same node.
To do that, you just have to rewrite what you have there as:
#vcsrepo { $my_repo:
path => "/home/user/$my_repo",
source => "git#github.com:7yl4r/$my_repo.git",
ensure => latest,
provider => git,
}
realize Vcsrepo[$my_repo]
Keep in mind that you will not be able to declare the class demo_class twice on the same node either. You would need to turn it into a defined type, as I did above.
It is mentioned in the comments below that you can also use ensure_resource and ensure_resources; see docs in stdlib.

manage hosts file on Windows using Puppet

I'm trying to manage my hosts file on a Windows machine using Puppet and Hiera. My problem is that I have never really used Hiera and I'm struggling with parsing the data content into a proper format.
The relevant section in hieradata/hiera.yaml looks like this:
myhosts : [
'host1 1.2.3.4',
'host2 2.3.4.5',
'host3 3.4.5.6']
I have code that uses a host module, but it also depends on a class that I don't have, so naturally it doesn't work.
class hosts::module (
$myhosts = hiera('myhosts'),
)
{
define update_hosts {
$value = split($name,' ')
host {
"${value[0]}" : ip => "${value[1]}",
}
}
update_hosts { $myhosts :; }
}
I have tried using the file resource instead of the host resource, and also tried doing it without any class, but for some reason I am getting this error
Error: Could not retrieve catalog from remote server: Error 500 on SERVER:
Server Error: Evaluation Error: Error while evaluating a Resource Statement,
Evaluation Error: Error while evaluating a Resource Statement, Duplicate
declaration: File[C:\Temp\tmp.txt] is already declared in file
/etc/puppetlabs/code/environments/production/manifests/site.pp:4; cannot redeclare
at /etc/puppetlabs/code/environments/production/manifests/site.pp:4
at /etc/puppetlabs/code/environments/production/manifests/site.pp:4:1
at /etc/puppetlabs/code/environments/production/manifests/site.pp:10 on node puppet-agent
As you can see, it claims that I have a duplicate declaration, but the weird thing is that it says it has a problem with the same line. It thinks it's declaring the same thing twice for some reason.
This is the code I have now (I know it won't work but the error doesn't really sound related)
define hosts_update($content) {
file { 'C:\Temp\tmp.txt' :
ensure => file,
content => $content,
}
}
hosts_update{ hiera('myhosts'):
content => split($name," "),
}
Any idea how to do this right?
fixed it.
site.pp
include update_hosts
init.pp
class update_hosts::host
(
$hosts = hiera('hosts_list'),
)
{
update_host { $hosts :; }
}
host.pp
define update_host {
​
$value = split($name,' ')
​
host {
"${value[0]}" : ip => "${value[1]}",
target => "C:/Windows/System32/drivers/etc/hosts"
}
}

Puppet: Multiple manifests in module

I am trying to create Puppet module to setup my web server.
What i want - is to split configuration to logical modules (manifests by services: webserver, database, ftp etc.) But I can not figure out how to use additional manifests in init.pp
I am going to use it only with puppet apply not server-client configuration.
My text module manifest (kp/manifests/init.pp):
class kp {
include kp::testfile
}
include kp
And additional manifest (kp/manifests/testfile.pp)
define kp::testfile {
$value = template("kp/some.erb")
file { 'testfile':
path => '/tmp/my.txt',
ensure => file,
content => $value
}
}
Documentation says:
If a class is defined in a module, you can declare that class by name in any manifest. Puppet will automatically find and load the manifest that contains the class definition.
But when I run puppet apply init.pp I am getting error message
Could not find class kp::testfile for myhost.com at /myDir/puppetModules/kp/manifests/init.pp:2 on node vagrant.example.com
Facts
/myDir/puppetModules/ is in modulepath so no problems here
Puppet version v2.7.11
Ubuntu 12.04 LTS
What I am doing wrong?
Thanks in advance!
Your kp::testfile is a defined type, not a class. To use a defined type you need to declare it like:
kp::testfile { 'name': }
Try redefining kp::testfile like
class kp::testfile {
$value = template("kp/some.erb")
file { 'testfile':
path => '/tmp/my.txt',
ensure => file,
content => $value
}
}
and you may have better luck.

Instantiating a class for each item in an array

Puppet Version: 3.2.4 (Puppet Enterprise 3.0.1)
In order to better support nagios cfg_dir and cfg_file directives in the config file, I've created the following class(es), one for each option:
# Class to add a cfg_dir to the nagios configuration file
class nagios::server::cfg_dir (
$config_dir,
$nagios_user,
$nagios_group,
$nagios_config_file = '/etc/nagios3/nagios.cfg',
)
{
# Build the config dir
file {$config_dir:
ensure => directory,
owner => $nagios_user,
group => $nagios_group,
mode => '0750',
}
# Append cfg_dir=$config_dir path to nagios.cfg file
augeas { "cfg_dir=$config_dir in $nagios_config_file":
incl => "$nagios_config_file",
lens => 'NagiosCfg.lns',
changes => "set cfg_dir/[last()+1] ${config_dir}",
require => File[$nagios_config_file],
}
}
Trying to use this construct inside nagios::server, I have this:
# Set up config directories
each($cfg_dir) |$x| {
class { 'nagios::server::cfg_dir':
config_dir => $x,
nagios_user => $nagios_user,
nagios_group => $nagios_group,
nagios_config_file => $nagios_config_file,
}
}
Which should, in theory, execute the class instantiation for each path passed in to the nagios::server class like so:
class{'::nagios::server': cfg_dir => ['/etc/nagios.d','/etc/nagios/objects'] }
However, I run into this issue:
Error: Could not match |$x| at /tmp/vagrant-puppet-1/modules-2/nagios/manifests/server.pp:181 on node localhost.localdomain
Can someone provide a working example of each in use? Am I expecting too much from this built-in puppet function?
Aside from a few of the code grammar issues above, I've found that this construct is only evaluated in the future parser:
puppet apply --parser=future --modulepath ...
http://docs.puppetlabs.com/puppet/latest/reference/experiments_lambdas.html
Still getting past other dependency issues. What pattern would I use to support this with the current parser instead of future? A custom function?
The answer to my follow-up question is to use defined types:
http://docs.puppetlabs.com/learning/definedtypes.html
Simply changing the above code from a class to a define and assign $config_dir the value from $target,
define nagios::server::cfg_dir (
$config_dir = $target,
$nagios_user,
$nagios_group,
$nagios_config_file = '/etc/nagios3/nagios.cfg',
){...
you can use constructs such as:
nagios::server::cfg_dir { '/etc/nagios.d/', '/etc/nagios/objects':
nagios_user => 'nagios',
nagios_group => 'nagios'
}
This solves the issue for me.

overriding Parameters in puppet modules

I want to override parameters of base nodes. What I want to get is a pattern like this:
# File manifests/nodes.pp
node myDefault {
class { 'my::common::puppet_setup':
service => 'enable',
pushable => 'disable',
}
# Do lots of default things ...
}
node 'myFirstNode' inherits myDefault {
# Do something ...
}
node 'mySecondNode' inherits myDefault {
class { 'my::common::puppet_setup::params':
service => 'disable',
pushable => 'enable',
}
}
I understood the the puppet documentation, i could do this by writing my module like this:
# File modules/my/manifests/common/puppet_setup.pp
class my::common::puppet_setup (
$pushable = $my::common::puppet_setup::params::pushable,
$service = $my::common::puppet_setup::params::service,
) inherits my::common::puppet_setup::params {
# package that configures puppet node
# input value validation
validate_re($pushable, ['^enable$', '^disable$', '^ignore$', ])
validate_re($service, ['^enable$', '^disable$', '^ignore$', '^cron$', ])
# setup puppet, start or disable agent, put ssh keys for push ...
}
class my::common::puppet_setup::params {
$pushable = 'enable'
$service = 'enable'
$puppetserver = 'puppet.my.site.de'
case $::osfamily {
'Debian': {
}
default: {
fail("not implemented yet for {::operatingsystem}")
}
}
}
The Documentations on puppet website says:
When a derived class is declared, its base class is automatically declared first (if it wasn’t already declared elsewhere).
But i get this error (some indentation added):
mySecondNode# puppet agent --test --environment dev_my
Error: Could not retrieve catalog from remote server:
Error 400 on SERVER: Duplicate declaration:
Class[My::Common::Puppet_setup::Params] is already declared;
cannot redeclare at /.../puppet/manifests/nodes.pp:16 on node mySecondNode
Warning: Not using cache on failed catalog
Error: Could not retrieve catalog; skipping run
I'm reading on this for a week and i guess my understanding ist totally wrong somewhere, although i used the puppetlabs ntp modules as an example.
what am i missing?
You should check Inheritance section from http://docs.puppetlabs.com/puppet/latest/reference/lang_node_definitions.html
Puppet treats node definitions like classes. It does not mash the two together and then compile the mix; instead, it compiles the base class, then compiles the derived class, which gets a parent scope and special permission to modify resource attributes from the base class.
One of the good solutions is to use roles and profiles, there's a great blog post about it:
http://garylarizza.com/blog/2014/02/17/puppet-workflow-part-2/
You can use virtual resources :
http://docs.puppetlabs.com/guides/virtual_resources.html

Resources