Predicting package version - puppet

I'm configuring syslog-ng through puppet on my servers. The configuration files are very different between versions 2.x, 3.1 and 3.3 . On my hosts, depending on the operating system (centos5, centos6, debian 7, ubuntu), the available syslog-ng version will vary.
I had 2 ideas to adapt the configuration of syslog-ng to the correct version :
Custom Fact : It's easy to write a custom fact to test the installed version of syslog-ng. But this fact will be useless if syslog-ng is not already installed.
Conditions in the manifest : I find it a bit ugly to define a "case" in the manifest wich would determine the version of syslog-ng that the operatingsystem provides.
For me, the cleanest way to do this is to test which version of the package is available through the operatingsystem before installation.
A facter could do this, but I guess it would be a bit difficult.
Is there a puppetish way to solve my problem ?

There is indeed puppet-ish way to solve this problem!
You can combine $::osfamily with $::operatingsystemrelease to do something like this in your manifests:
case $::osfamily {
'CentOS': {
case $::operatingsystemrelease {
/^6/: { include syslog-ng::centos6 }
/^5/: { include syslog-ng::centos5 }
default: { notice("This operating system release for CentOs '${::operatingsystemrelease}' is not supported.")
}
}
default: { notice "Unsupported osfamily ${::osfamily}" }
}

I am not sure I understood all of your problem. In any case, one can use puppet package type to ensure a particular version and use $lsbdistdescription to get the OS name. For example :
package { 'syslog-ng' :
ensure => $::lsbdistdescription {
'/CentOS 7/': => "3.2",
'/CentOS 6/': => "3.1",
'/(Debian|Ubuntu)/' => "2.x",
default => "latest",
},
}
NOTE: In the above one has to get the exact name of the OS, i.e. CentOS 7 or CentOS 6 or Ubuntu from each OS. You can do that by executing facter --puppet | grep lsbdistdescription on the OS. I don't have variety of machines, so I couldn't check that exactly.
Then the configuration file can be just one sourced from a template. The template will vary based on the OS.
file { 'file.cfg' :
ensure => "present",
content => template("modulename/file.erb"),
require => Package["syslog-ng"],
}
Hope it helps.

Related

How can I override facter's os facts during local testing on a different operating system?

Environment
I am currently using Puppet 6.21.1 on Ruby 2.7.4 with PDK 2.3.0 (mostly for linting and launching the Puppet console as a REPL). The OS is macOS Monetery. If it matters, I'm running Ruby under RVM 1.29.12-next with a module-specific gemset.
Objective
I'm trying to test some Hiera lookups within a module on a macOS machine, and am trying to override the kernel, os, and osfamily facts returned on the development box by facter.
[See notes on the X/Y problem at the bottom of the post.]
What I've Tried
I have tried using the FACTERLIB environment variable, various individually-set FACTER_ environment variables, and a custom facts directory with the facter --custom-dir flag. While I have limited success my setting top-level variables with individual FACTER_ prefixed environment variables, I can't seem to get facter to return a defined Hash for facter os that I copied from a Debian or Ubuntu host, which is what I'm currently trying to do for reasons I touch on below.
My current fact override looks like this:
# ${module_repository?}/.myfacts/os.rb
Facter.add(:os) do
confine :kernal => 'Darwin'
has_weight = 1_000
setcode do
{
architecture => 'amd64',
distro => {
codename => 'focal',
description => 'Ubuntu 20.04.3 LTS',
id => 'Ubuntu',
release => {
full => '20.04',
major => '20.04',
},
},
family => 'Debian',
hardware => 'x86_64',
name => 'Ubuntu',
release => {
full => '20.04',
major => '20.04',
},
selinux => {
enabled => false,
},
}
end
end
Unfortunately, calling facter --custom-dir=".myfacts" facter os from within the module's top-level directory results in the standard facts (give or take the top-level facter element, which doesn't appear in the output when not running overrides) for macOS Monterey:
facter =>
os => {
architecture => "x86_64",
family => "Darwin",
hardware => "x86_64",
macosx => {
build => "21A559",
product => "macOS",
version => {
full => "12.0.1",
major => "12",
minor => "0",
patch => "1"
}
},
name => "Darwin",
release => {
full => "21.1.0",
major => "21",
minor => "1"
}
}
Both the X and the Y in my X/Y Problem
How can I get facter to report itself as a Debian-family Ubuntu system so that I can properly test my Hiera v5 lookups under ${module_respository?}/data/os/Debian.yaml from PDK console on my non-Puppetized Darwin development system?
Try
FACTER_<fact_name>=<fact_value> puppet agent -t ...
See also: https://www.puppetcookbook.com/posts/override-a-facter-fact.html
Using rspec-puppet is the best path forward in my experience. I too haven't had luck injecting facts (in any reliable way) to override... However, rspec-puppet can test with any pre-determined set of facts locally, or from various canned factsets. Give it a shot.

