Missing resources when running "puppet agent --noop" - puppet

I may have misunderstood how "puppet agent --noop" works:
In the definition of a class I set the existence of a file and I set it's user&group ownership and this is what I have when I un "puppet agent --noop" :
If the file doesn't exist, "puppet agent --noop" works fine
If the file exists but user or group doesn't exist, then "puppet agent --noop" fails
complaining about the missing user or group.
If I simply run "puppet agent" (without "--noop") it works fine: Doesn't
matter if the user, group or file exists or not previously: it
creates the group, the user and/or the file.
1st question: I suppose that the "--noop" run doesn't verify if the catalog is asking the missing resources to be created. Isn't it?
2nd question: Is there any way to do any kind of mocking to avoid the problem of missing resources when launching "--noop"?
Let's paste some code to show it:
# yes, it should better be virtual resources
group { $at_group:
ensure => "present"
}
user { $at_user:
ensure => present,
gid => "$at_group",
require => Group[$at_group],
}
file { '/etc/afile':
owner => $at_user,
group => $at_group,
mode => '0440',
content => template('......erb')
require => User[$at_user]
}
output:
# puppet agent --test --noop
Info: Retrieving plugin
Info: Loading facts in /var/lib/puppet/lib/facter/puppet_vardir.rb
Info: Loading facts in /var/lib/puppet/lib/facter/facter_dot_d.rb
Info: Loading facts in /var/lib/puppet/lib/facter/pe_version.rb
Info: Loading facts in /var/lib/puppet/lib/facter/root_home.rb
Info: Caching catalog for pagent02
Info: Applying configuration version '1403055383'
Notice: /Stage[main]/Agalindotest::Install/Group[my_group]/ensure: current_value absent, should be present (noop)
Notice: /Stage[main]/Agalindotest::Install/User[my_user]/ensure: current_value absent, should be present (noop)
Error: Could not find user my_user
Error: /Stage[main]/Agalindotest::Install/File[/etc/afile]/owner: change from 1001 to my_user failed: Could not find user my_user
Error: Could not find group my_group
Error: /Stage[main]/Agalindotest::Install/File[/etc/afiles]/group: change from 1001 to my_group failed: Could not find group my_group
Let's show how it works if the file doesn't exist:
then "puppet agent --test --noop" works like a charm:
Notice: /Stage[main]/Agalindotest::Install/Group[my_group]/ensure: current_value absent, should be present (noop)
Notice: /Stage[main]/Agalindotest::Install/User[my_user]/ensure: current_value absent, should be present (noop)
Notice: /Stage[main]/Agalindotest::Install/File[/etc/afile]/ensure: current_value absent, should be file (noop)
Thanks a lot!!
/ Angel

Unfortunately, there is currently no way to overcome this limitation.
The ensure property doesn't fail just on account of a missing owner - I believe the file will just end up owned by root. That is why the output is more pleasant when the file doesn't exist.
As for the behavior with an existing file: Each resource is considered individually, and the file resource must admit failure if the group does not exist when the file is evaluated. The fact that the group would (likely) be created without noop cannot be easily accounted for.
As for you idea of ignoring the issue under noop conditions if there is a user resource - that has merit, I believe. Would you raise that as a feature request at Puppet's Jira?
Update
As of Puppet 3.3 you can use rely on the $clientnoop value that is supplied by the agent along with Facter facts. Please note that tailoring your manifest to avoid failures in noop mode has two consequences.
The manifest itself becomes much less maintainable and comprehendible.
The reporting from noop runs becomes inaccurate, because the "unsafe" property values are not part of the noop catalog
You could build the manifest like this:
# this scenario does not actually call for virtual resources at all :-)
group { $at_group:
ensure => "present"
}
user { $at_user:
ensure => present,
gid => "$at_group",
require => Group[$at_group],
}
file { '/etc/afile':
mode => '0440',
content => template('......erb')
# require => User[$at_user] # <- not needed at all, Puppet autorequires the user and group
}
if ! $::clientnoop {
File['/etc/afile'] {
owner => $at_user,
group => $at_group,
}
}
The owner and group properties are ignored in noop mode, with the pros and cons as discussed above.
All things considered, I feel that this is not worth the hassle at all.

