how to ensure puppet exec run on every event triggered? - puppet

I want puppet exec to run on first deployment of a file and on every further change on the file. For that I have created below code in puppet .
Exec does not run on first file deployment. I am assuming it is because of refreshonly. Does anyone know what needs to be changed in the code?
define dev_tools::javacert(
$keystore="/etc/alternatives/java_sdk/jre/lib/security/cacerts",
$storepass='xxx',
$alias = $name,
$filecertpath = "/var/lib/certs/${name}.crt",
){
file{
$filecertpath:
source => "puppet:///modules/${module_name}/sonarqube/${::env}/${::server_location}/${filecertpath}",
mode => '0644',
notify => Exec["deploy_javacert_${name}"];
}
exec {
"deploy_javacert_${name}":
path => "/usr/bin",
command => "keytool -importcert -keystore ${keystore} -alias $alias -file $filecertpath -storepass ${storepass} -noprompt 2>/dev/null",
provider => shell,
refreshonly => true;
}
}

The use of refreshonly looks correct. I am unable to reproduce this. Using this simplified version of your code:
Attempt to create a MCVE:
# test.pp
file { 'foo':
path => '/tmp/foo',
source => '/tmp/source',
notify => Exec['bar'],
}
exec { 'bar':
path => '/bin',
command => 'echo "baz qux"',
refreshonly => true,
logoutput => true,
}
Setup:
▶ touch /tmp/source
Initial run:
▶ puppet apply test.pp
...
Notice: /Stage[main]/Main/File[foo]/content: content changed '{md5}0a227d644d5435d49addae1da06e909c' to '{md5}d41d8cd98f00b204e9800998ecf8427e'
Notice: /Stage[main]/Main/Exec[bar]/returns: baz qux
Notice: /Stage[main]/Main/Exec[bar]: Triggered 'refresh' from 1 event
Subsequent run:
▶ puppet apply test.pp
...
Notice: Compiled catalog for 192-168-1-2.tpgi.com.au in environment production in 0.08 seconds
Notice: Applied catalog in 0.03 seconds
New content:
▶ echo foobar > /tmp/source
▶ puppet apply test.pp
...
Notice: /Stage[main]/Main/File[foo]/content: content changed '{md5}d41d8cd98f00b204e9800998ecf8427e' to '{md5}14758f1afd44c09b7992073ccf00b43d'
Notice: /Stage[main]/Main/Exec[bar]/returns: baz qux

Related

How to pass multiple values in exec command resource in puppet?