puppet / facter: "created(corrective)"

I use puppet to update / maintain itself (among other things). For some reason every time the client runs I get these two actions:
Notice: /Stage[main]/Servers::Packages::Puppet/Package[facter]/ensure: created (corrective)
Notice: /Stage[main]/Servers::Packages::Puppet/Package[puppet]/ensure: created (corrective)
The definitions in question look like this:
package { 'puppet' :
ensure => 'latest',
require => Package['facter'];
}
package { 'facter' :
ensure => 'latest',
}
file { '/etc/default/puppet' :
ensure => 'file',
mode => '644',
source => 'puppet:///modules/servers/packages/puppet/default';
}
file{ '/etc/puppetlabs/puppet/puppet.conf' :
mode => '644',
content => template("servers/packages/puppet/puppet_conf.erb"),
require => Package[ 'puppet' ];
}
service{ 'puppet' :
ensure => 'running',
enable => true,
require => Package[ 'puppet' ],
subscribe => [
File[ '/etc/default/puppet'],
File[ '/etc/puppetlabs/puppet/puppet.conf'],
];
}
What's wrong with my definition(s)? Why do puppet / facter appear to be reinstalled with every run?
Since Puppet 4, Puppet, Inc. has provided only all-in-one packages of client-side components, not named either 'puppet' or 'facter'. The package for Puppet 6 is named puppet-agent -- this is what you should be managing, not packages named 'puppet' or 'facter'.
The messages you report indicate that Puppet does not see up-to-date 'puppet' or 'facter' packages, which is natural because these do not exist. They also indicate that puppet thinks it has corrected the problem -- which it will have attempted to do by installing / updating packages with those names, and which apparently succeeded. This seeming incongruity will have arisen because the puppet-agent packages declare that they provide features named "puppet" and "facter", which your package manager is using to associate those package names with the puppet-agent package. As a result, the installation / update succeeds without actually installing anything new, leaving the system primed to do the same thing over again on the next run.
I suspect that the "(corrective)" marks on the log output reflect package-manager exit statuses indicating success without doing anything.

puppetlabs-dhcp: Could not find dependency File[/etc/dhcp/dhcpd.conf]

I'm pretty new to puppet and I'm trying to use the module puppetlabs-dhcp (v0.3.0) with puppet master/agent v3.7.2. I'm using a very simple class declaration following the example given by the README file.
When I try to run the class on the node using puppet agent -t the run fails with the error
Error: Failed to apply catalog: Could not find dependency File[/etc/dhcp/dhcpd.conf] for Service[isc-dhcp-server] at /etc/puppet/modules/dhcp/manifests/init.pp:173
I tried adding a file resource before the dhcp class declaration but the file stays blank. None of the configurations are taken into account.
I checked the dependencies:
concat > 1.0.1 (using 2.0.2)
stdlib > 2.0.0 (using 4.13.1)
Here is the dhcp portion of the node in site.pp:
class {'dhcp':
dnsdomain => [
'jecks.lab',
'0.0.10.IN-ADDR.ARPA',],
nameservers => ['10.0.0.2'],
ntpservers => ['us.pool.ntp.org'],
interfaces => ['eth0','eth1'],
}
dhcp::pool{'ops.jecks.lab':
network => '10.0.0.0',
mask => '255.255.255.0',
range => ['10.0.0.100','10.0.0.254'],
gateway => '10.0.0.1',
}
dhcp::host {'debian-main':
mac => 'xxxxxxxxxxxxxx',
ip => '10.0.0.3',
}
What am I doing wrong? I assumed the dhcpd.conf file was created using concat from the parameters given in the class declaration.
This is a bug in puppetlabs-dhcp 0.3.0 when using concat 2.x that was fixed in 0.4.0. Using a newer version of the dhcp module or downgrading concat to 1.x would fix it.
Note that the puppetlabs-dhcp module moved to the Vox Pupuli community organisation a while ago, so you can find updates at puppet/dhcp on the Forge. The latest at the time of writing is 1.0.1, rather than 0.3.0.

Custom fact should run after a package is installed

