Puppet. Using define with 2 variables - puppet

I have config file /home/ipeacocks/Dropbox/nscd/nscd.conf:
$ cat home/ipeacocks/Dropbox/nscd/nscd.conf
logfile /var/log/nscd.log
threads 4
max-threads 32
server-user nobody
stat-user somebody
debug-level 0
reload-count 5
paranoia no
restart-interval 3600
With puppet I want to change 2 lines:
server-user nobody
paranoia no
To these lines:
server-user nscd
paranoia yes
So for changing one first line I can use such manifest:
include nscd
class nscd {
define line_replace ($line, $match) {
file_line {'some useful info':
path => '/home/ipeacocks/Dropbox/nscd/nscd.conf',
line => $line,
match => $match
}
}
anchor{'nscd::begin':}
->
package { 'nscd':
ensure => installed,
}
->
line_replace {'test':
line => "server-user nscd",
match => "^\s*server-user.*$"
}
->
service { 'nscd':
ensure => running,
enable => "true",
}
->
anchor{'nscd::end':}
}
Puppet launch:
» sudo puppet apply /home/ipeacocks/Dropbox/nscd/nscd.pp
Notice: Compiled catalog for softserve-pc.ddns.softservecom.com in environment production in 0.37 seconds
Notice: /Stage[main]/Nscd/Nscd::Line_replace[test]/File_line[some useful info]/ensure: created
Notice: Finished catalog run in 0.22 seconds
But cant when 2 lines (using declared function twice):
include nscd
class nscd {
define line_replace ($line, $match) {
file_line {'some useful info':
path => '/home/ipeacocks/Dropbox/nscd/nscd.conf',
line => $line,
match => $match
}
}
anchor{'nscd::begin':}
->
package { 'nscd':
ensure => installed,
}
->
line_replace {'test':
line => "server-user nscd",
match => "^\s*server-user.*$"
}
->
line_replace {'test2':
line => "paranoia yes",
match => "^\s*paranoia.*$"
}
->
service { 'nscd':
ensure => running,
enable => "true",
}
->
anchor{'nscd::end':}
}
Launching again:
» sudo puppet apply /home/ipeacocks/Dropbox/nscd/nscd.pp 1 ↵
Error: Duplicate declaration: File_line[some useful info] is already declared in file /home/ipeacocks/Dropbox/nscd/nscd.pp:10; cannot redeclare at /home/ipeacocks/Dropbox/nscd/nscd.pp:10 on node softserve-pc.ddns.softservecom.com
Error: Duplicate declaration: File_line[some useful info] is already declared in file /home/ipeacocks/Dropbox/nscd/nscd.pp:10; cannot redeclare at /home/ipeacocks/Dropbox/nscd/nscd.pp:10 on node softserve-pc.ddns.softservecom.com
What can be wrong? Is it possible to pass two pairs of vars to declared function at once (with arrays or like that)?
I have tried this solution but it doesn't work for me:
https://stackoverflow.com/a/19034077/2971192

Change the 'some useful info' to $name in file_line -
define line_replace ($line, $match) {
file_line {$name:
path => '/home/ipeacocks/Dropbox/nscd/nscd.conf',
line => $line,
match => $match
}
}
The problem you are facing is because the second call to line_replace causes call to file_line with resource name 'some useful info' which is already declared.

Replace your define with this:
define line_replace ($line, $match) {
file_line {$name:
path => '/home/ipeacocks/Dropbox/nscd/nscd.conf',
line => $line,
match => $match
}
}
I changes the file_line resource name from a constant to the $name parameter of your define.

Related

Using facts inside a module to push data to file

