Puppet exception handling? - puppet

I was wondering how one would do try/catch/throw type exception handling in a puppet manifest. Here's how I wish puppet would work ...
class simple {
unless ( package { 'simple': ensure => present } ) {
file { '/tmp/simple.txt':
content => template( 'simple/simple.erb' ),
}
}
}
Thanks

I don't think there is an exception handling in a programmatic way you would like in Puppet. If you declare a resource, it is expected that puppet brings your machine to that state (installed package) and if not, it will fail automatically.
One thing that you can do (and I don't recommend) and that is not "puppet way" is following:
Create custom facter (not custom function since it is executed on puppet master and you want this ruby code to be executed on puppet agent)
Since it is plain ruby code in facter, you can have exception handling and all programmatic things. You can install package as unix command from puppet code and have some logic which will, if not installed retrieve some value as fact
You would use this fact value and based on it you would determine if you want to create file or not
Also, if easier, you can write bash script which will do this logic and execute it from puppet using exec resource
Hope it helps.

Related

Puppet manifest: Overriding default variables in Hash

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.

Puppet: Class Ordering / Containment - always wrong order

I read a lot about ordering puppet classes with containment (iam using Puppet 6). But it still does not work for me in one case. Maybe my english is not good enough and i miss something. Maybe somebody know what iam doing wrong.
I have a profile to installing a puppetserver (profile::puppetserver). This profile has three sub-classes which I contain within the profile::puppetserver
class profile::puppetserver(
) {
contain profile::puppetserver::install
contain profile::puppetserver::config
contain profile::puppetserver::firewall
}
That works fine for me. Now I want to expand this profile and install PuppetDB. For this, i use the puppetdb module from puppet forge:
So what i do is add profile::puppetserver::puppetdb and the contain to the profile::puppetserver
class profile::puppetserver::puppetdb(
) {
# Configure puppetdb and its underlying database
class { 'puppetdb': }
# Configure the Puppet master to use puppetdb
class { 'puppetdb::master::config': }
}
When i provision my puppetserver first and add the profile::puppetserver::puppetdb after it, puppetdb installs and everything works fine.
If I add it directly with contain, and provisioning everything at once, it crashes. It's because the puppetdb module is installed randomly during my master server installs (and also the postgresql server and so on). That ends in my puppetserver is not running and my puppetdb generate no local ssl certificates and the service doesn't comes up.
What i try first:
I installed the puppetdb Package in my profile::puppetserver::puppetdb directly and use the required flag. It works when i provision all at once.
class profile::puppetserver::puppetdb (
) {
Package { 'puppetdb':
ensure => installed,
require => Class['profile::puppetserver::config']
}
}
So i think i could do the same in the code above:
class profile::puppetserver::puppetdb(
) {
# Configure puppetdb and its underlying database
class { 'puppetdb':
require => Class['profile::puppetserver::config']
}
# Configure the Puppet master to use puppetdb
class { 'puppetdb::master::config':
require => Class['profile::puppetserver::config']
}
}
But this does not work...
So i read about puppet class containment and ordering by chains. So i did this in my profile::puppetserver
class profile::puppetserver(
) {
contain profile::puppetserver::install
contain profile::puppetserver::config
contain profile::puppetserver::firewall
contain profile::puppetserver::puppetdb
Class['profile::puppetserver::install'] ->
Class['profile::puppetserver::config'] ->
Class['profile::puppetserver::firewall'] ->
Class['profile::puppetserver::puppetdb']
}
But it still does not have any effect... he still starts to install postgresql and the puppetdb package during my "puppetserver provisioning" in the install, config, firewall steps.
How i must write the ordering, that all things from the puppetdb module, which i call in profile::puppetserver::puppetdb, only starts when the rest of the provisioning steps are finished?
I really don't understand it. I think maybe it haves something to do with the fact, that i declare classes from the puppetdb module inside of profile::puppetserver::puppetdb and not the directly Resource Type. Because when i use the Package Resource Type with the Require Flag, it seems to work. But i really don't know how to handle this. I think there must be a way or?
I think maybe it haves something to do with the fact, that i declare
classes from the puppetdb module inside of
profile::puppetserver::puppetdb and not the directly Resource Type.
Because when i use the Package Resource Type with the Require Flag, it
seems to work.
Exactly so.
Resources are ordered with the class or defined-type instance that directly declares them, as well as according to ordering parameters and instructions applying to them directly.
Because classes can be declared multiple times, in different places, ordering is more complicated for them. Resource-like class declarations such as you demonstrate (and which you really ought to avoid as much as possible) do not imply any particular ordering of the declared class. Neither do declarations via the include function.
Class declarations via the require function place a single-ended ordering constraint on the declared class relative to the declaring class or defined type, and declarations via the contain function place a double-ended ordering constraint similar to that applying to all resource declarations. The chaining arrows and ordering metaparameters can place additional ordering constraints on classes.
But i really dont know how to handle this. I think there must be a way or?
Your last example shows a viable way to enforce ordering at the level of profile::puppetserver, but its effectiveness is contingent on each of its contained classes taking the same approach for any classes they themselves declare, at least where those third-level classes must be constrained by the order of the second-level classes. This appears to be where you are falling down.
Note also that although there is definitely a need to order some things relative to some others, it is not necessary or much useful to try to enforce an explicit total order over all resources. Work with the lightest hand possible, placing only those ordering constraints that serve a good purpose.

Puppet Run Stages with sub modules

I am learning Puppet and have taken their "Getting Started with Puppet" class but it did not cover Run Stages, and their documentation on Run Stages is thin.
I need to make sure that two things happen before anything else that Puppet does. I have been advised by the instructor of my "Getting Started with Puppet" class to look at Run Stages.
In my investigation of Run Stages, I have learned that the puppetlabs-stdlib class sets up some "standard" Run Stages. One of them being "setup". As shown in the snippet below I have implemented the stage => 'setup' as per https://puppet.com/docs/puppet/5.5/lang_run_stages.html. However, I am getting errors from Puppet:
root#server:~# puppet agent -t
Info: Using configured environment 'dev_branch'
Info: Retrieving pluginfacts
Info: Retrieving plugin
Info: Retrieving locales
Info: Loading facts
Error: Could not retrieve catalog from remote server: Error 500 on SERVER:
Server Error: Evaluation Error: Error while evaluating a Resource Statement, Could not find stage setup specified by
Class[Vpn::Roles::Vpn::Client] (file:
/etc/puppetlabs/code/environments/wal_prod1910_dev/modules/bh/manifests/roles/snaplocker.pp, line: 5, column: 3) on node server
Warning: Not using cache on failed catalog
Error: Could not retrieve catalog; skipping run
Looking at the error message and the Puppet documentation, I have added quotations around the various string values and replaced my initial -> with the correct =>, but I still get the same error.
class bh::roles::snaplocker()
{
# stage => setup takes advantage of the setup run stage introduced by
# puppetlabs-stdlib which is pulled in by puppet-control-bh/Puppetfile
class { 'vpn::roles::vpn::client': stage => 'setup' }
class { 'bh::profiles::move_archives': stage => 'setup' }
#...
}
Looking more closely at the error message, I believe that the cause is that puppetlabs-stdlib id introduced by the Puppetfile in the class that calls the module that I am working on. I have been deliberately avoiding trying to pull in puppetlabs-stdlib in the class I am working on to avoid duplication. But apparently I need it... The module I am working on does not have a Puppetfile do I need to somehow include puppetlabs-stdlib in my sub module? If so how should I do that? If not, how to I tell my sub module to use the instance declared in the parent module's Puppetfile?
Usually, you don't need any stage if you have correct classes/resources dependencies.
From the "Run stages" documentation:
CAUTION: Due to these limitations, use stages with the simplest of classes, and only when absolutely necessary. A valid use case is mass dependencies like package repositories.
In your case, if you really want stages, you should add include stdlib::stages1 or explicitly add stage like stage { 'setup': }

Reusing Puppet Defined Type Parameter in Other Defined Type

Lets say I want to define a set of resources that have dependencies on each other, and the dependent resources should reuse parameters from their ancestors. Something like this:
server { 'my_server':
path => '/path/to/server/root',
...
}
server_module { 'my_module':
server => Server['my_server'],
...
}
The server_module resource both depends on my_server, but also wants to reuse the configuration of it, in this case the path where the server is installed. stdlib has functions for doing this, specifically getparam().
Is this the "puppet" way to handle this, or is there a better way to have this kind of dependency?
I don't think there's a standard "puppet way" to do this. If you can get it done using the stdlib and you're happy with it, then by all means do it that way.
Personally, if I have a couple defined resources that both need the same data I'll do one of the follow:
1) Have a manifest that creates both resources and passes the data both need via parameters. The manifest will have access to all data both resources need, whether shared or not.
2) Have both defined resources look up the data they need in Hiera.
I've been leaning more towards #2 lately.
Dependency is only a matter of declaring it. So your server_module resource would have a "require => Server['my_server']" parameter --- or the server resource would have a "before => Server_module['my_module']".

