fail when a file exist in puppet - 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.

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'

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.

puppet not able to run shell script from master to client

I am new to puppet. I want to run a shell script call crfs.sh located under /myscripts on a RHEL linux puppet master server.
How do I execute this script on a client or target server?
What you want can be solved using the file and the exec modules of puppet.
class mymodule::myclass {
file { 'my_bash_script':
ensure => 'file',
source => 'puppet:///modules/mymodule/my_bash_script.sh',
path => '/usr/local/bin/my_bash_script.sh',
owner => 'root'
group => 'root'
mode => '0744', # Use 0700 if it is sensitive
notify => Exec['run_my_script'],
}
exec { 'run_my_script':
command => '/usr/local/bin/my_bash_script.sh',
refreshonly => true,
}
}

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',
}
}

Sequence of Execs in Puppet

I have a sequence of exec in my Puppet manifest:
The first one downloads ZIP file with binary (unless the binary has already been installed) and saves it to /tmp.
The second one unzips it.
When I apply the manifest for the first time, it works correctly. However, when I clean my /tmp and apply the manifest again, it fails because the first exec doesn't executed (that is correct), but the second still tries to execute and fails because ZIP file is not present.
How do I modify the manifest to skip the second exec if the first one doesn't download file?
exec { 'ngrok-download':
command => 'wget https://dl.ngrok.com/linux_386/ngrok.zip -O /tmp/ngrok.zip',
unless => 'which ngrok',
path => ['/bin', '/usr/bin'],
}
exec { 'ngrok-unzip':
command => 'unzip ngrok.zip',
cwd => '/tmp',
path => ['/usr/bin'],
require => Exec['ngrok-download'],
}
Try this:
exec { 'ngrok-download':
command => 'wget https://dl.ngrok.com/linux_386/ngrok.zip -O /tmp/ngrok.zip',
unless => 'which ngrok',
path => ['/bin', '/usr/bin'],
notify => Exec['ngrok-unzip'],
}
exec { 'ngrok-unzip':
command => 'unzip ngrok.zip',
cwd => '/tmp',
path => ['/usr/bin'],
refreshonly => true,
require => Exec['ngrok-download'],
}
This will result in the unzip exec only running when the wget exec actually does something -- which it won't if ngrok is found.
Normally I would wget it to a more permanent location and leave it there. Then instead of the unless => 'which ngrok' check, replace with creates => '/path/to/zip.file'. The result being as long as the file is still there, none of the execs fire.
Comes in handy when you version the zip files and want to change versions.
You could also try easier approach:
exec { 'ngrok-download':
command => 'wget https://dl.ngrok.com/linux_386/ngrok.zip -O /tmp/ngrok.zip',
unless => 'which ngrok',
path => ['/bin', '/usr/bin'],
} ~>
exec { 'ngrok-unzip':
command => 'unzip ngrok.zip',
cwd => '/tmp',
path => ['/usr/bin'],
refreshonly => true,
}
Where Exec['ngrok-download'] notifies Exec['ngrok-unzip'] if applied and Exec['ngrok-unzip'] refresh its state only if needed
Same thing can be achieved by doing following:
exec { 'ngrok-download':
command => 'wget https://dl.ngrok.com/linux_386/ngrok.zip -O /tmp/ngrok.zip',
unless => 'which ngrok',
path => ['/bin', '/usr/bin'],
}
exec { 'ngrok-unzip':
command => 'unzip ngrok.zip',
cwd => '/tmp',
path => ['/usr/bin'],
refreshonly => true,
}
Exec['ngrok-download'] ~> Exec['ngrok-unzip']
Hope this helps.

Resources