Related

Copy overwriting a file only on RPM update

I have following problem with my Puppet installation:
I would like to copy (overwrite) a file only, if a new version of RPM package was installed.
I have something like this:
package { 'my-rpm':
ensure => $version,
source => $rpm_file,
notify => File[$my_file],
}
file { $my_file:
ensure => present,
source => $my_file_template,
replace => true, # Overwrite (default behaviour). Needed in case new RPM was installed.
}
The problem is, that the "file" get executed also, if no new version of RPM was installed. This happens, since I change the $my_file file afterwards using "file_line"
file_line { 'disable_my_service':
ensure => present,
path => $my_file,
line => ' <deployment name="My.jar" runtime-name="My.jar" enabled="false">',
match => ' <deployment name="My.jar" runtime-name="My.jar">',
}
This change of the content of the $my_file triggers copying fresh version from the template on each and every Puppet run.
I could add "repace => false" to my file copy define, but this would break any further updates...
The long story short: I have the following loop
Copy file -> change file -> copy file -> ...
How can I break this loop?
UPDATE:
Clarification:
The "file_line" define is executed optionally, controlled by a Puppet hiera-property and so the "enabled" part can't be included in the RPM.
The entire file can't be turned into a template (IMHO). The problem: Puppet module must be able to install different (future) versions of the file.
The problem remains unsolved for the time being.
I think the problem you're getting here is that you're trying to manage $my_file using both the file and file_line resource types and this is going to cause the file to change during the catalog application.
Pick one or the other, manage it as a template or by file line.
I suspect what's happening here during the Puppet run is the file resource changes $my_file to look like this;
<deployment name="My.jar" runtime-name="My.jar">
Because that's what is in the template then, the file_line resource changes it to;
<deployment name="My.jar" runtime-name="My.jar" enabled="false">
Then on the next run the exact same thing happens, file changes $my_file to match the template and then file_line changes it to modify that line.
I would remove the notify => File[$my_file], it's not actually doing anything, you're defining the desired state in code so if that file changes for any reason, manual change or RPM update, Puppet is going to bring that file back into the desired state during the run. You may want to consider;
file { $my_file:
ensure => present,
source => $my_file_template,
require => Package['my-rpm'],
}
This ensures the file desired state is enforced after the package resource so if the package changes the file the file will be corrected in the same run.
https://puppet.com/docs/puppet/7.4/lang_relationships.html
You may also want to consider;
file { $my_file:
ensure => present,
source => $my_file_template,
require => Package['my-rpm'],
notify => Service['my-service'],
}
So the service provided by the rpm restarts when the config file is changed.
Copy overwriting a file only on RPM update
The problem is, that the "file" get executed also, if no new version of RPM was installed. This happens, since I change the $my_file file afterwards using "file_line"
Yes, File resources in a node's catalog are applied on every run. In fact, it's best to take the view that every resource that makes it into in a node's catalog is applied on every run. Resources' attributes affect what applying them means and / or what it means for them to be in sync, not whether they are applied at all. In the case of File, for example, setting replace => false says that as long as the file initially exists, its content is in sync (and therefore should not be modified), whereas replace => true says that the file's content is in sync only if it is an exact match to the specified source or content.
Generally speaking, it does not work well to manage the same or overlapping physical resources via multiple Puppet resources, and that's what you're running into here. The most idiomatic approach when you run into a problem with that is often to write a custom resource type with which to manage the target object in detail. But in this case, it looks like you could work around the issue by using an Exec to perform the one-time post-update copy:
package { 'my-rpm':
ensure => $version,
source => $rpm_file,
}
~> exec { "Start with default ${my_file}":
command => "cp '${my_file_template}' '${my_file}'",
# this is important:
refreshonly => true,
}
-> file { $my_file:
ensure => 'file',
replace => false,
# no source or content
owner => 'root', # or whatever
group => 'root', # or whatever
mode => '0644',
# ...
}
-> file_line { 'disable_my_service':
ensure => present,
path => $my_file,
# ...
}
You can, of course, use relationship metaparameters instead of the chaining arrows if you prefer or have need.
That approach gives you:
management of the package via the package manager;
copying the packaged default file to the target file only when triggered by the package being updated (by Puppet -- you won't get this if the package is updated manually);
managing properties of the file other than its contents via the File resource; and
managing a specific line of the file's contents via the File_line resource.

puppet-service should be stopped only if file exists

According to the documentation in my example, the resource 'service x' will be executed if target resource require => Exec['checkForFile'] is successfully applied.
And the exec command will execute onlyif the file '/etc/init.d/x' is present.
so when the file '/etc/init.d/x' is absent,the command in Exec['checkForFile'] should not be executed and
the resource 'service x' should be skipped if target resource is not executed.
For me, the resouce service x is not run which is correct, but at the same time i am not receiving any error or exit code 1 to show Exec['checkForFile'] failed to run. is this expected behavior?
class test2::testcleanup (
$target_location,
)
{
service { 'x':
ensure => stopped,
restart => "service x restart",
stop => "service x stop",
status => "service x status",
require => Exec['checkForFile'],
before => [
File["/etc/init.d/x"],
File[ "remove_directory"],
],
}
exec { "checkForFile":
command => "/bin/true",
path => ["/usr/bin","/usr/sbin", "/bin"],
onlyif => 'test -f /etc/init.d/x',
logoutput => true,
}
file {'/etc/init.d/x':
ensure => absent,
}
file {'remove_directory':
ensure => absent,
path => $target_location,
recurse => true,
purge => true,
force => true,
   }
}
According to the documentation in my example, the resource 'service x'
will be executed if target resource require => Exec['checkForFile'] is
successfully applied. And the exec command will execute onlyif the
file '/etc/init.d/x' is present.
Yes, Service['x'] will not be applied if Exec['checkForFile'] is not successfully applied.
so when the file '/etc/init.d/x' is absent,the command in
Exec['checkForFile'] should not be executed
Yes, but that's different from the Exec not being applied.
Like any other resource, Execs model target-machine state, though in their case, that state is local to the context of one catalog run. Often it is described as whether the Exec's command has been run or not, but it is better characterized the other way around, as whether the Exec's command needs to be run or not. If it is in the "needs to be run" state then successfully executing the Exec's command transitions it to the "does not need to be run" state.
The unless, onlyif, and creates properties of an Exec serve to determine the initial state of the corresponding physical (so to speak) resource. If none of them are provided then the physical resource is initially in the "needs to be run" state. If one or more of them is provided then they may indicate that the Exec initially is in the "does not need to be run" state. And that's the target state. In that case, then, the Exec is successfully applied without running its command.
and the resource 'service
x' should be skipped if target resource is not executed.
No. Service['x'] will be skipped if applying the Exec fails. That happens only if its command is run and exits with a failure status.
For example, instead of using an onlyif in your Exec, use 'test ! -f /etc/init.d/x' as its command.
For me, the resouce service x is not run which is correct, but at the
same time i am not receiving any error or exit code 1 to show
Exec['checkForFile'] failed to run. is this expected behavior?
Maybe.
In your case, the Exec is always successfully applied. If /etc/init.d/x exists then /bin/true is run (successfully). If the file does not exist then the Exec succeeds without running /bin/true. Service['x'] will be applied either way, but if the physical service is already in its target state (stopped), then Puppet won't take any further action, and nothing about that resource will appear in its output at the default log level. Turning on --debug logging in the agent ought to make it clearer what's happening.
But all of that is backward. Puppet is not intended to be a script engine, and it does not work well as one. Any transient machine information needed for determining the appropriate target state for the machine is best conveyed to Puppet in the form of facts. Then you determine during catalog building, based on those facts and any other available data, what resources to declare.
Moreover, it is best to minimize the amount of current machine state that goes into determining its target state. Prefer instead to tie target state to machines' identities, and where necessary, tweak the details based on invariant characteristics (OS, hardware architecture, etc.). Although this model may not be sufficient for all needs, it often does suffice, and it otherwise serves as an excellent baseline.

Are resources defined by an array of titles always evaluated in order?

In Puppet, it's possible to define a number of resources using an array of titles, like so:
file { ['/some/file1', '/some/file2']:
ensure => file,
}
In terms of order of application, is it the same as the following?
file { '/some/file1':
ensure => file,
}
-> file { '/some/file2':
ensure => file,
}
I'm using puppet version 3.8.7, though I am curious if this behavior changes between Puppet 3 / 4 / 5.
I've searched the docs extensively, and while they do talk about defining resources with an array of titles, they don't talk about what order those resources get applied in.
This is kind of a complicated question to answer, but I will split up the question into components to help explain.
Is this resource array ordering typical behavior in Puppet?
Yes. To help with this, we need to delve into the Puppet DSL. Since arrays are part of the asymmetric syntax tree portion of the Puppet DSL (and usually for most if not all DSL), we need to check the relevant AST code of the Puppet Parser. The code for the commit at this time of writing is here. You can change the link to point at your version to see how and if the code has changed to see what the behavior will be for the version of Puppet you are using at any given time.
The code we want to examine is:
# Second level of implicit iteration; build a resource for each
# title. This handles things like:
# file { ['/foo', '/bar']: owner => blah }
resource_titles.flatten.map do |resource_title| # <-- line of iterator
exceptwrap :type => Puppet::ParseError do
...
if resource.resource_type.is_a? Puppet::Resource::Type
resource.resource_type.instantiate_resource(scope, resource)
end
scope.compiler.add_resource(scope, resource)
scope.compiler.evaluate_classes([resource_title], scope, false) if fully_qualified_type == 'class'
resource
end
end
So we see that the array of resources is iterated via the Array.map() method in Ruby. This converts the specific question component into "Does the Array.map() method in Ruby preserve ordering?". The specific type being iterated upon in the array is the title as specified in the type code when an instance is instantiated. This will typically be a symbol or a string, but the main point of emphasis here is that the iteration is over an array of simple types (i.e. not a nested hash etc.).
The answer to this new question component is also yes as specified in the docs or a simple yes in this linked answer.
Is this resource array ordering a documented supported behavior in Puppet?
No. You can check the relevant documentation here or here and see an absence of any mention of this behavior. Also, a former colleague directly asked Puppet about this a few years ago and their high-level response was the same as what I have outlined here thus far.
The code for the resource arrays in the AST can change at any time without warning in the documentation since this is unsupported behavior.
Should I depend on this unsupported but seemingly always existent behavior?
Your call. If this is for something like creating nested directories, then the automatic require code in Puppet will catch any issues with resources being applied out of order anyway. If not, then you probably need to do a quick cost/benefit calculation on code cleanliness versus potential undefined behavior, mitigated by a code lookup every time you upgrade, plus the time cost of said code lookup.
In terms of order of application, is it the same as the following?
file { '/some/file1':
ensure => file,
}
-> file { '/some/file2':
ensure => file,
}
Contrary to the claims of the other answer, NO, the two are not equivalent. Nor does the other answer demonstrate differently. What it shows is that in Puppet's current implementation, your array-style declaration is equivalent to this:
file { '/some/file1':
ensure => file,
}
# no chain operator here
file { '/some/file2':
ensure => file,
}
Since you specifically ask about order of application, the absence of any explicit relationship between the declared resources is significant.
Now, by default, in the absence of an explicit relationship chain between two resources, their relative order of application will be the same as their relative order of evaluation in the manifest set. But there are two important caveats there:
That's only a default. That default order-of-application rule can be changed via Puppet's 'ordering' setting. If it is, then your array-based resource declaration cannot be relied upon to produce the same order of application as your alternative using the chain operator.
Implicit relationships created by the default rule can be overridden or circumvented by explicit ones. If a contrary chain of one or more explicit relationships were declared in your manifest set, then Puppet would accept and honor it with the array-based declaration, but would reject it as a circular dependency in the case with an explicit chain operator between the resource declarations.
Where relative order of application matters, it is essential to declare explicit relationships. Array-titled resource declarations do not have such semantics, but you can, in general, add the relationships separately and still obtain the code clarity and brevity advantages of array titles:
file { ['/some/file1', '/some/file2']:
ensure => file,
}
File['/some/file1'] -> File['/some/file2']
There are two interesting answers above which are in disagreement. I will add my 2 cents here, as well as providing some more info that might be interesting to the OP.
The OP has asked in general if in terms of order of application:
file { ['/some/file1', '/some/file2']:
ensure => file,
}
is equivalent to:
file { '/some/file1':
ensure => file,
}
-> file { '/some/file2':
ensure => file,
}
In general, the answer is "no", as John says.
Here's a simple demonstration to prove that:
Puppet version:
$ bundle exec puppet -V
5.3.3
My code:
# code.pp
include stdlib
$files = range(1, 10).map |$x| { "/tmp/file${x}" }
notice("Files array is: $files")
file { $files:
ensure => file,
}
Apply with --ordering=random:
$ bundle exec puppet apply --ordering=random code.pp
Notice: Scope(Class[main]): Files array is: [/tmp/file1, /tmp/file2, /tmp/file3, /tmp/file4, /tmp/file5, /tmp/file6, /tmp/file7, /tmp/file8, /tmp/file9, /tmp/file10]
Notice: Compiled catalog for alexs-macbook-pro.local in environment production in 0.05 seconds
Notice: /Stage[main]/Main/File[/tmp/file3]/ensure: created
Notice: /Stage[main]/Main/File[/tmp/file9]/ensure: created
Notice: /Stage[main]/Main/File[/tmp/file8]/ensure: created
Notice: /Stage[main]/Main/File[/tmp/file7]/ensure: created
Notice: /Stage[main]/Main/File[/tmp/file1]/ensure: created
Notice: /Stage[main]/Main/File[/tmp/file6]/ensure: created
Notice: /Stage[main]/Main/File[/tmp/file2]/ensure: created
Notice: /Stage[main]/Main/File[/tmp/file4]/ensure: created
Notice: /Stage[main]/Main/File[/tmp/file10]/ensure: created
Notice: /Stage[main]/Main/File[/tmp/file5]/ensure: created
Notice: Applied catalog in 0.06 seconds
Thus, without an explicit relationship declaration, Puppet's random ordering setting sees the resources ordered randomly. If we had explicit orderings declared in the manifest however:
# code.pp
file { '/tmp/file1':
ensure => file,
}
-> file { '/tmp/file2':
ensure => file,
}
The files are always ordered as we want them to be:
$ for i in {1..5} ; do rm -f /tmp/file* ; bundle exec puppet apply --ordering=random code.pp ; done
Notice: Compiled catalog for alexs-macbook-pro.local in environment production in 0.21 seconds
Notice: /Stage[main]/Main/File[/tmp/file1]/ensure: created
Notice: /Stage[main]/Main/File[/tmp/file2]/ensure: created
Notice: Finished catalog run in 0.02 seconds
Notice: Compiled catalog for alexs-macbook-pro.local in environment production in 0.20 seconds
Notice: /Stage[main]/Main/File[/tmp/file1]/ensure: created
Notice: /Stage[main]/Main/File[/tmp/file2]/ensure: created
Notice: Finished catalog run in 0.02 seconds
Notice: Compiled catalog for alexs-macbook-pro.local in environment production in 0.23 seconds
Notice: /Stage[main]/Main/File[/tmp/file1]/ensure: created
Notice: /Stage[main]/Main/File[/tmp/file2]/ensure: created
Notice: Finished catalog run in 0.02 seconds
Notice: Compiled catalog for alexs-macbook-pro.local in environment production in 0.22 seconds
Notice: /Stage[main]/Main/File[/tmp/file1]/ensure: created
Notice: /Stage[main]/Main/File[/tmp/file2]/ensure: created
Notice: Finished catalog run in 0.02 seconds
Notice: Compiled catalog for alexs-macbook-pro.local in environment production in 0.23 seconds
Notice: /Stage[main]/Main/File[/tmp/file1]/ensure: created
Notice: /Stage[main]/Main/File[/tmp/file2]/ensure: created
Notice: Finished catalog run in 0.02 seconds
We can also get pseudo-random ordering using Puppet's previous default title-hash ordering, where resources are ordered by a hash generated from the resource title:
$ bundle exec puppet apply --ordering=title-hash code.pp
Notice: Scope(Class[main]): Files array is: [/tmp/file1, /tmp/file2, /tmp/file3, /tmp/file4, /tmp/file5, /tmp/file6, /tmp/file7, /tmp/file8, /tmp/file9, /tmp/file10]
Notice: Compiled catalog for alexs-macbook-pro.local in environment production in 0.05 seconds
Notice: /Stage[main]/Main/File[/tmp/file3]/ensure: created
Notice: /Stage[main]/Main/File[/tmp/file6]/ensure: created
Notice: /Stage[main]/Main/File[/tmp/file8]/ensure: created
Notice: /Stage[main]/Main/File[/tmp/file1]/ensure: created
Notice: /Stage[main]/Main/File[/tmp/file4]/ensure: created
Notice: /Stage[main]/Main/File[/tmp/file2]/ensure: created
Notice: /Stage[main]/Main/File[/tmp/file7]/ensure: created
Notice: /Stage[main]/Main/File[/tmp/file10]/ensure: created
Notice: /Stage[main]/Main/File[/tmp/file5]/ensure: created
Notice: /Stage[main]/Main/File[/tmp/file9]/ensure: created
Notice: Applied catalog in 0.06 seconds
But if we switch to manifest ordering we get the files ordered numerically again:
$ bundle exec puppet apply --ordering=manifest code.pp
Notice: Scope(Class[main]): Files array is: [/tmp/file1, /tmp/file2, /tmp/file3, /tmp/file4, /tmp/file5, /tmp/file6, /tmp/file7, /tmp/file8, /tmp/file9, /tmp/file10]
Notice: Compiled catalog for alexs-macbook-pro.local in environment production in 0.05 seconds
Notice: /Stage[main]/Main/File[/tmp/file1]/ensure: created
Notice: /Stage[main]/Main/File[/tmp/file2]/ensure: created
Notice: /Stage[main]/Main/File[/tmp/file3]/ensure: created
Notice: /Stage[main]/Main/File[/tmp/file4]/ensure: created
Notice: /Stage[main]/Main/File[/tmp/file5]/ensure: created
Notice: /Stage[main]/Main/File[/tmp/file6]/ensure: created
Notice: /Stage[main]/Main/File[/tmp/file7]/ensure: created
Notice: /Stage[main]/Main/File[/tmp/file8]/ensure: created
Notice: /Stage[main]/Main/File[/tmp/file9]/ensure: created
Notice: /Stage[main]/Main/File[/tmp/file10]/ensure: created
Notice: Applied catalog in 0.07 seconds
I think as John said, Matt's answer is correct, but only assuming that the default ordering of "manifest" is used; and also assuming that none of the resources auto-require other resources.
See also Gary Larizza's post on ordering here.

Make fact available during puppet catalog compilation

I have two classes in puppet.
Here is the first one:
class nagios::is_production {
file { '/etc/puppetlabs/facter/':
ensure => directory,
}
file { '/etc/puppetlabs/facter/facts.d/':
ensure => directory,
}
file { '/etc/puppetlabs/facter/facts.d/production.txt':
ensure => file,
content => epp('nagios/production.epp')
}
}
This creates a custom fact (production=yes/no based on the node name)
This class on its own assigns the fact correctly.
The second class:
class nagios::client {
if $facts[production] =~ yes {
##nagios_host {"${::hostname}":
ensure => present,
address => $::ipaddress,
hostgroups => "production, all-servers",
notifications_enabled => $notifications_enabled,
use => 'generic-server',
}
} else {
##nagios_host {"${::hostname}":
ensure => present,
address => $::ipaddress,
hostgroups => "non-production, all-servers",
notifications_enabled => $notifications_enabled,
owner => root,
use => 'generic-server',
}
}
}
This creates the exported resource for the host and adds it to either the production/non-production hostgroup.
If the custom fact exists, the host gets created with the hostgroup correctly.
I created a 3rd class to pull in these 2 just to keep track of it a little easier for myself:
class nagios::agent {
Class['nagios::is_production']->Class['nagios::client']
include nagios::is_production
include nagios::client
}
This seems like it should make ::is_production run before ::client. When I include this class on the node for the puppet run, I get this error:
Error: Could not retrieve catalog from remote server: Error 500 on SERVER: Server Error: Evaluation Error: Left match operand must result in a String value. Got an Undef Value. at /etc/puppetlabs/code/environments/production/modules/nagios/manifests/client.pp:3:6 on node
So the fact seems like it's not getting set causing the export of the host to fail.
What am I missing?
Followup to answer
I am trying to do this:
if domain name contains 'something'
production=yes
else
production=no
Then in the nagios module,
if $facts[production] =~ yes
Assign to production host group.
Bash:
#!/bin/bash
if [[ $(hostname) =~ '512' ]] ; then
echo production=yes
else
echo production=no
fi
Id like to be able to use $facts[something] in here to make create other facts based off things like OS and IP.
I read here: Custom Facts Walkthrough
But I wasn't able to understand the custom facts load path as I didn't have that directory. I'm very new to puppet...
Also new to stack overflow... did I do this right?
Thanks
Facts are generated during pluginsync. Since you are trying to place the external fact during catalog execution, it is not available earlier during catalog compilation, which occurs after pluginsync.
You need to remove your nagios::production class and place your external fact directly in the module to take advantage of pluginsync. It should be located in your module structure like this:
nagios
|__facts.d
|__production.txt
The external fact will then be copied over during pluginsync and the fact will be generated. It will then be available later during catalog compilation. Facter will expect your production.txt to be key:value pairs too.
Check here for more information on properly using external facts: https://docs.puppet.com/facter/3.5/custom_facts.html#external-facts

