Puppet install DS with 2 different commands and only if - puppet

I am not very experienced in Puppet and I really need your help.
I have 2 servers where I need to install DS (ed. Directory Server) . Is running without error but in the servers not run these commands. What I made wrong?
exec { 'Install first DS':
command => "setup --serverId first-ds --deploymentKeyPassword ${prof_ds::constants::deployment_pwd} --deploymentKey ${prof_ds::constants::deployment_key} --rootUserDn ${prof_ds::constants::admin_user} --rootUserPassword ${prof_ds::constants::admin_pwd} --monitorUserPassword ${prof_ds::constants::monitor_pwd} --hostname ${::fqdn} --ldapPort ${prof_ds::constants::ldap_port} --ldapsPort ${prof_ds::constants::ldaps_port} --httpsPort ${prof_ds::constants::https_port} --adminConnectorPort ${prof_ds::constants::admin_port} --replicationPort ${prof_ds::constants::replication_port} --start --acceptLicense",
onlyif => ['test "${::fqdn}" == "${ds_hosts[0]}" && echo 0 || echo 1'],
environment => ["JAVA_HOME=${java_home}"],
path => ['/usr/bin', '/usr/sbin', '/bin', '/opt/opendj','/opt/opendj/bin'],
}
exec { 'Install second DS':
command => "setup --serverId second-ds --deploymentKeyPassword ${prof_ds::constants::deployment_pwd} --deploymentKey ${prof_ds::constants::deployment_key} --rootUserDn ${prof_ds::constants::admin_user} --rootUserPassword ${prof_ds::constants::admin_pwd} --monitorUserPassword ${prof_ds::constants::monitor_pwd} --hostname ${::fqdn} --ldapPort ${prof_ds::constants::ldap_port} --ldapsPort ${prof_ds::constants::ldaps_port} --httpsPort ${prof_ds::constants::https_port} --adminConnectorPort ${prof_ds::constants::admin_port} --replicationPort ${prof_ds::constants::replication_port} --bootstrapReplicationServer ${ds_hosts[0]}:${prof_ds::constants::replication_port} --start --acceptLicense",
onlyif => ['test "${::fqdn}" == "${ds_hosts[1]}" && echo 0 || echo 1'],
environment => ["JAVA_HOME=${java_home}"],
path => ['/usr/bin', '/usr/sbin', '/bin', '/opt/opendj','/opt/opendj/bin'],
}

