Suppose you have an arbitrary requirement that must be met for a puppet module to run.
How would you cause the puppet module to exit gracefully?
For example, say my module requires puppet 3.2 or newer to run successfully. If the module attempts to run on 3.1.x it will fail (non gracefully).
I could do a Notify, notice, alert or warning
http://docs.puppetlabs.com/references/latest/function.html#warning
Since you can't compare strings with integers using normal puppet comparison operators, you need to use the versioncmp() function.
if versioncmp("${::puppetversion}", '3.0.0') < 0 {
fail("foo requires foo 3.0.0 or greater, found: \'$::puppetversion\'")
}
ramindk from the puppet irc chanel also points out that you could use regular expressions:
if $::puppetversion =~ /^3/ {
fail("foo requires foo 3.0.0 or greater, found: \'$::puppetversion\'")
}
Related
Let's assume I have defined a custom fact for Puppet which depends on the rbvmomi module.
# frozen_string_literal: true
require 'pathname'
require_relative 'rbvmomi'
require_relative 'rbvmomi/optimist'
require_relative 'optimist'
require_relative 'vm_tag_utility'
Facter.add('custom_attributes') do
setcode do
hostname = Facter.value('hostname')
VmTagUtility.get_vm_custom_attributes(hostname)
end
end
When Puppet applies on an agent, it encounters this error:
Error: Facter: error while resolving custom facts in C:/ProgramData\PuppetLabs\puppet\cache\lib\facter\my_custom_fact.rb: cannot load such file -- C:/ProgramData/PuppetLabs/puppet/cache/lib/facter/rbvmomi
This tells me rbvmomi isn't present on the agent, or is not in a path searched by Puppet.
So, what's the best way manage the dependency between a custom fact and any supporting gem module?
So, what's the best way manage the dependency between a custom fact and any supporting gem module?
You have at least these options:
avoid depending on any gems outside those included in the Puppet distribution.
include all needed dependencies in your module's lib directory, so that they are synced together with the fact.
rescue the LoadError, and provide an alternative version of the fact (maybe a dummy) or no fact at all if it occurs.
maybe also have Puppet manage the gem, so that the regular version of the fact is available on the next run
this is tricky, because you need to install the gem in Puppet's Ruby, not the system Ruby, if any.
I am an absolute novice in puppet and I need to modify the existent puppet script
So, I have in the chain:
package { 'python-pip':
ensure => 'present',
} ->
In reality it should be
package { 'python2-pip':
ensure => 'present',
} ->
Can I add an OR condition, so it will work for both 'python2-pip' and 'python-pip'? So, if any of these packages is installed the result will be positive - is it possible?
Can I add an OR condition, so it will work for both 'python2-pip' and 'python-pip'? So, if any of these packages is installed the result will be positive - is it possible?
No, it is not possible as such. Every Package resource manages exactly one package, and declaring such a resource means that Puppet should attempt to ensure the specified state -- in this case, that some version of the specified package is installed on the target node. If you declare two packages then Puppet will try to manage two packages.
If truly "in reality it should be" python2-pip, then you should simply change the manifest appropriately.
If your nodes under management are heterogeneous, however, you may actually mean that on a strict subset of your nodes, the package name should really be "python2-pip", whereas on others, the current "python-pip" is correct. This kind of situation is relatively common, and it is usually addressed by using node facts to modulate your declarations.
That could take the form of using if blocks or other conditional statements to switch between alternative whole declarations, but often one can be more surgical. For example, a common approach is to choose just the package name conditionally, store the result in a variable, and just plug it in to your declaration. Maybe something like this:
$pip_package = $::operatingsystemmajrelease ? {
default => 'python-pip',
'7' => 'python2-pip'
}
package { $pip_package:
ensure => 'present',
}
Since you are a novice, it might be worth your while to have a look at how some existing modules approach such problems. Many modules are available on Github for you to browse, and anything you install from the Forge is at minimum available locally on your machine for you to examine.
Running rake spec tests I am getting:
Warning: The function 'hiera' is deprecated in favor of using 'lookup'. See
https://docs.puppet.com/puppet/4.10/reference/deprecated_language.html
(file & line not available)
So I decided to change to using lookup. However my hiera lookups are now not working
v1 = lookup('key') # doesn't work
v2 = hiera('key', undef) #works
I'm using rspec-puppet-2.5.0 and onceover 3.2.0, because otherwise other things don't work with the Puppet Enterprise version we're using (equivalent to puppet 4.7.0) (don't you just love Ruby versioning).
What I suspect is that something in the interception of lookup by rspec-puppet is not working properly and the correct hiera.yaml is not being found. Before I go debugging I was wondering if someone had already seen this?
Thanks a lot #matt-schuchard. That is the very reason: the hiera config v3. So I was approaching the refactor in the wrong direction. First upgrade the hiera and then the puppet code itself is the correct sequence for this.
In Puppet 3 and prior, templates in defines inherited scope from their calling class the same way native defined types do, so if I had a file resource with a template source created by a define, that template could make use of variables set in the calling class no matter which class called the define.
In Puppet 4 (also tested with Puppet 3.8 future parser), that appears to no longer be the case, and it is causing breakage that is hard to even measure in my environment, which has relied on this behavior for tens of thousands of lines of code. Is there any way at all to get this back? After looking into it, even converting the defines into native types doesn't solve the problem, as they rely on the ability to gather server-side information about what templates are available in different modules via custom functions, and everything in a native resource type appears to happen on the client.
Is this fixable, or do I attempt to wait for Puppet 5?
Edit: Don't get too caught up in the word 'scope' here -- I need any way to pass all class variables to a define and unpack them there so that they are available to templates, or a way to have a native type see what files are inside specified modules on the puppetmaster. I will accept any bizarre, obscure message passing option as long as it has this result, because the classes do not know where the templates are -- only the define does, because it's making use of the helper functions that scan the filesystem on the server.
Edit 2: To prove this works as expected in Puppet 3.8.5, use the following:
modules/so1/manifests/autotemplate.pp:
# Minimal define example for debugging
define so1::autotemplate (
$ensure = 'present',
$module = $caller_module_name,
) {
$realtemplate = "${module}${title}"
file { $title :
ensure => $ensure,
owner => 'root', group => 'root', mode => '0600',
content => template($realtemplate),
}
}
in modules/so2/manifests/example.pp:
# Example class calling so1::autotemplate
class so2::example (
$value = 'is the expected value'
) {
so1::autotemplate { '/tmp/qwerasdf': }
}
in modules/so2/templates/tmp/qwerasdf:
Expected value: <%= #value %>
In Puppet 3.8.5 with future parser off, this results in /tmp/qwerasdf being generated on the system with the contents:
Expected value: is the expected value
In Puppet 3.8.5. with parser = future in environment.conf (and also Puppet 4.x, though I tested for this example specifically on a 3.8.5 future parser environment), this results in the file being create with the contents:
Expected value:
Edit 3: two-word touch-up for precision
In Puppet 3 and prior, defines inherited scope from their calling class the same way native defined types do, so if I had a file resource with a template source created by a define, that template could make use of variables set in the calling class no matter which class called the define.
What you're describing is Puppet's old dynamic scoping. The change in scoping rules was one of the major changes in Puppet 3.0; it is not new in Puppet 4. There was, however, a bug in Puppet 3 that templates still used dynamic scope. That was fixed in Puppet 3.5, but only prospectively -- that is, when the future parser is enabled. Defined types themselves went through the scoping change in Puppet 3.0.0, along with everything else. The scope changes were a big deal (and Puppet devoted considerable effort to alerting users to them) when they first went into place, but nowadays there's no big deal here.
it is causing breakage that is hard to even measure in my environment, which has relied on this behavior for tens of thousands of lines of code.
I'm sorry you're having that experience.
Is there any way at all to get this back?
No. Puppet scoping rules do not work the way you want them to do. That they did work that way in templates (but not most other places) in Puppet 3 was and still is contrary to Puppet's documentation, and never intentional.
Is this fixable, or do I attempt to wait for Puppet 5?
There is no way to get dynamic variable scoping in templates or elsewhere in Puppet 4, and I have no reason to think that there will be one in Puppet 5.
If you need a template to expand host variables from the scope of a particular class, then you can get that by evaluating the template in the scope of that class. Alternatively, an ERB template can obtain variables from (specific) other scopes by means of the scope object. On the other hand, if you want to expand the template in the scope of a defined type, then you could consider passing the needed variables as parameters of that type.
There may be other ways to address your problem, but I'd need more details to make any other suggestions. (And that would constitute a separate question if you choose to ask it here on SO.)
I am trying to skip a module run on some nodes which doesn't have a facter value, say "FACTER_dev_boxes=true". how to do that using a conditional statement in init.pp?
Appreciate your suggestion on this!
For starters, here is a guide to conditionals
Additionally, facts discovered by facter are provided as top-level scope variables in Puppet
So something simple the following should work:
if $dev_boxes == 'true' {
// Run your module
}