I'm trying to execute the following module in Puppet this and got the error:
[root#localhost modules]# puppet apply jenkin_install.pp
Error: Could not parse for environment production: Syntax error at '8080' at /etc/puppetlabs/code/modules/jenkin_install.pp:41:52 on node localhost.localdomain
Here's the code:
exec { 'Change the port from 8080 to 8000':
command => 'sed -i'.original' 's/JENKINS_PORT="8080"/JENKINS_PORT="8000"/' /etc/sysconfig/jenkins',
path => '/usr/local/bin/:/bin/',
logoutput => true,
onlyif => 'grep "8000" /etc/sysconfig/jenkins',
}
I'm wondering how to solve the syntax error in the command?
Thanks
The problem is that you are quoting the command string using single quotes when the command itself contain single quotes. The parser can't tell the difference between the two, so ends up interpreting bits of the command as Puppet code.
The easiest and clearest fix is to use heredoc strings:
exec { 'Change the port from 8080 to 8000':
command => #(END),
sed -i'.original' 's/JENKINS_PORT="8080"/JENKINS_PORT="8000"/' /etc/sysconfig/jenkins
| END
path => '/usr/local/bin/:/bin/',
logoutput => true,
onlyif => 'grep "8000" /etc/sysconfig/jenkins',
}
Although, as another commenter says, this resource should be recoded as a file_line resource.
file_line { 'set_JENKINS_PORT':
ensure => present,
path => '/etc/sysconfig/jenkins',
match => 'JENKINS_PORT=',
line => 'JENKINS_PORT="8000"',
}
Related
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",
}
}
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.
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"",
}
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.
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.