I presume your commands are correct. So what may need to be fixed is the onlyif command, that means that the exec command is going to be ran onlyif it will return a non-zero output (more). The command will run and will interpolate when using double-quote and will not if used single quote (is a Ruby standard rather than Puppet)
The full code:
if $::hostname == $ds_hosts[0] {
exec { 'setup first-ds':
command => "setup --serverId first-ds --deploymentKeyPassword ${prof_ds::constants::deployment_pwd} --deploymentKey ${prof_ds::constants::deployment_key} --rootUserDn ${prof_ds::constants::admin_user} --rootUserPassword ${prof_ds::constants::admin_pwd} --monitorUserPassword ${prof_ds::constants::monitor_pwd} --hostname ${::fqdn} --ldapPort ${prof_ds::constants::ldap_port} --ldapsPort ${prof_ds::constants::ldaps_port} --httpsPort ${prof_ds::constants::https_port} --adminConnectorPort ${prof_ds::constants::admin_port} --replicationPort ${prof_ds::constants::replication_port} --start --acceptLicense",
onlyif => ["test '${::fqdn}' == '${ds_hosts[0]}' && echo 0 || echo 1"],
environment => ["JAVA_HOME=${java_home}"],
path => ['/usr/bin', '/usr/sbin', '/bin', '/opt/opendj','/opt/opendj/bin'],
}
}
if $::hostname == $ds_hosts[1] {
exec { 'setup second-ds':
command => "setup --serverId second-ds --deploymentKeyPassword ${prof_ds::constants::deployment_pwd} --deploymentKey ${prof_ds::constants::deployment_key} --rootUserDn ${prof_ds::constants::admin_user} --rootUserPassword ${prof_ds::constants::admin_pwd} --monitorUserPassword ${prof_ds::constants::monitor_pwd} --hostname ${::fqdn} --ldapPort ${prof_ds::constants::ldap_port} --ldapsPort ${prof_ds::constants::ldaps_port} --httpsPort ${prof_ds::constants::https_port} --adminConnectorPort ${prof_ds::constants::admin_port} --replicationPort ${prof_ds::constants::replication_port} --bootstrapReplicationServer ${ds_hosts[0]}:${prof_ds::constants::replication_port} --start --acceptLicense",
onlyif => ["test '${::fqdn}' == '${ds_hosts[1]}' && echo 0 || echo 1"],
environment => ["JAVA_HOME=${java_home}"],
path => ['/usr/bin', '/usr/sbin', '/bin', '/opt/opendj','/opt/opendj/bin'],
}
}
UPDATE:
Another optimization I recommend is avoid testing empty strings (depends on the OS/version), by adding another char in front, e.g. x:
test 'x${::fqdn}' == 'x${ds_hosts[0]}' && echo 0 || echo 1
UPDATE:
More on how you can restrict exec on specific nodes (more), in a very simple empirical way (updated the code snippet above, too)
However, the proper way to restrict specific execution on specific nodes, is to either have 2 Nodegroups: Directory Service Primary and Directory Service Secondary, pin each node to its counterpart, and use the resource collector feature to distribute the work on different nodes.
Another note is that you should avoid using exec resource as they are not idempotent. If that is not possible, to define a type that would wrap that resource and manage all of its lifecycle. In your case would be something like:
directory_service { 'first-ds':
deploymentKeyPassword => '?',
deploymentKey => '?',
}
This would probably come later, when you understand how do you want to manage the configurations of your DS service. To get inspired, check apache. Looking on forge for DS, one module raised my attention: markt-de/puppet-ds_389

Your onlyif attributes are nonsense on several levels. Your other answer points out some of these, but for completeness:
An Exec's onlyif attribute should be a single command, as a string. You may be lucking out via stringification of the arrays you are actually presenting, but you really ought to make them plain strings.
By putting the onlyif command in single quotes, you prevent Puppet from interpolating values for the Puppet variable references within.
You appear to have the idea that it is the standard output of the onlyif command that determines whether the main command will be run, but that is incorrect: it is the onlyif's exit status.
Since all the details being tested are known during catalog building, it is pointless to use an onlyif attribute at all. A Puppet conditional statement would be better suited.
But what you should use an unless or onlyif or creates for is to prevent installing the software again if it has already been installed.
Overall, then, you want something more like this:
if $::fqdn == $ds_hosts[0] {
exec { 'Install first DS':
command => "setup --serverId first-ds ...",
environment => ["JAVA_HOME=${java_home}"],
path => ['/usr/bin', '/usr/sbin', '/bin', '/opt/opendj','/opt/opendj/bin'],
unless => "exit_with_status_0_if_DS_is_already_installed",
}
} elsif $::fqdn == $ds_hosts[1] {
exec { 'Install second DS':
command => "setup --serverId second-ds ...",
environment => ["JAVA_HOME=${java_home}"],
path => ['/usr/bin', '/usr/sbin', '/bin', '/opt/opendj','/opt/opendj/bin'],
unless => "exit_with_status_0_if_DS_is_already_installed",
}
}

Related

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 test and remove an array of files/folders

