Puppet manifest: Overriding default variables in Hash - puppet

I've been handed the code for a Puppet module which was written by someone else. I've been tasked with getting it working in an actual Puppet environment.
I'm struggling to override defaults in the module in the manifest file. Hopefully this is a syntax issue, and not a issue with the init class.
In init.pp:
class our_module(
# Defaults to be overridden in the manifest file
Hash $config = {
'id' => '38e18a',
'secret' => 'donttellanyone',
'path' => '/test/path'
}
){
# Logic here...
}
How can I override these attributes? I've tried the following which gives my an InvalidCredentialsException:
node 'my_node' {
class { 'our_module':
config => {
id => 'newid',
secret => 'newsecret',
path => '/newpath
}
}
I'm new to Puppet and still getting my head around the docs and the syntax.

Given class our_module as presented in the question, this variation on the node block is valid for declaring that class and customizing its config parameter:
node 'my_node' {
class { 'our_module':
config => {
id => 'newid',
secret => 'newsecret',
path => '/newpath'
}
}
}
Hopefully this is a syntax issue, and not a issue with the init class.
If what you're really using takes the same form as above, then I'm sorry to have to tell you that the problem is not with your class declaration. If your Puppet runs are successful for nodes that do not declare class our_module, then my conclusion would be that the issue is indeed with the class implementation.
I've tried the following which gives my an InvalidCredentialsException
I am disinclined to think that that arises during catalog building. I cannot completely rule out the possibility, but that sure looks like a Java exception, whereas the catalog builder is implemented mainly in Ruby. It could be coming from puppetserver, Puppet's Java-based server front end, but that would not depend on whether your manifests declare a particular class. My crystal ball suggests that the our_module implementation is Execing a Java program when it is applied to the client, and that it is that program that is throwing the exception.
Some of those possibilities could be related to bad class parameter data, but I don't see any reason to think that the issue arises from a syntax error.

Related

In puppet, how to retrieve variables inside a class scope from a different parameterized class

In the scope of one class, I need to be able to access a variable from another class. The variable is passed as a parameter, e.g.
class parameterized_class (
$param1,
) {
...
}
and
class other_class () {
include parameterized_class
Class['parameterized_class'] -> Class['other_class']
$local_var = $parameterized_class::param1
}
With an example usage of:
node default {
class { 'parameterized_class':
param1 => 'somevalue',
}
class { 'other_class': }
}
The above example does not work, as I get an error that looks roughly like:
Must pass param1 to
Class[Parameterized_class] at
/path/to/modules/parameterized_class/manifests/init.pp:1
on node localhost
Obviously, include is trying to declare parameterized_class without passing any parameters. But from the docs, I can see that include allows for a class to have been already previously declared, and since I have the parameterized_class declaration as a dependency of other_class, I don't understand how I could be getting this error.
I'm using Puppet 3.4.3, the version available on Ubuntu 14.04 Trusty
How should I go about retrieving the value of the $param1 in parameterized_class from within other_class's scope? Is it not possible to use include on parameterized classes?
According to the Puppet documentation (https://docs.puppet.com/puppet/3.5/lang_classes.html#include-like-behavior) you cannot use include-like declarations with mandatory parameters. In your case, what you can do is just not bother with the include since you've already handled that by declaring it in your node definition. Also, because both are declared in your node definition you would want to order them there as well.
As a side note not providing default values is a bad practice as is using variables across modules.

Puppet configuration for multiple classes in a module

I'd like to use and configure the puppet-nginx module, although this is a general question about Puppet configuration.
Exec { path => [ "/bin/", "/sbin/" , "/usr/bin/", "/usr/sbin/" ] }
class nginx-setup {
class { 'nginx': }
}
include nginx-setup
Works great! Now, if I follow the docs for configuration I end up with something like this:
Exec { path => [ "/bin/", "/sbin/" , "/usr/bin/", "/usr/sbin/" ] }
class nginx-setup {
class { 'nginx': }
class { 'nginx::package':
package_source => 'nginx-mainline'
}
}
include nginx-setup
Error: Duplicate declaration: Class[Nginx::Package] is already declared
I tried include nginx instead of my first class declaration, but I think the module's init.pp is declaring the nginx::package class already and I still get duplicate declaration error. Even if that worked, what if I wanted to apply more configurations to another class within the nginx module? For example:
Exec { path => [ "/bin/", "/sbin/" , "/usr/bin/", "/usr/sbin/" ] }
class nginx-setup {
class { 'nginx': }
class { 'nginx::package':
package_source => 'nginx-mainline'
}
class { 'nginx::config':
nginx_error_log => 'syslog:server=localhost',
}
}
include nginx-setup
Many duplicate definitions!
So it feels like I should be passing everything required into my initial class declaration, but I can't seem to find the right way to do it. What is the best way to achieve this?
TL;DR
Consider using Hiera after all, for this module is tricky to use otherwise, due to some shortcomings in Puppet's handling of class parameters.
Long answer:
That's a loaded question, actually, even though it should not be. You correcly inferred the gist already. But let's take it step-by-step.
Module structure
It is now considered best practice (citation needed, although Ryan Coleman from Puppet Labs mentioned this in a recent presentation at FOSDEM) to expose all tunables of a module in its central class (here, class nginx).
This way, it is clear for the user that they need to look up the appropriate parameter for this one class, instead of going on a hunt for the appropriate class to tune.
The nginx module you picked seems to adopt this in large parts, but not consequently.
Hacks using defined()
As you have noticed, the module author added some shortcuts to allow you to declare your classes "safely" if you make sure the nginx::config class is encountered before the nginx class proper, lexically.
This is dangerous, because in a complex manifest, this might not be easy to assert.
include vs. class { }
Class parameters are problematic, because they lead to include being less safe than it used to be, because they don't mix well with class { 'name': ... } style declarations. The latter are always bad news because they have to be unique, as you are now experiencing.
It is best to stick to include as much as possible, which leads to the next issue.
Hiera
With parameterized classes, you really want to adopt Hiera as soon as possible. Defining class parameters as data is almost universally superior to doing it in the manifest. I understand the desire to stick to simple constructs first, but due to the issue described above, it can really make life harder on yourself.
It turns out it was module-specific. jfryman/puppet-nginx module classes are loaded automatically except for nginx::config (unless it isn't declared already) and most other classes inherit their settings from nginx::config. The correct solution for this module is;
class nginx-setup {
class { '::nginx::config':
http_access_log => 'syslog:server=localhost,tag=nginx,severity=info',
nginx_error_log => 'syslog:server=localhost,tag=nginx,severity=info',
}
class { '::nginx':
package_source => 'nginx-mainline',
}
}
include nginx-setup
jfryman/puppet-nginx is moving towards Hiera configurations and this might not work for long. I wanted a pure Puppet solution (to learn) before integrating Hiera but I wouldn't recommend it for everyone...

Why are all my hosts installing a package defined for one host on the puppetmaster

I'm new to puppet and I'm trying to figure out how to get different hosts installing different packages, but I've stumbled upon an issue I can't figure out. These are my manifests:
My site.pp:
node default {
}
node 'debh3' inherits default {
}
node 'debh4' inherits default {
import "db"
}
node 'master' inherits default {
}
My db.pp:
package { 'mysql-server':
ensure => installed
}
service { 'mysql':
ensure => true,
enable => true,
require => Package['mysql-server']
}
With this setup, mysql-server is being installed on debh3.
If I replace the "import db" with the actual code inside my db.pp, then mysql-server is only installed on debh4 (which is the behaviour i'm after).
Does anyone have a clue what I'm doing wrong here? I've put it all in site.pp to ensure there are no other dependencies affecting anything.
Also note that the import statement is deprecated and about to be removed from Puppet 4.0.
You should move your code to modules. In this case, you want to create a db module.
In /etc/puppet/modules/db/manifests/install.pp
class db::install {
package { 'mysql-server':
ensure => installed
}
}
an in /etc/puppet/modules/db/manifests/service.pp
class db::service {
include db::install
service { 'mysql':
ensure => true,
enable => true,
require => Class['db::install'],
}
}
From you node block, you can then just
include db::install
include db::service
or even just include db::service.
You could have both resources in one class, but it's good practice to structure your code through multiple classes.
Upon further digging, I found this in the "import" documentation at https://docs.puppetlabs.com/puppet/latest/reference/lang_import.html:
Import statements have the following characteristics:
They read the contents of the requested file(s) and add their code to top scope
They are processed before any other code in the manifest is parsed
They cannot be contained by conditional structures or node/class definitions
These quirks mean the location of an import statement in a manifest does not matter.
This points to why what I was doing was incorrect and why it caused the behaviour. As for a solution, I will look into best practices and determine the "correct" way to structure my manifests.

resource ordering synchronization issue "->" doesn't work?

I have encounter really weird behaviour which goes against what I have learned, tutorial says etc. So I would be glad if someone could explain why that is happening.
I have a role module which is made up of composition of profiles (role-profile pattern). My role consists:
class role::lab_prg_c2_dn inherits lab_prg_c2 {
class { 'profile::cluster_data_node':
namenode_fqdn => $role::lab_prg_c2::namenode_fqdn,
secondarynamenode_fqdn => $role::lab_prg_c2::secondarynamenode_fqdn,
}
->
class{'bigdatasolution':}
}
First class installs technology and second one installs our components and items which are build on top of technology. Hence the technology need to be installed first, thats the reason for "->" dependency. However this seems to me doesn't work correctly. As components from class 'bigdatasolution' are installed somewhere before the class profile::cluster_data_node finishes.
I tried to use require => Class['profile::cluster_data_node'] but that doesn't make any difference!
The content of class{'bigdatasolution':} :
class bigdatasolution {
$hdfs_default_conf = '/usr/local/hadoop.hdfs.conf'
$hbase_default_conf = '/usr/local/hadoop.hbase.conf'
include symlinks
include bdjar
}
Symlinks - create symlinks for the configuration installed in class profile::cluster_data_node and are not directly managed - it will be presented when actually specified package get installed.
bdjar - add our jar to a technology library so content is as follows:
class bigdatasolution::bdjar {
file { "/usr/lib/hadoop/lib/bigdata-properties.jar":
ensure => present,
mode => 0644,
group => 'root',
owner => 'root',
source => "puppet:///modules/bigdatasolution/bigdata-properties.jar"
}
}
I even tried to put require => "technologycalClass" here but that doesn't help either.
Can someone please help me understand what's wrong and how that should be solved properly?
I Using puppet 3 and ordering is specified explicetly - so no arbitrary ordering set by puppet should happen.
Thanks
If your 'profile::cluster_data_node' class 'includes' other classes/modules they will have no dependency ordering with the 'bigdatasolution' class.
I see you actually do include symlinks and bdjar. Basically every piece of ordering you want to have in puppet, you need to write explicitly.
Here you should replace the include statements with require, that way the class cluster_data_node will require the other two modules to complete before it says it has completed. Include is a pretty lose way of importing things in puppet and in my opinion is best to just avoid it and go with explicit require statements instead.
TL;DR: included modules have no transitive ordering; required modules do.

Changing class variable in node declaration in Puppet

I am trying to create a "template" for all my servers. I have 2 configurations. An NTP client (which is taken care of in the baseclass class. I want to create an override specific for the NTP servers by declaring something specific in the node declaration. Something like "baseclass::ntp:restrict => true,". Or alternatively, how would I change one of the already declared variable from baseclass::ntp?
Does anyone have any ideas host to do this?
This is what I have so far:
templates.pp
class baseclass {
include defaultusers
include sudoers
include issue
class { ntp:
ensure => running,
servers => ['ntpserver1.host.com',
'ntpserver2.host.com',],
autoupdate => false,
}
}
nodes.pp
node default {
include baseclass
}
node "ntpserver1.host.com" inherits default {
<some code here to declare new variable in baseclass::ntp>
<some code here to change existing variable, such as "ensure">
}
You have run smack into the problem with parameterized classes: they don't support overrides. They should, but due to various problems with the order in which things are initialized in Puppet, you can't override parameters to classes. Once you set them, you're done. This is different from defines, where overriding parameters works as you expect. There's an open bug about this that a bunch of us have voted up and are watching, but there appears to be little progress.
Given that, my recommendation would be to recast your parameterized ntp class as a define instead, because a define will work exactly as you want. Change the class to something like:
define ntp($servers, $autoupdate = false, $ensure = 'running') {
# ... put code from class here ...
}
and then change baseclass to:
ntp { $fqdn:
servers => [ 'ntpserver1.host.com',
'ntpserver2.host.com',],
}
You will have to change the class structure to add a new class, since you can't inherit from a class in a node, so change your node to:
node "ntpserver1.host.com" inherits default {
include hosts::ntpserver1
}
or however you want to name your per-host configuration classes. Then, in that class, you can do exactly what you expect to be able to do:
class hosts::ntpserver1 inherits baseclass {
Ntp["$fqdn"] { ensure => 'stopped' }
}
I know this seems like a huge runaround, particularly if you're used to doing a bunch of stuff inside nodes (which don't participate in the class inheritance tree). But without being able to override parameters to classes, there doesn't seem to be a good alternative. (We manage 500+ nodes and about 100 completely separate service definitions, with hundreds of modules and a huge amount of variety between hosts, including per-host overrides, using this method and it works extremely well.)
TL,DR summary: You can't override class parameters. Once you've passed a parameter to a class in Puppet, you're done. You can override define parameters. Therefore, anything you want to override is better written as a define than a class. However, remember that override hierarchies means that you have to put the core of your node definition in a class, since only classes can inherit from and override another class. Therefore, if you use overrides heavily, get into the habit of having your node definitions be trivial (just including a class that does all the work) so that your classes can inherit from base classes and override the parameters to defines.
I accepted rra's answer, but I found a solution that worked for me a little better. It's a slight hack, I suppose:
template.pp
class baseclass ($ntprestrict = 'false') {
include defaultusers
include sudoers
include issue
class { ntp:
ensure => running,
servers => ['ntpserver1.host.com',
'ntpserver2.host.com',],
autoupdate => false,
restrict => $ntprestrict,
}
}
nodes.pp
node "ntpserver1.host.com" {
class { baseclass: ntprestrict => 'true' }
}
node "client.host.com" {
class { baseclass: ntprestrict => 'false' }
}

Resources