I'm trying to create a simple module that will use facts from the agent to push the relevant output to file..
I've already managed to do it in one module but for an unknown reason it doesn't work as expected..
this is what I did
class testrepo {
case $facts['os']['family'] {
'RedHat': {
file_line { 'dscrp to local repo file':
path => '/etc/yum.repos.d/test.repo',
line => "name=${::description}",
ensure => present,
}
file_line { 'repo from agent':
path => '/etc/yum.repos.d/test.repo',
line => "baseurl=file:///usr/local/src/RHEL/RHEL-${::full}-${::architecture}",
ensure => present,
}
in the first file_line the output in file is "name=". and in the second file_line it doesn't translate the ${::full} but I get the ${::architecture}
file_line { 'Add fdqn to /etc/hosts':
path => '/etc/hosts',
line => "${::ipaddress} ${::fqdn} ${::hostname}",
ensure => present,
}
the above is working as expected
right now I'm not sure which direction should I check
I've tried $facts['os']['familiy']['full'] , it also doesn't work
could anyone give me some advice here
thank you
Architecture, fqdn and ipaddress are all facts available at the top level, if you jump onto the target node and run facter architecture you'll get an answer;
[root#example ~]# facter ipaddress
10.10.10.110
[root#example ~]# facter architecture
x86_64
"full" is part of the OS nested fact:
[root#example ~]# facter full
[root#r2h-bg5ore5nix0 ~]# facter os
{
architecture => "x86_64",
family => "RedHat",
hardware => "x86_64",
name => "CentOS",
release => {
full => "7.7.1908",
major => "7",
minor => "7"
},
selinux => {
config_mode => "enforcing",
config_policy => "targeted",
current_mode => "enforcing",
enabled => true,
enforced => true,
policy_version => "31"
}
}
So you'll have to drill down through the os facts hash to do that, on the command line that's;
[root#example ~]# facter os.release.full
7.7.1908
In code you can experiment with;
notify { 'message':
message => "message is ${::os['release']['full']}",
}
or
notify { 'message':
message => "message is ${::facts['os']['release']['full']}",
}
So what you're going to need to do in your code is use:
line => "baseurl=file:///usr/local/src/RHEL/RHEL-${::os['release']['full']}-${::architecture}",

Containing a defined resource type in Puppet

I'm trying to create an instance of a defined resource type (::apt::ppa) that comes before other resources. I am using the PuppetLabs Apt Module.
When adding a new repository via the module, the defined type contains an exec statement that notifies apt::update so that any packages that might be required can be installed correctly. However, when I run my below code, the notify gets scheduled after I attempt to install Java, thereby causing the Java install to fail. I've tried putting anchors around the apt::ppa declaration, but that doesn't help. What else can I do?
class rap::java(
$version = '7',
) {
$package = "oracle-java${version}-installer"
apt::ppa { 'ppa:webupd8team/java': } ->
exec { 'accept-java-license':
command => "/bin/echo ${package} shared/accepted-oracle-license-v1-1 select true | /usr/bin/sudo /usr/bin/debconf-set-selections",
unless => "/usr/bin/debconf-show ${package} | grep 'shared/accepted-oracle-license-v1-1: true'",
} ->
class { '::java':
package => $package,
distribution => 'oracle-jre',
}
file_line { 'java_environment':
path => '/etc/environment',
line => "JAVA_HOME=\"/usr/lib/jvm/java-${version}-oracle\"",
}
}
I believe the issue is that you need to include the apt class within the class you've made to get the ordering right.
This works for me on a new Precise box:
class rap::java(
$version = '7',
) {
$package = "oracle-java${version}-installer"
include apt
apt::ppa { 'ppa:webupd8team/java':
package_manage => true,
}
exec { 'accept-java-license':
command => "/bin/echo ${package} shared/accepted-oracle-license-v1-1 select true | /usr/bin/sudo /usr/bin/debconf-set-selections",
unless => "/usr/bin/debconf-show ${package} | grep 'shared/accepted-oracle-license-v1-1: true'",
}
class { '::java':
package => $package,
distribution => 'oracle-jre',
require => [
Apt::Ppa['ppa:webupd8team/java'],
Exec["accept-java-license"],
]
}
file_line { 'java_environment':
path => '/etc/environment',
line => "JAVA_HOME=\"/usr/lib/jvm/java-${version}-oracle\"",
}
}
Log of run:
Notice: Compiled catalog for precise64 in environment production in 0.78 seconds
Notice: /Stage[main]/Apt/File[preferences]/ensure: created
Notice: /Stage[main]/Rap::Java/Exec[accept-java-license]/returns: executed successfully
Notice: /Stage[main]/Rap::Java/File_line[java_environment]/ensure: created
Notice: /Stage[main]/Apt/Apt::Setting[conf-update-stamp]/File[/etc/apt/apt.conf.d/15update-stamp]/ensure: defined content as '{md5}0962d70c4ec78bbfa6f3544ae0c41974'
Notice: /Stage[main]/Rap::Java/Apt::Ppa[ppa:webupd8team/java]/Package[python-software-properties]/ensure: created
Notice: /Stage[main]/Rap::Java/Apt::Ppa[ppa:webupd8team/java]/Exec[add-apt-repository-ppa:webupd8team/java]/returns: executed successfully
Notice: /Stage[main]/Apt::Update/Exec[apt_update]: Triggered 'refresh' from 1 events
Notice: /Stage[main]/Java/Package[java-common]/ensure: created
Notice: /Stage[main]/Java/Package[java]/ensure: created
Notice: Applied catalog in 39.58 seconds
To extend the question further, generally things that are blockers for a standard setup to run are usually moved into a run stage (documented here).
So I would probably move all of the various repo setup puppet code into pre run stage with other prerequisites (normally you put in repo setup), the run stage will always be run first before the main stage, so you don't have to worry about explictly setting requirements that repos are setup on each package. This makes making changes to repos and prerequisites a lot easier

fail when a file exist in puppet

I am trying to write a puppet script which will install a module by un-tar. I want puppet to fail if it is already un tar. I tried to do below code but it always fails even if directory is absent.
I am checking if /opt/sk is present then fail otherwise proceed on installation.
define splunk::fail($target)
{
$no = 'true'
case $no {
default : { notice($no) }#fail('sk is already installed.')}
}
}
define splunk::forwarder( $filename , $target )
{
file{"$target/sk":
ensure => present
}
splunk::fail{"NO":
target => '/opt/',
require => File[$target],
}
file{"$target/A.tgz":
source => $filename ,
replace => false ,
}
exec{"NO1":
command => "tar xzvf A.tgz" ,
cwd => $target ,
require => File["$target/A.tgz"] ,
}
exec{"Clean":
command => "rm -rf A.tgz" ,
cwd => target ,
require => Exec["NO1"],
}
}
splunk::forwarder {"non":
filename => 'puppet:///modules/splunk/files/NO.tgz' ,
target => '/opt/',
}
Thanks
Define custom_fact and use it combined with fail resource.
In your ruby directory e.g /usr/lib/ruby/vendor_ruby/facter define file tmp_exist.rb with content:
# tmp_exist.rb
Facter.add('tmp_exist') do
setcode do
File.exist? '/root/tmp'
end
end
Next use it in puppet manifest. E.g I combined it with str2bool function from stdlib:
class test {
if !str2bool($::tmp_exist) {
fail('TMP NOT EXIST')
}
if !str2bool($::foo_exist) {
fail('FOO NOT EXIST')
}
}
include test
In /root create only tmp file.
In result you will have:
Error: FOO NOT EXIST at /etc/puppet/deploy/tests/test.pp:8 on node dbmaster
UPDATED: I updated my answer. Chris Pitman was right, my previous solution works only on puppet master or with puppet apply.
I have also found an article describing how to define custom function file_exists in puppet. That also might be helpful.
You should use "creates" attribute of exec, for example:
exec { 'install':
command => "tar zxf ${package}",
cwd => $some_location,
path => $path,
creates => "${some_location}/my_package",
}
Puppet will only execute 'install' if "${some_location}/my_package" doesn't exist.

validate_cmd in Puppet: supporting older versions

I have the following Puppet code:
file { "/etc/sudoers.d/${name}":
content => template('sudo/sudoers.erb'),
owner => 'root',
group => 'root'
mode => '0440',
validate_cmd => '/usr/sbin/visudo -c -f %',
}
However, validate_cmd was only added in Puppet > 3.5, and I have to support some systems running 3.2 and older.
Is there some clever way of monkey-patching this into older versions of Puppet, or should I just resign to using the puppetlabs-stdlib validate_cmd statement?
Which does a similar thing, but won't revert the file if it does not validate correctly (the main benefit of the validate_cmd parameter on a fle).
EDIT: Using Felix's overwrite syntax, I ended up with the following code:
file { "/etc/sudoers.d/${name}":
content => template('sudo/sudoers.erb'),
owner => 'root',
group => 'root',
mode => '0440',
}
if versioncmp($::puppetversion, '3.5') >= 0 {
File["/etc/sudoers.d/${name}"] { validate_cmd => '/usr/sbin/visudo -c -f %' }
}
else {
validate_cmd(template('sudo/sudoers.erb'), '/usr/sbin/visudo -c -f', 'Visudo failed to validate sudoers content')
}
It's a bit fiddly to test in puppet-rspec, I ended up going with this:
if (Puppet.version >= '3.5.0')
context "validating content with puppet #{Puppet.version}" do
let(:params) { { :users => ['joe'] } }
let(:facts) {{ :puppetversion => Puppet.version }}
it { should contain_file('/etc/sudoers.d/worlddomination').with_validate_cmd('/usr/sbin/visudo -c -f %') }
end
else
context "validating content with puppet #{Puppet.version}" do
let(:params) { { :users => ['joe'] } }
let(:facts) {{ :puppetversion => Puppet.version }}
it { should contain_file('/etc/sudoers.d/worlddomination').with_validate_cmd(nil) }
end
end
Your manifest can adjust its behavior to the agent version.
file { "/etc/sudoers.d/${name}":
content => template('sudo/sudoers.erb'),
owner => 'root',
group => 'root'
mode => '0440',
}
if versioncmp($puppetversion, '3.5') >= 0 {
File["/etc/sudoers.d/${name}"] { validate_cmd => '/usr/sbin/visudo -c -f %' }
}
else {
# your workaround here
}
This will work courtesy of the puppetversion fact, of course.
The override syntax File[<name>] { ... } can be used like this because the actual resource declaration specifies no value for the validate_cmd attribute.

puppet: Syntax error at 'target'; expected '}' - parsing error

Syntax error while parsing puppet resource.
class nagios::export {
##nagios_host { $::fqdn:
address => $::ipaddress,
use => "linux-server",
check_command => 'check-host-alive!3000.0,80%!5000.0,100%!10',
hostgroups => 'all-servers',
target => "/etc/nagios/resource.d/host_${::fqdn}.cfg"
}
##nagios_service { "check_ping_${hostname}":
check_command => "check-host-alive!100.0,20%!500.0,60%",
use => "generic-service",
host_name => "$fqdn",
notification_period => "24x7",
#target => "/etc/nagios/resource.d/service_${::fqdn}.cfg"
service_description => "${hostname}_check_ping"
target => "/etc/nagios/resource.d/service_${::fqdn}.cfg"
}
}
When I run puppet apply , following error is seen..
[root#ip-10-172-161-25 manifests]# puppet apply export.pp --noop
Could not parse for environment production: Syntax error at 'target'; expected '}' at /etc/puppet/modules/nagios/manifests/export.pp:28 on node ip-10-172-161-25.us-west-1.compute.internal
class nagios::export {
##nagios_host { $::fqdn:
address => $::ipaddress,
use => "linux-server",
check_command => 'check-host-alive!3000.0,80%!5000.0,100%!10',
hostgroups => 'all-servers',
target => "/etc/nagios/resource.d/host_${::fqdn}.cfg",
}
##nagios_service { "check_ping_${hostname}":
check_command => "check-host-alive!100.0,20%!500.0,60%",
use => "generic-service",
host_name => "$fqdn",
notification_period => "24x7",
#target => "/etc/nagios/resource.d/service_${::fqdn}.cfg",
service_description => "${hostname}_check_ping",
target => "/etc/nagios/resource.d/service_${::fqdn}.cfg",
}
}
Was just a few missing commas at the end.
I generally always finish with a comma on the last line. It's not needed, but catches gotcha's that happen when you add an extra line to the end.

Resources