Reading from file /etc/resolv.conf and populating in named.conf.options - puppet

I'm using puppet to generate my named.conf.options file, in order to do this I'd like it to use the forwarders defined in /etc/resolv.conf. What's the best way of doing this, I've been doing it like this (where named.conf.options.erb contains ) - but this runs constantly.
file { '/etc/bind/named.conf.options':
ensure => present,
content => template('my_template_location/named.conf.options.erb'),
replace => true,
}
->
exec { "add_nameserver":
command => '/bin/sed -i "s/<name_server>/$(grep nameserver/etc/resolv.conf | tr -d [a-z])/g" /etc/bind/named.conf.options',
}

An exec will always run unless it has something to limit it. There are a number of parameters you can set.
In your case, it sounds like you want the exec to run only when your file changes. You might want to use the refreshonly parameter on your exec.
First, change the require arrow to a notify arrow, from -> to ~>. This will cause puppet to refresh the exec whenever the file changes.
Second, add refreshonly => true to your exec. This will cause the exec to only run when it is refreshed by some other resource.
You'll end up with the following:
file { '/etc/bind/named.conf.options':
ensure => present,
content => template('my_template_location/named.conf.options.erb'),
replace => true,
}
~>
exec { "add_nameserver":
command => '/bin/sed -i "s/<name_server>/$(grep nameserver/etc/resolv.conf | tr -d [a-z])/g" /etc/bind/named.conf.options',
refreshonly => true,
}
You can check out some of the other ways to limit an exec on the Puppet Type Reference Page.

You can not get desired state this way because you are modifying the same resource (file /etc/bind/named.conf.options) with two different declarations.
Normally you have to avoid exec resources in Puppet because is difficult to keep state and idempotence while executing "old-school" commands.
So, the best way to get your desired behaviour is to create a custom fact [1] that exposes your nameservers to any resource and then include it in your ERB template.
Facter.add(:nameservers_array) do
setcode do
nameservers = Facter::Core::Execution.exec('grep nameserver/etc/resolv.conf | tr -d [a-z]')
nameservers_array = nameservers.split(" ")
nameservers_array
end
end
You have another example here: https://www.jethrocarr.com/2013/11/05/exposing-name-servers-with-puppet-facts/
[1] https://docs.puppetlabs.com/facter/latest/fact_overview.html

Related

Manage multiple files permissions using puppet