Puppet - test if a package already defined?

I'm writing some puppet modules and have a package defined in two modules hence get the following error:
err: Could not retrieve catalog from remote server: Error 400 on SERVER: Duplicate definition: Package[gnome-session-fallback] is already defined in file /etc/puppet/modules/vnc4server/manifests/init.pp at line 3; cannot redefine at /etc/puppet/modules/vino/manifests/init.pp:7 on node l
Hence want to ensure that the package has not already been defined but the following does not work:
if ! defined ('gnome-session-fallback') {
package { 'gnome-session-fallback':
ensure => installed,
}
}
Can anyone suggest how to fix this, and on the broader scale, what is the "proper" approach to avoiding clashes such as this in modules?
You are missing Package[] inside defined(). The correct way to do it:
if ! defined(Package['gnome-session-fallback']) {
package { 'gnome-session-fallback':
ensure => installed,
}
}
The cleanest way to do this is to use the ensure_resource function from puppetlabs-stdlib:
ensure_resource('package', 'gnome-session-fallback', {'ensure' => 'present'})
To answer my own question about what the "proper" approach is : This issue is discussed at https://groups.google.com/forum/?fromgroups=#!topic/puppet-users/julAujaVsVk and jcbollenger offers what looks like a "best-practice" solution - resources which are defined multiple times should be moved into their own module and included into the classes on which they depend. I applied this and solved my problem.
This doesn't actually answer why "if !defined" fails however...
One cleaner way (among multiple ways) is to create a virtual package resource and then realize it. You can realize the same virtual package multiple times without error.
#package { 'gnome-session-fallback':
ensure => installed,
}
And then where you need it:
realize( Package[ 'gnome-session-fallback' ] )

Resources