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

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.

Related

Verifying if package is installed before downloading it in PUPPET

That is my manifest code which download, install and remove installer on a host.
class googlechrome_2 {
package { 'GoogleChrome':
ensure => installed,
source => 'C:\Soft\ChromeSetup.msi',
install_options => ['/qn'],
require => File['GoogleChromeMsi'],
}
file { 'GoogleChromeMsi':
ensure => file,
path => 'C:\Soft\ChromeSetup.msi',
source => 'puppet:///files/production/ChromeSetup.msi',
}
exec { 'msi_removing':
command => 'C:\Windows\System32\cmd.exe /c del C:\Soft\ChromeSetup.msi',
}
}
In this case my windows host always download chromesetup.msi regardless if google chrome already installed or not. How can I realize kind of "if condition" here to avoid downloading msi package each time in case if this package already installed?
In this case my windows host always download chromesetup.msi regardless if google chrome already installed or not.
Yes. Having the File resource in the node's catalog specifies that the file should be managed. Therefore, if it is not initially in the target state then Puppet will attempt to fix that.
By managing the file present but also including an Exec that removes the file, you ensure that the target node cannot achieve a stable state.
How can I realize kind of "if condition" here to avoid downloading msi package each time in case if this package already installed?
Simplest option: don't remove the installer.
Variation: Put the installer in an accessible network folder, so that you not only don't remove it, but you also don't install it.
If you really want a conditional: then it should be based on a custom fact that reports on the installation status of the package in question. You then use a Puppet if statement to control the contents of the node's catalog appropriately. Something along these lines, for example:
package { 'GoogleChrome':
ensure => 'installed',
source => 'C:\Soft\ChromeSetup.msi',
install_options => ['/qn'],
# relationhip with File['GoogleChromeMsi'] now declared on the other end
}
if $facts['chrome_is_installed'] {
file { 'GoogleChromeMsi':
ensure => 'absent',
path => 'C:\Soft\ChromeSetup.msi',
}
} else {
file { 'GoogleChromeMsi':
ensure => 'file',
path => 'C:\Soft\ChromeSetup.msi',
source => 'puppet:///files/production/ChromeSetup.msi',
before => Package['GoogleChrome'],
}
exec { 'msi_removing':
command => 'C:\Windows\System32\cmd.exe /c del C:\Soft\ChromeSetup.msi',
require => Package['GoogleChrome'],
}
}
I've solved this by using network shared folder in the package source:
class googlechrome_smb {
package { 'Google Chrome':
ensure => installed,
source => '\\\xxx.xxx.xxx.xxx\winfiles\ChromeSetup.msi',
install_options => ['/qn'],
}
}

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.

Predicting package version

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.

execute puppet class if file is modified by some package

I'm using puppet to deploy standardized ubuntu installs as well as configuration files.
I'm facing a problem where installing a certain package (through a dependency), will overwrite a critical config file. Is there a way to monitor whether this file changes (get's overwritten by some package) and restore it's original content?
what would be the best way of achieving this?
This is the class which takes care of configuring /etc/nsswitch.conf:
class nsswitchconfig {
# roll out nsswitch
class { 'nsswitch':
passwd => ['compat'],
group => ['compat'],
hosts => ['files'],
automount => ['files'],
}
notify { "hate #8040": message => "work around bug #8040" }
}
this is the class which overwrites /etc/nsswitch.conf
class desktop {
include nsswitchconfig
$package_name = ["ubuntu-desktop" ]
package { $package_name:
ensure => latest,
}
}
If the nsswitch class configures the file, all you need is to make sure it runs after the class that overrides it.
So in your case:
class { 'nsswitch':
passwd => ['compat'],
group => ['compat'],
hosts => ['files'],
automount => ['files'],
require => Class['desktop'],
}
should do the trick. (note the require part)

Dependency cycle with apt source

I'm trying to install the puppet module at https://github.com/dwerder/puppet-mongodb
One of the requirements for it to work is to have the mongodb repository set-up. Since I'm trying to deploy it on Debian I tried using the following class to add the source:
class mongodb::apt::repo {
include apt
apt::source { '10gen':
location => 'http://downloads-distro.mongodb.org/repo/debian-sysvinit',
release => 'dist',
repos => '10gen',
key => '7F0CEB10',
key_server => 'keyserver.ubuntu.com',
include_src => false
}
}
However, upon trying to install the module (on a test node) I get the following output:
root#debian:/etc/puppet/modules# puppet agent --test
info: Caching catalog for debian.lan
info: Applying configuration version '1353946258'
err: Could not apply complete catalog: Found 1 dependency cycle:
(Exec[apt_update] => Class[Apt::Update] => Anchor[apt::source::10gen] => Anchor[apt::source::10gen] => Apt::Source[10gen] => Class[Mongodb::Apt::Repo] => Package[mongodb-10gen] => Anchor[mongodb::install::end] => Anchor[mongodb::install::end] => File[10gen.list] => Apt::Source[10gen])
Try the '--graph' option and opening the resulting '.dot' file in OmniGraffle or GraphViz
notice: Finished catalog run in 0.06 seconds
The class is included in the module's install class at https://github.com/dwerder/puppet-mongodb/blob/master/manifests/install.pp
I'm not quite sure why this dependency cycle happens, any ideas?
What was your last change (that's probably the moment you added the cycle).
Try the suggestion to generate the graph. Post the generated dot file as gist so that we can investigate further.
Take a look at Debugging cycle or missing dependency.
Note that some dependencies are explicit (require, ->) or implicit (the resource provider add the dependency by it self)... having a look at the dot file should help.

Resources