I have a small custom fact in a my php module
Facter.add('php_extension_version') do
setcode do
Facter::Core::Execution.exec("php -i | awk '/^PHP Extension =>/ { print $4}'") || nil
end
end
This obviously requires the php binary to be installed. However, I noticed that all facts are run once before applying the catalog, so this fact is invalid before php installed.
Is there any way of gathering the information after the module is installed? Is there perhaps another way of exposing this information except facter?
Update
I'm using the two facts to determine which of multiple .so files is the right one to install:
if $php_zts_enabled {
$so_name = "newrelic-$php_extension_version.so"
} else {
$so_name = "newrelic-$php_extension_version-zts.so"
}
file {"/usr/lib64/php5/extensions/newrelic.so":
source => "file:///opt/newrelic-php5-$version-linux/agent/x64/$so_name",
owner => root,
group => root,
mode => 0644,
notify => Service['apache'],
require => Exec["extract-php-agent-$version"]
}
The files that are located in the agent/x64 directory can be
newrelic-20060613.so newrelic-20090626-zts.so newrelic-20121212.so newrelic-20131226-zts.so
newrelic-20060613-zts.so newrelic-20100525.so newrelic-20121212-zts.so
newrelic-20090626.so newrelic-20100525-zts.so newrelic-20131226.so
You essentially have only two opportunities to execute code on the node:
As part of a Facter fact. As you are aware, this happens before puppet applies a catalog, so any facts dependent on the results of the puppet run will not be useful until the next run.
As part of a custom provider. You can create a custom type and provider for installing the extensions that checks the node state before deciding what to do. Providers execute on the node, and as long as you know the overall provider lifecycle you can make this happen after the PHP install. However, this is incredibly complex compared to normal puppet modules.
Outside of those options, the normal way of doing this would be to enforce the version and configuration of php within your own manifests, and then pass that information to here. You should already know the version of PHP and its extensions based on what packages you have installed.
I would modify the fact so that it's present only when the binary is present (hence it won't be present at the very first run).
Facter.add('php_extension_version') do
setcode do
if system("which php > /dev/null 2>&1")
Facter::Core::Execution.exec("php -i | awk '/^PHP Extension =>/ { print $4}'") || nil
end
end
end
and then in your manifest you'd wrap the original code in the if
if $php_extension_version {
if $php_zts_enabled {
$so_name = "newrelic-$php_extension_version.so"
} else {
$so_name = "newrelic-$php_extension_version-zts.so"
}
file {"/usr/lib64/php5/extensions/newrelic.so":
source => "file:///opt/newrelic-php5-$version-linux/agent/x64/$so_name",
owner => root,
group => root,
mode => 0644,
notify => Service['apache'],
require => Exec["extract-php-agent-$version"]
}
}

Can not find class during Puppet apply

My puppet structure is as follows
/puppet
/manifests
/nodes
redis.pp
site.pp
/modules
The site.pp resembles
class base {
include ml-basefw
include ml-users
include ml-filelimits
include repoforge
include epel
class { 'ml-yumrepo':
base_url => "http://${puppet_server}/yumrepo"
}
}
import 'nodes/*.pp'
node default {
include base
}
When I run
puppet apply --modulepath=/puppet/modules:/puppet/manifests --noop --debug /puppet/manifests/nodes/redis.pp
I receive
Error: Could not find class base for redis-1.test.ml.com on node redis-1.test.ml.com
Is there something non-standard about my file layout that precludes me from using apply?
I am not the maintainer of the puppet module so I am not able to alter the file structure or layout.
There are numerous related questions but I wasn't able to relate them to the problem that I am having.
Edit1 : Adding redis.pp
node /^redis-\d+(.stage)?(.test)?(.aws)?.ml.com$/ {
include base
include epel
class { 'redis':
package_ensure => '2.8.15-1.el6.remi',
service_ensure => 'running',
conf_bind => '0.0.0.0',
conf_port => '6379',
}
firewall { '176 allow port 6379 for redis traffic':
chain => 'INPUT',
state => ['NEW'],
dport => '6379',
proto => 'tcp',
action => 'accept'
}
}
What happens when you run puppet apply against your site.pp file instead? You probably don't have a node definition in your redis.pp file (nor should you).
This does in fact look a little messy and convoluted.
What you want is
an actual base module
defining class base in /puppet/modules/base/manifests/init.pp
You should also loose the import statement by arranging your manifests better. If your version of Puppet is recent enough (I think 3.6+), just see the docs.
fist of all, puppet have the entry manifest file.
in master mode, the entry is site.pp and puppet deprated deprecated it from version 3.5, it started auto imported all manifest files in specified directory.
in apply mode, the entry is specified file in your command.
so it works fine in your production environment, puppet master read site.pp(contains class base) and import nodes/*.pp(redis.pp, contains node definition). but when you use "puppet apply /puppet/manifests/nodes/redis.pp", puppet just read redis.pp, no anyone tell puppet where the base class is.

Resources