I want to deploy certs in default keystore using puppet
I have two files root.crt and intermediateca.crt managed by puppet. I will have many alias and cert file in future
Alias could be rootca, intermediateca.This alias and filepath should be passed to exec command.
alias=rootca, intermediateca
filecert= "/var/lib/certs/rootca", "/var/lib/certs/intermediateca.crt"
Now I am running an exec command which takes alias and crt file as an argument in exec command resource.
'/var/lib/certs':
ensure => directory;
'/var/lib/certs/root.crt':
source => "puppet:///modules/${module_name}/sonarqube/${::env}/var/lib/certs/root.crt",
mode => '0644',
notify => Exec['markitrootca'];
'/var/lib/certs/intermediateca.crt':
source => "puppet:///modules/${module_name}/sonarqube/${::env}/var/lib/certs/intermediateca.crt",
mode => '0644',
notify => Exec['markitintermediateca'];
}
exec {
'markitrootca':
path => "/usr/bin",
command => "keytool -importcert -keystore ${keystore} -alias markitrootca -file /var/lib/certs/root.crt -storepass ${storepass} -noprompt 2>/dev/null",
provider => shell,
refreshonly => true;
'markitintermediateca':
path => "/usr/bin",
command => "keytool -importcert -keystore ${keystore} -alias markitintermediateca -file /var/lib/certs/intermediateca.crt/ -storepass ${storepass} -noprompt 2>/dev/null",
provider => shell,
refreshonly => true;
}
The above solution is working fine but I want to write only one exec for multiple alias and filepaths. How can i achieve that?
What it sounds like you are asking here is how to iterate over a hash and have each resource in the iteration notify an exec resource. Given that, you can use a lambda iterator on the hash. If this is not true, then please clarify the question with terminology and details. First you construct the hash with your keys:
$certs = { 'rootca' => '/var/lib/certs/rootca',
'intermediateca' => '/var/lib/certs/intermediateca.crt' }
Then you can iterate over the hash with the each method.
$certs.each |String $cert, String $loc| {
# code here
}
Populating the body of the lambda would look like this for your situation:
$certs.each |String $cert, String $loc| {
file { $loc:
source => "puppet:///modules/${module_name}/sonarqube/${::env}${loc}",
mode => '0644',
notify => Exec['install_cert'];
}
}
Note that in your question it looked like you were doing per-resource default attributes, but it is unclear since the rest of the body is missing, so I converted it to a conventional resource.
The entire body of code would look like:
$certs = { 'rootca' => '/var/lib/certs/rootca',
'intermediateca' => '/var/lib/certs/intermediateca.crt' }
$certs.each |String $cert, String $loc| {
file { $loc:
source => "puppet:///modules/${module_name}/sonarqube/${::env}${loc}",
mode => '0644',
notify => Exec['install_cert'];
}
}
exec { 'install_cert':
path => "/usr/bin",
command => "keytool -importcert -keystore ${keystore} -alias $alias -storepass ${storepass} -noprompt -trustcacerts",
provider => linux,
subscribe => File['/var/lib/certs/'],
refreshonly => true;
}
Since you did not mention in the question what keystore and storepass are, I will have to assume you are defining them elsewhere. Also you are subscribing to File['/var/lib/certs/'] which I also have to assume is somewhere else.
There are other improvements you can make here, including:
Not specifying the provider in the exec.
Not using the path in the exec since it is a string and not an array of search paths and instead providing the full path to keytool.
Not subscribing to File['/var/lib/certs/'] since the exec does not care about that directory and the notify are providing the necessary functionality.
Not specifying the path to the certs since they are always in the same location, so you can use a single array instead of a hash.
env looks like a fact and not a global variable, so it should be specified as such.
Not having a deep source for your file resources and instead just storing them inside the env directory.
Helpful documentation on lambda iterators can be found here.
There is a much better way to do this. you can simple define a hierayaml file with an array of objects/task or just write it directly in.
instead of code such as
# Run Start Script*
exec { 'run script':
command => "somecmdhere",
provider => shell,
cwd => 'dir',
refreshonly => false,
logoutput => true
}
exec { 'run installation script':
command => "${target_dir}/${start_service_script}",
provider => shell,
cwd => $target_dir,
refreshonly => false,
logoutput => $cmdlogoutput
}
you can use create resource with arguments TYPE and Objects to create
create_resources('package', )
in this case we would use
#goes and looks for yaml file with variable exec scripts. This can be done by also defining it in puppet.
$listofExecCommands = hiera_hash('execScripts::allscripts)
create_resources('exec', $listofExecCommands)
Then where ever you define your YAML file you can re-use it with no code changes.
you can use this for RPMS NPM packages its great! ps make sure your yaml is correct*
#RPM packages to be installed
execScripts::allscripts:
'script1':
command: 'installed'
provider: 'yum'
cwd: 'somedir'
'script2':
command: 'installed'
provider: 'yum'
cwd: 'somedir'

How to remove Cron job through puppet

I was wondering if there is any way that i can remove my cron which got created through puppet. It works fine when i add "ensure => absent" to my manifest. But the challenge for me is, I have to wait an hour to run my puppet agent on my clients since agent is configured to run on every hour. Also i have to manually edit each job to add "ensure => absent".
Is there any other way that i can remove the cron than adding `"ensure => absent"` to each cron jobs
for example, how can we remove "job1" from all applied servers without adding "ensure => absent"
class cron::my_cron
(
)
{
cron::hourly { 'Job1':
minute => '0',
user => 'root',
command => 'cmd',
environment => [ 'MAILTO=root', 'PATH="/usr/bin:/bin"', ],
}
cron::hourly { 'job2':
minute => '0,5,10,15,20,25,30,35,40,45,50,55',
user => 'root',
command => 'cmd',
environment => [ 'MAILTO=root', 'PATH="/usr/bin:/bin"', ],
}
}
Here is the work around I found.
On my puppet class I have created an array with all my active crons. Then I will pass those list to my bash script. My bash script will install and execute once puppet agent run on my clients.
On my bash script i will grep for all my crons which installed via puppet then i loop through each cron job and compare with my active cron array, If it does not match with my active cron list, will execute the rm command to remove the cron entry.
class cron (
$active_cron=['cron1', 'cron2', 'cron3', 'cron4')
)
{
file {
'/usr/sbin/remove_cron.sh':
ensure => present,
mode => 755,
owner => 'root',
group => 'root',
content => template('cron/remove_cron.erb'),
notify => Exec['remove_cron'],
}
exec { 'remove_cron':
command => "/usr/sbin/remove_cron.sh >> /var/log/remove_cron.log",
path => '/usr/local/bin/:/bin/:/usr/bin/',
require => File['/usr/sbin/remove_cron.sh'],
refreshonly => true,
}
}
My Bash script template
#!/bin/bash
LIST='<%= #active %>'
grep -il puppet* /etc/cron.d/* | grep -il puppet* /etc/cron.d/* | awk -F"/" '{print $NF}' |while read CRON
do
FOUNDIT=$(echo $LIST |grep "\"$CRON\"" |wc -l)
if [ $FOUNDIT -eq 0 ]
then
echo "$(date) : Cron $CRON Removed"
rm -r /etc/cron.d/$CRON
fi
done
You don't have to wait for 30 minutes for the agents to run
Do "mco runall 5" on the puppetmaster to provoke a puppet agent run on all hosts
Then you can use "ensure => absent" and not have to bother with an exec hack

conditions to chain bash scripts with puppet

I am new to puppet and I have two questions. I want to execute 2 successive custom bash scripts:
file{ 'deploy_0':
ensure => 'file',
path => '/home/user_name/scripts/deploy_0.sh',
...
notify => Exec['deploy_core']
}
file{ 'deploy_1':
ensure => 'file',
path => '/home/user_name/scripts/deploy_1.sh',
...
notify => Exec['deploy_core_api']
}
exec { 'deploy_core':
command => '/bin/bash -c "/home/user_name/scripts/deploy_0"',
}
exec { 'deploy_core_api':
command => '/bin/bash -c "/home/user_name/scripts/deploy_1.sh"',
onlyif => 'deploy_core'
}
But this does not work
I know I can put for the onlyif paramter a bash command such as /bin/bash -c "/home/user_name/scripts/deploy_0.sh, but I prefer to declare a file resource.
You used the notify metaparameters correctly and well to specify the scripts needed to be deployed before execution (file before corresponding exec) and should be executed again if the file content changes. You need similar metaparameters for application order on the exec resources if you want similar functionality there. Note that onlyif is an exec attribute that executes a local command on the client and causes the resource to be considered already in sync (not applied due to idempotence) during catalog application if it returns something falsey.
Since you do not need refreshing here from one exec to the other like you did with the file resource, we can use require or before instead.
# before
exec { 'deploy_core':
command => '/bin/bash -c "/home/user_name/scripts/deploy_0"',
before => File['deploy_core_api'],
}
exec { 'deploy_core_api':
command => '/bin/bash -c "/home/user_name/scripts/deploy_1.sh"',
}
# require
exec { 'deploy_core':
command => '/bin/bash -c "/home/user_name/scripts/deploy_0"',
}
exec { 'deploy_core_api':
command => '/bin/bash -c "/home/user_name/scripts/deploy_1.sh"',
require => File['deploy_core'],
}
This will give you the behavior you are looking for.

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

How can i install a local rpm using puppet

I am trying to install a particular rpm using puppet, my init.pp is:
class nmap {
package {'nmap':
provider => 'rpm',
source => "<Local PATH to the RPM>",
}
}
and the rpm is in ...modules/nmap/files
If i move the rpm to manifests, and provide the rpm name in source => ''
class nmap {
package {'nmap':
provider => 'rpm',
source => "rpm-name.rpm",
}
}
it works, but how can i specify source path with ../files/ and do puppet apply successfully
When i use :
source => 'puppet:///files/nmap-6.45-1.x86_64.rpm',
i get an error:
Debug: Executing '/bin/rpm -i puppet:///files/nmap-6.45-1.x86_64.rpm'
Error: Execution of '/bin/rpm -i puppet:///files/nmap-6.45-1.x86_64.rpm' returned 1: error: open of puppet:///files/nmap-6.45-1.x86_64.rpm failed: No such file or directory
Error: /Stage[main]/Nmap/Package[nmap]/ensure: change from absent to present failed: Execution of '/bin/rpm -i puppet:///files/nmap-6.45-1.x86_64.rpm' returned 1: error: open of puppet:///files/nmap-6.45-1.x86_64.rpm failed: No such file or directory
`
when running the command:
sudo puppet apply --modulepath=/home/user1/qa/puppet_qa/modules/ -e "include nmap" --debug
Unlike the file resource type, the package type has no support for Puppet fileserver URLs. You will need to use a file resource to download the rpm prior to installing it. If this is a recurring problem for you, make a defined type that does those in one go (think macros), e.g.
define fileserver_package($source, $ensure='installed') {
file { "/my/tmp/dir/$name.rpm": source => $source }
package { $name:
ensure => $ensure,
provider => 'rpm',
source => "/my/tmp/dir/$name.rpm",
require => File["/my/tmp/dir/$name.rpm"],
}
}
Edit: it is generally advisable to use a local yum repo instead, see also the first comment by #rojs below.
The RPM package can be installed this way:
package { 'epel-release-6':
provider => 'rpm',
ensure => 'present',
source => '/usr/local/rpms/epel-release-latest-6.noarch.rpm',
}
It seems the module name you are using is nmap. You can use the same source parameter like this,
source => 'puppet:///modules/nmap/nmap-6.45-1.x86_64.rpm',
The syntax to access a file under a module goes like this,
puppet:///modules/<modulename>/<file you want to access>
See this link here, http://docs.puppetlabs.com/puppet/latest/reference/modules_fundamentals.html#files
Lets start from start :
on server:
$rpm -ivh https://yum.puppetlabs.com/puppetlabs-release-pc1-el-7.noarch.rpm
$yum -y install puppetserver
$vi /etc/sysconfig/puppetserver #change JAVA args
$systemctl start puppetserver
$systemctl enable puppetserver
$vi /etc/puppetlabs/puppet/puppet.conf #Add “dns_alt_names” in [master]
On Agent:
$rpm -ivh https://yum.puppetlabs.com/puppetlabs-release-pc1-el-7.noarch.rpm
$yum -y install puppet-agent
$systemctl start puppet
$systemctl enable puppet
$vi /etc/puppetlabs/puppet/puppet.conf # Add “server = pupmaster” in [main]
puppet cert list
puppet cert sign
/etc/puppetlabs/code/environments/production/manifests/site.pp:
node webserver {
class { 'apache': }
}
node dbserver {
class { ‘mysql’: }
}
mkdir –p /etc/puppetlabs/code/environments/production/modules/apache/{manifests, files}
apacheinstall.pp:
class apache::apacheinstall {
if $osfamily == 'redhat' {
package { 'httpd':
ensure => 'latest'
}
service {'httpd':
ensure => 'running',
require => Package["httpd"],
}
file { '/var/www/html/ndex.html':
mode => "0644",
owner => 'root',
group => 'root',
source => 'puppet:///modules/apache/index.html',
}
}
elsif $osfamily == 'debian' {
package { 'apache2':
ensure => 'latest'
}
service {'httpd':
ensure => 'running',
require => Package["httpd"],
}
}
}
INIT.pp
class apache {
notify { 'Installing and Configuring Webserver for $osfamily': }
include apache::mysqlinstall
}
Mysqlinstall.pp:
class apache::mysqlinstall {
exec { 'wget':
path => [ "/bin/", "/sbin/", "/usr/bin/", "/usr/sbin/" ],
command => "/usr/bin/wget https://dev.mysql.com/get/mysql57-community-release-el7-9.noarch.rpm && rpm -ivh /tmp/mysql57-community-release-el7-9.noarch.rpm",
cwd => '/tmp/',
creates => '/etc/firstruns/p1.done',
}
}

Resources