How do I set file permission from list of file names from cat command?
For example, below command returns 3 file names:
$ cat /tmp/test | grep file
/etc/systemd/file_1.log
/etc/systemd/file_2.log
/etc/systemd/file_3.log
How do I use puppet to run the command, get the file names and then loop the 3 file names and set permission accordingly?
The files are resources and if you want to manage a resource you have to know it's there so dynamically created log files are not easy. If you know the file names already then you can use something like this and pass an array into the file resource.
file { ['/etc/systemd/file_1.log',
'/etc/systemd/file_1.log',
'/etc/systemd/file_1.log'] :
ensure => 'file',
mode => '0644',
owner => 'root',
group => 'root',
}
An other method might be to use an exec
exec { 'chmod 644 /etc/systemd/file_*.log':
path => ['/usr/bin', '/usr/sbin',],
}
But you really need something like an onlyif or unless or this is going to execute every 30 minutes and that breaks the idempotent rule we try and apply with Puppet code where things only change if they need correcting. So you're going to need a command line that'll test the permissions and return a boolean to the onlyif.
There are more details here https://puppet.com/docs/puppet/5.5/types/exec.html
A alternative (and the way I'd do it) would be to expose the contents of that file via an external fact which passes the list of files to Puppet to use in the catalog compilation. An external fact can be a bash script so I'd create a file called /etc/facter/facts.d/logfiles.sh, obviously I'd deploy this using Puppet.
#!/usr/bin/env bash
logfiles=($(grep file /tmp/test))
echo "logfiles=${logfiles[*]}"
Then in my Puppet code I'd have something like this;
$logfiles.each |String $logfile| {
file { $logfile :
ensure => 'file',
mode => '0644',
owner => 'root',
group => 'root',
}
}
So when the Puppet run happens the list of log files will be returned to Puppet via the facts and then each file listed is defined as a resource with the correct permissions.
How do I set file permission from list of file names from cat command?
There are two main alternatives, but I observe first that your example is of the output from grep, not cat, and that the cat in that example is superfluous. Nevertheless, those details don't change the big picture -- substantially the same approaches are applicable for data output by any command.
It would be more idiomatic to write a custom fact that captures the filenames (as of the time of each catalog request), and use that information to create the appropriate File resources.
Custom facts are not that hard, but the full details are more than would be appropriate for an SO answer. Supposing that you have a fact $facts['systemd_logs'] whose value is an array of the absolute filenames, you can compactly express the whole group of wanted File resources like so:
file { $facts['systemd_logs']:
mode => '0644',
}
(or whatever mode it is that you want).
It would be quicker (and dirtier) to use an Exec resource to run an appropriate command:
exec { 'ensure correct file permissions':
command => 'chmod 0644 $(/bin/grep file /tmp/test)',
onlyif => '/bin/grep -q file /tmp/test',
provider => 'shell',
}

Puppet: 'creates' does not create a new file

I would like to run a command, only if a file does not exist.
This is what I tried:
exec { 'test':
command => '/usr/bin/echo Test',
creates => '/etc/test/test-init'
}
But unfortunately this file is not being created. The puppet agent works without throwing an error.
The creates parameter of an Exec does not cause Puppet to create the designated file. Rather it instructs Puppet to use the existence of that file to determine whether the command should be run, on the supposition that the command will create it when it runs.
That's often used with commands that naturally create a file or directory, but you can do it synthetically, too. For example:
exec { 'test':
command => '/usr/bin/echo Test && touch /etc/test/test-init',
creates => '/etc/test/test-init',
provider => 'shell'
}

Copying a file only once in puppet

I am new to puppet, I am trying to copy a file to remote server for installation of a package, This file would get deleted a few minutes after installation. Is there a way to copy this file only once.? like the example below.
class absent_file {
file { '/tmp/hello-file':
ensure => 'present',
replace => 'no',
content => "From Puppet\n",
mode => '0644',
once => true
}
}
So, I have figured out how to do it. Instead of using the file module, I ended up using command and touching another which would not get deleted along with installation process. The key can be deleted but the somefile.txt would be still present and the process would become idempotent.
exec {'add key':
command => "echo 'SharedKeyfile' > /key/location && touch /key/location/somefile.txt'",
onlyif => "test ! -f /key/location/somefile.txt",
}

How to ensure Service is restarted before commands from Exec resource execute?

(Update) I left out an important line of code in my original question.
I am write a puppet module that will create local storage. The un-automated way of does this is.
Edit the /etc/multipath.conf file
Restart the multipathd daemon
Execute pvcreate $device
Execute vgcreate $volume_name $device
So in my puppet module I want to make sure the multipathd daemon gets restarted if the /etc/multipath.conf file changes. So I wrote my puppet manifest this way.
Service['multipathd'] -> Anchor['create_storage_volume::begin']
...
file { '/etc/multipath.conf':
ensure => file,
content => template( 'local_storage::hadoop/multipath.conf.erb' ),
owner => 'root',
mode => '0600',
notify => Service['multipathd'],
} ->
service { 'multipathd':
enable => true,
ensure => running,
}
anchor { 'create_storage_volume::begin': } ->
exec { "pvcreate ${device}":
path => ['/usr/sbin', '/bin'],
unless => "pvs | grep ${"volume_name},
} ->
exec { "vgcreate ${volume_name} ${device}":
path => ['/usr/sbin', '/bin'],
unless => "pvs | grep ${"volume_name},
} -> # -> do nova config stuff
anchor { 'create_storage_volume::end': }
...
My question is, "does the above code guarantee that the multipathd daemon gets restarted before the pvcreate and vgcreate commands are executed"? Do I need to add more resource order with something like ...
Service['multipathd'] -> Anchor['local_storage::begin']
?
My question is, "does the above code guarantee that the multipathd daemon gets restarted before the pvcreate and vgcreate commands are executed"?
No. Both Service['multipathd'] and Exec["pvcreate ${device}"] will be applied after File['/etc/multipath.conf'], but nothing in what you've presented the relative order of application of the Service and the Exec.
I'd probably write it like this, instead:
file { '/etc/multipath.conf':
ensure => file,
content => template( 'local_storage::hadoop/multipath.conf.erb' ),
owner => 'root',
mode => '0600',
} ~>
Service['multipathd'] ->
exec { "pvcreate ${device}":
path => ['/usr/sbin', '/bin'],
unless => "pvs | grep ${"volume_name},
} ->
# ...
Note the use of the notifying chain operator; this is a less-used feature of the Puppet language.
With respect to the update to the question, the key requirement for the Service to be refreshed before the Exec is applied is that there be an ordering relationship between the two, whether direct or transitive. In the modified question, the resources involved have such a relationship already.
The original answer does gloss over a fine point here, which I called out in comments: Puppet does not actually document explicitly when resource refreshes happen relative to anything else. The documented semantics have some implications for that, but no firm rule is given. In practice, through Puppet 4, if a resource refresh happens, it will happen immediately after that resource is synced -- or immediately after it would have been synced if it had needed to be.

Uncomment a line in Puppet

I'm getting into Puppet, and loving it, but am hitting my head against a wall with one small thing which I'm sure must be easier than I'm making it.
I wish to uncomment this line in the .bashrc of a user:
#force_color_prompt=yes
I've been using augeas for lots of things, but it seems as though that won't work with this.
I note that there's a file_line resource which I can use to ensure a line is present in a file, but I need the line to remain in the same place.
I don't wish to replace the .bashrc entirely with my own copy, despite seeing that this pattern is popular in Puppet, it just doesn't make sense to me as I don't want to maintain my own version between upgrades.
In sed I use this:
sed -i "s/#force_color_prompt=yes/force_color_prompt=yes/g" ~/.bashrc
Any ideas?
You could just add your sed command to an exec with an unless option using grep. Something like this (untested):
exec { 'force_color_prompt':
command => 'sed -i "s/#force_color_prompt=yes/force_color_prompt=yes/g" .bashrc'
cwd => '/home/user',
shell => true,
unless => 'grep -Fxq "force_color_prompt=yes" .bashrc',
}
Or use the file_line resource you mentioned (part of the Puppet Labs stdlib module I believe) to add the line uncommented and leave the commented line untouched.
In my experience, file_line will replace a line that matches the regex.
So to get force_color_prompt=yes vs. #force_color_prompt=yes you could:
file_line {
'color_prompt':
path => '/home/<youruser>/.bashrc',
match => 'force_color_prompt',
line => "force_color_prompt=yes",
}
Importantly, if the regex matches, the replace happens in place, ie. it will change the line, not the location of the line. If the regex does not match however, the line will be appended to the end of the file.
This experience is with Puppet 3.1.
Reference: https://forge.puppetlabs.com/puppetlabs/stdlib
The shellvar already can uncomment lines
shellvar { '.bashrc':
ensure => present,
target => "/home/${your_user}/.bashrc",
variable => 'force_color_prompt'
uncomment => true,
}
I perceive this much cleaner than using exec and calling sed.
While Augeas alone cannot do it, the shellvar provider from Augeasproviders will be able to do it, whenever this PR is merged.

Resources