When working with Puppet manifests, a common automation scenario requires executing a command only when a specific file is absent from the system. The original example demonstrates this perfectly:
exec { 'git add url':
command => 'git remote add origin https://github.com/testing/puppet.git',
require => Exec['git init'],
cwd => '/home/vagrant/django',
user => 'vagrant',
onlyif => "not sure what to put here"
}
The solution lies in Puppet's onlyif
parameter combined with shell test commands. Here's the proper implementation:
exec { 'git add url':
command => 'git remote add origin https://github.com/testing/puppet.git',
require => Exec['git init'],
cwd => '/home/vagrant/django',
user => 'vagrant',
onlyif => "test ! -f /usr/local/bin/papply"
}
For more complex conditions or better readability, consider these variations:
# Using unless parameter (reverse logic)
unless => "test -f /usr/local/bin/papply"
# Using Ruby DSL for complex conditions
onlyif => "! File.exist?('/usr/local/bin/papply')"
# Checking multiple files
onlyif => "test ! -f /path1 && test ! -f /path2"
Here's a complete manifest example for initializing a Git repo only when the remote isn't configured:
class git_setup {
exec { 'init_repo':
command => 'git init',
cwd => '/home/vagrant/django',
user => 'vagrant',
creates => '/home/vagrant/django/.git',
}
exec { 'add_remote':
command => 'git remote add origin https://github.com/testing/puppet.git',
cwd => '/home/vagrant/django',
user => 'vagrant',
unless => 'git config --get remote.origin.url',
require => Exec['init_repo'],
}
}
- Always use absolute paths in conditions
- Consider using
creates
parameter when appropriate - Test conditions manually before implementing
- Chain dependent resources with
require
If your condition isn't working as expected:
# Test the condition directly on the target system
sudo -u vagrant test ! -f /usr/local/bin/papply && echo "Would execute" || echo "Would skip"
# Enable Puppet debug logging
puppet apply --debug manifest.pp
# Verify file permissions
ls -la /usr/local/bin/papply
When managing infrastructure with Puppet, a common requirement is to execute commands only when specific conditions are met. The particular case we're addressing involves running a git command only when a target file (/usr/local/bin/papply
) doesn't exist.
Puppet provides several ways to handle conditional execution of resources. The exec
type supports three main attributes for this purpose:
onlyif
: Runs command only if test returns 0unless
: Runs command unless test returns 0creates
: Doesn't run command if specified file exists
The most robust approach uses the unless
parameter with a test command:
exec { 'git add url':
command => 'git remote add origin https://github.com/testing/puppet.git',
require => Exec['git init'],
cwd => '/home/vagrant/django',
user => 'vagrant',
unless => 'test -f /usr/local/bin/papply'
}
Other valid implementations include:
Using onlyif
with negation:
onlyif => 'test ! -f /usr/local/bin/papply'
Using creates
if you want to create the file:
exec { 'git add url':
# ... other parameters ...
creates => '/usr/local/bin/papply'
}
- Always use absolute paths for file checks
- Consider adding timeout parameters for long-running commands
- Test your conditional logic thoroughly in development
- For complex conditions, consider writing custom facts or functions
Here's a complete example managing a git repository only when the deployment script is missing:
exec { 'initialize git repo':
command => 'git init',
cwd => '/home/vagrant/django',
user => 'vagrant',
unless => 'test -d .git'
}
exec { 'git add remote':
command => 'git remote add origin https://github.com/testing/puppet.git',
require => Exec['initialize git repo'],
cwd => '/home/vagrant/django',
user => 'vagrant',
unless => 'test -f /usr/local/bin/papply || git remote -v | grep -q origin'
}