Puppet: Dependency chain not getting executed in order

I have an issue wherein i am trying to set external facts and then copy a template file which gets populated with values from hiera yaml file. The template file is dependent on certain facts(such as the owner and group of the of the template file) which gets set by the external facts file. Below is the puppet code.
file {['/etc/facter/','/etc/facter/facts.d']:
ensure => directory,
owner => 'root',
group => 'root',
mode => '0755',
}
file {"/etc/facter/facts.d/domain_facts.sh":
ensure => present,
owner => 'root',
group => 'root',
mode => '0755',
source => $::hostname?{
'hostname1' => 'puppet:///modules/vfecare/hostname1.sh',
},
require => File['/etc/facter/','/etc/facter/facts.d'],
}
file {"/tmp/testing123":
ensure => present,
owner => "${::remoteuser}",
group => "${::remotegroup}",
content => template("vfecare/testscript.erb"),
require => File["/etc/facter/facts.d/domain_facts.sh"]
}
However during execution, i see that the template gets copied first to the puppet agent machine and since the template has some values that it needs from the external facts file, it cannot find and it throws error saying "Invalid owner and group value".
Below is the content of the external facts file
#!/bin/bash
echo "remoteuser=tempuser"
echo "remotegroup=tempuser"
Why does puppet seem to ignore the dependency cycle here?
Facts are collected by the agent at the very start of a Puppet run, before the catalog containing your file resources gets executed. It isn't possible to deploy an external fact during the run and use it like this as the facts will be missing.
Instead, you need to rely on Puppet's "pluginsync" mechanism that copies external facts from the master to the agent before it collects facts.
Move the vfecare/files/hostname1.sh fact file in the module to vfecare/facts.d/hostname1.sh, remove the file resources you have for /etc/facter and copying the external fact, then re-run the agent. It should first download the hostname1.sh fact file, then evaluate the /tmp/testing123 file resource correctly with the remoteuser/group values.
See the docs at Auto-download of agent-side plugins for more information.

Resources