I'm looking to make the following code work somehow, it seems if i do not test the files/folders first I end up with the error:
Error: Failed to apply catalog: Parameter path failed on
File[/opt/dynatrace-6.2]: File paths must be fully qualified, not
'["/opt/dynatrace-6.2", "/opt/dynatrace-5.6.0",
"/opt/rh/httpd24/root/etc/httpd/conf.d/dtload.conf",
"/opt/rh/httpd24/root/etc/httpd/conf.d/01_dtagent.conf"]' at
newrelic.pp:35
The pertinent parts
$dtdeps = [
"/opt/dynatrace-6.2",
"/opt/dynatrace-5.6.0",
"${httpd_root}/conf.d/dtload.conf",
"${httpd_root}/conf.d/01_dtagent.conf",
]
exec { "check_presence":
require => File[$dtdeps],
command => '/bin/true',
onlyif => "/usr/bin/test -e $dtdeps",
}
file { $dtdeps:
require => Exec["check_presence"],
path => $dtdeps,
ensure => absent,
recurse => true,
purge => true,
force => true,
} ## this is line 35 btw
exec { "stop_dt_agent":
command => "PID=$(ps ax |grep dtwsagent |grep -v grep |awk '{print$1}') ; [ ! -z $PID ] && kill -9 $PID",
provider => shell,
}
service { "httpd_restart" :
ensure => running,
enable => true,
restart => "/usr/sbin/apachectl configtest && /etc/init.d/httpd reload",
subscribe => Package["httpd"],
}
Your code looks basically correct, but you went overboard with your file resources:
file { $dtdeps:
require => Exec["check_presence"],
path => $dtdeps,
...
This does create all the file resources from your array (since you use an array for the resource title) but each single one of them will then try to use the same array as the path value, which does not make sense.
TL;DR remove the path parameter and it should Just Work.
You can actually simplify this down a lot. Puppet only runs the file removal if the files don't exist, so the check_presence exec is not required.
You can't give a path an array, but you can pass the title as an array and then the paths get automatically made.
$dtdeps = [
"/opt/dynatrace-6.2",
"/opt/dynatrace-5.6.0",
"${httpd_root}/conf.d/dtload.conf",
"${httpd_root}/conf.d/01_dtagent.conf",
]
file { $dtdeps:
ensure => absent,
recurse => true,
purge => true,
force => true,
}
exec { "stop_dt_agent":
command => '[ ! -z $PID ] && kill -9 $PID',
environment => ["PID=\$(ps ax |grep dtwsagent |grep -v grep |awk '{print$1}'))"],
provider => shell,
}
However, running the stop_dt_agent exec is a bit fragile. You could probably refactor this into a service resource instead:
service { 'dynatrace':
ensure => stopped,
provider => 'base',
stop => 'kill -TERM $(ps ax | grep \"dtwsagent\"|grep -v grep|awk '{print \$1}')',
status => "ps ax | grep "dtwsagent"",
}

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.

run Exec only if another Exec ran

how can I configure a Exec to run only if another Exec ran?
I have a manifest like this:
file { $target:
ensure => directory
}
exec { "unzip foobar.zip -C ${target}":
unless => "file ${target}/some-file-form-archive"
}
exec { "chown -R $user ${target}":
onlyif => ???
}
I would like the chown to run only if unzip foobar.zip ran. Of course I could start checking whether some-file-from-archive is already owned by $user, but somehow it does not seem right.
There's an answer here already: http://ask.puppetlabs.com/question/14726/run-exec-only-if-another-exec-ran/
Changing the manifest like this fixes my problem:
exec { 'unpack file':
command => "unzip foobar.zip -C ${target}",
path => '/usr/bin',
creates => "${target}/some-file-form-archive",
require => File[$target, '<archive>'],
notify => Exec[fix archive],
}
exec { 'fix archive':
command => "chown -R ${user} ${target}",
path => '/bin',
refreshonly => true,
}
UPDATE 28.11.2014
motivated by Felix Frank's comment i tried out something else. instead of notify/refreshonly you can ensure all resource in a file-tree are owned by a user like this:
exec { 'fix archive':
command => "chown -R ${user} ${target}",
path => '/bin',
unless => "test 0 -eq $(find ${target} \\! -user ${user} | wc -l)"
}
this way owner is ensured to be $user even if it was changed after unpack file ran.

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