Include/declare two classes which both contain the same resource and name - puppet

I have two puppet modules m1 and m2. Please see sample modules below with the same resource type and name:
class m1 {
service {'firewalld':
ensure => 'stopped',
enable => 'false',
}
... // more resource types
}
class m2 {
service {'firewalld':
ensure => 'running',
enable => 'true',
hasrestart => true,
subscribe => Exec['firewall-cmd'],
}
package {'httpd':
ensure => 'present',
}
exec { 'firewall-cmd':
command => "firewall-cmd --zone=public --add-port=80/tcp --add-port=443/tcp --permanent",
path => "/usr/bin/",
refreshonly => true,
subscribe => Package['httpd'],
}
... // more resource types
}
I want to apply both of these modules to multiple nodes. When I try to apply these modules it gives me the error Duplicate Declaration for resource type
I am new to puppet. I have tried to design third module with common resource types, and after inheriting this third module m3 to m1 and m2, it gives me the error Parameter 'ensure' is already set on Service puppet
How can I design my modules to get rid of these errors?
I don't want to create a single module, because for some nodes I want to apply only m1 or m2 and not both.

Each resource can only exist in one place for each node. This is true even if they match.
I would suggest moving resources that are needed by multiple modules to their own module, and includeing it from the other modules. In this case, that means creating a firewalld module.

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.

Puppet: how to share a common resource / variable in modules

I am trying to use puppet to bootstrap a new VPS. I am going to be running multiple sites, and, at the moment I am planning on running them in separate user accounts. I want to have a common authorized key for all of these users. I am using puppet 4.10.
The issue I'm having is that I want to add my ssh key into the authorized_keys for all of these users, but I can't seem to work out how to have a common resource. I've tried adding it in a class and then including that, but it's a duplicate. I tried passing in a variable to the class, but again, duplicate.
Basically I have a module like this
class wibble_somesite {
user { 'someuser':
ensure => 'present',
managehome => true,
purge_ssh_keys => true,
home => '/home/someuser',
shell => '/bin/bash'
}
ssh_authorized_key { 'patrickmacbookair':
ensure => present,
user => 'someuser',
type => 'ssh-rsa',
key => 'some_shared_key'
}
}
which I then include in my manifests/site.pp. However, I want to have multiples of these class wibble_someothersite and I want to centrally manage the some_shared_key inside the ssh_authorized_key stanza.
Any help would be appreciated. I have tried following the docs but I just haven't got anywhere.
I could just duplicate all the ssh_authorized_key calls, but that's obviously horrible.
You cannot have multiple instances of a class. However, you can with defined types.
Your example can be :
define wibble_somesite () {
user { $title:
ensure => 'present',
managehome => true,
purge_ssh_keys => true,
home => "/home/${title}",
shell => '/bin/bash'
}
ssh_authorized_key { "${title}_patrickmacbookair":
ensure => present,
user => $title,
type => 'ssh-rsa',
key => 'some_shared_key'
}
}
And you can use it like this :
wibble_somesite{'patrick':}
wibble_somesite{'bob':}
It will create users 'patrick' and 'bob', and allow the shared key to connect.
Is this what you are are looking for ?

Puppet; Call another .pp

So I am using the https://forge.puppetlabs.com/pdxcat/nrpe module to try to figure out automation of NRPE across hosts.
One of the available usages is
nrpe::command {
'check_users':
ensure => present,
command => 'check_users -w 5 -c 10';
}
Is there anyway to make a "group" of these commands and have them called on specific nodes?
For example:
you have 5 different nrpe:command each defining a different check, and then call those specific checks?
I am basically trying to figure out if I could group certain checks/commands together instead of setting up a ton of text in the main sites.pp file. This would also allow for customized templates/configurations across numerous nodes.
Thanks!
EDIT:
This is the command and what it's supposed to do when called on with the 'check_users' portion. If I could have a class with a set of "nrpe:command" and just call on that class THROUGH the module, it should work. Sorry, though. Still new at puppet. Thanks again.
define nrpe::command (
$command,
$ensure = present,
$include_dir = $nrpe::params::nrpe_include_dir,
$libdir = $nrpe::params::libdir,
$package_name = $nrpe::params::nrpe_packages,
$service_name = $nrpe::params::nrpe_service,
$file_group = $nrpe::params::nrpe_files_group,
) {
file { "${include_dir}/${title}.cfg":
ensure => $ensure,
content => template('nrpe/command.cfg.erb'),
owner => root,
group => $file_group,
mode => '0644',
require => Package[$package_name],
notify => Service[$service_name],
}
}
What version are you talking about? In puppet latest versions, inheritance is deprecated, then you shouldn't use it.
The easiest way would be to use "baselines".
Assuming you are using a manifests directory (manifest = $confdir/manifests inside your puppet.conf), simply create a $confdir/manifests/minimal.pp (or $confdir/manifests/nrpe_config.pp or whatever class name you want to use) with the content below:
class minimal {
nrpe::command { 'check_users':
ensure => present,
command => 'check_users -w 5 -c 10',
}
}
Then just call this class inside your node definitions (let's say in $confdir/manifests/my_node.pp) :
node 'my_node.foo.bar' {
include minimal
}

Puppet dependency resolution ordering

I have a couple hosts which have Docker containers on them, so I've defined a class called apps::docker which installs Docker on the hosts:
class apps::docker {
include apps::docker::repository, apps::docker::install,
apps::docker::service
Class["Apps::Docker::Repository"] ->
Class["Apps::Docker::Install"] ->
Class["Apps::Docker::Service"]
Package["Docker"] ~> Service["Docker"]
}
class apps::docker::repository {
apt::source { 'docker':
location => "http://get.docker.io/ubuntu",
key => "A88D21E9",
release => "docker",
repos => "main",
include_src => false
}
}
class apps::docker::install {
package { 'docker':
name => "lxc-docker",
ensure => present
}
class apps::docker::service {
service { 'docker':
provider => 'upstart',
enable => true,
ensure => running,
hasrestart => true,
hasstatus => true
}
}
Pretty simple stuff, actually.
The problem is that when I try to define a class which depends on this class, the execution happens out of order and commands fail. For example, my class profiles::shiningstar::containers depends on apps::docker as defined in profiles::shiningstar:
class profiles::shiningstar {
include apps::docker
include profiles::shiningstar::containers
Class["Apps::Docker"] -> Class["Profiles::Shiningstar::Containers"]
}
Unfortunately, this doesn't work as seen below:
Error: /Stage[main]/Profiles::Shiningstar::Containers::Puppetmaster::Pull/Docker::Image[rfkrocktk/puppetmaster:1.0.5]/Exec[docker pull rfkrocktk/puppetmaster:1.0.5]: Could not evaluate: Could not find command 'docker'
... (similar errors)
Notice: /Stage[main]/Apps::Docker::Repository/Apt::Source[docker]/Apt::Key[Add key: A88D21E9 from Apt::Source docker]/Apt_key[Add key: A88D21E9 from Apt::Source docker]/ensure: created
It's executing things completely out of order. What's wrong with my configuration and how can I specify that ALL of the dependencies of apps::docker must be satisfied before profiles::shiningstar::containers?
You probably want to contain the inner classes instead of just including them.
class apps::docker {
contain apps::docker::repository
contain apps::docker::install
contain apps::docker::service
Class['apps::docker::repository']
->
Class['apps::docker::install']
~>
Class['apps::docker::service']
}
Note that it makes sense (in your case at least) to make the ::install class as a whole notify all of the ::service class. The makes you more flexible in refactoring the respective implementation of those classes.
Edited after first comment - don't try to put chaining arrows between contain statements.
You should use an anchor, that will ensure that all dependencies are built
class apps::docker {
include apps::docker::repository, apps::docker::install,
apps::docker::service
Class["Apps::Docker::Repository"] ->
Class["Apps::Docker::Install"] ->
Class["Apps::Docker::Service"] ->
anchor{"apps::docker":}
Package["Docker"] ~> Service["Docker"]
}

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.

Resources