Set Environment Variables with Puppet - shell

I am using vagrant with puppet to set up virtual machines for development environments. I would like to simply set a few environment variables in the .pp file. Using virtual box and a vagrant base box for Ubuntu 64 bit.
I have this currently.
$bar = 'bar'
class foobar {
exec { 'foobar':
command => "export Foo=${bar}",
}
}
but when provisioning I get an error: Could not find command 'export'.
This seems like it should be simple enough am I missing some sort of require or path for the exec type? I noticed in the documentation there is an environment option to set up environment variables, should I be using that?

If you only need the variables available in the puppet run, whats wrong with :
Exec { environment => [ "foo=$bar" ] }
?

Simplest way to acomplish this is to put your env vars in /etc/environment, this ensures they are available to everything (or pretty much everything).
Something like this:
class example($somevar) {
file { "/etc/environment":
content => inline_template("SOMEVAR=${somevar}")
}
}
Reason for having the class parameterised is so you can target it from hiera with automatic variable lookup (http://docs.puppetlabs.com/hiera/1/puppet.html#automatic-parameter-lookup) ... if you're sticking something in /etc/environment, it's usually best if you actually make it environment specific.
note: I've only tested this on ubuntu

The way I got around it is to also use /etc/profile.d:
$bar = 'bar'
file { "/etc/profile.d/my_test.sh":
content => "export Foo=${bar}",
mode => 755
}
This ensures that everytime you login (ex ssh), the variable $MYVAR gets exported to your environment. After you apply through puppet and login (ex ssh localhost), echo $Foo would return bar

You can set an environment variable by defining it on a line in /etc/environment and you can ensure a line inside a file using file_line in puppet. Combine these two into the following solution:
file_line { "foo_env_var":
ensure => present,
line => "Foo=${bar}",
path => "/etc/environment",
}

You could try the following, which sets the environment variable for this exec:
class foobar {
exec { 'foobar' :
command => "/bin/bash -c \"export Foo=${bar}\"",
}
}

Something like this would work while preserving existing contents of the /etc/environment file:
/code/environments/{environment}/manifests/environment/variable.pp:
define profile::environment::variable (
$variable_name,
$value,
$ensure => present,
) {
file_line { $variable_name:
path => '/etc/environment',
ensure => $ensure,
line => "$variable_name=$value",
match => "$variable_name=",
}
}
Usage (in the body of a node manifest):
profile::environment::variable { 'JAVA_HOME':
variable_name => 'JAVA_HOME',
value => '/usr/lib/jvm/java-1.8.0',
}

I know this is an old question, but I was able to set the PS1 prompt value and add it to my .bashrc file like this:
$PS1 = '\[\e[0;31m\]\u\[\e[m\] \[\e[1;34m\]\w\[\e[m\] \$ '
and within a class:
exec {"vagrant-prompt":
unless => "grep -F 'export PS1=\"${PS1}\"' ${HOME_DIR}/.bashrc",
command => "echo 'export PS1=\"${PS1}\"' >> ${HOME_DIR}/.bashrc",
user => "${APP_USER}",
}
The -F makes grep it interpret it as a fixed string. Otherwise it won't find it and keeps adding to the .bashrc file.

Another variation. This has the advantage that stdlib isn't required (as is with file_line solutions), and the existing content of /etc/environment is preserved:
exec {'echo foo=bar>>/etc/environment':
onlyif => 'test -f /etc/environment',
unless => 'grep "foo=bar" /etc/environment',
path => '/usr/bin',
}

Check out the documentation https://puppet.com/docs/puppet/5.5/types/exec.html
class envcheck {
file { '/tmp/test':
ensure => file,
}
exec { 'foobar':
command => 'echo $bar >> /tmp/test',
environment => ['bar=foo'],
path => ['/bin/'],
}
}
Creating an empty file because an echo would happen in the shell Puppet is running the command in, not the one we're looking at.
Setting an environment variable bar to equal foo.
Setting the path for the echo binary, this isn't normally necessary for system commands but useful to know about.

Related

Jenkins pipeline undefined variable

I'm trying to build a Jenkins Pipeline for which a parameter is
optional:
parameters {
string(
name:'foo',
defaultValue:'',
description:'foo is foo'
)
}
My purpose is calling a shell script and providing foo as argument:
stages {
stage('something') {
sh "some-script.sh '${params.foo}'"
}
}
The shell script will do the Right Thing™ if the provided value is the empty
string.
Unfortunately I can't just get an empty string. If the user does not provide
a value for foo, Jenkins will set it to null, and I will get null
(as string) inside my command.
I found this related question but the only answer is not really helpful.
Any suggestion?
OP here realized a wrapper script can be helpful… I ironically called it junkins-cmd and I call it like this:
stages {
stage('something') {
sh "junkins-cmd some-script.sh '${params.foo}'"
}
}
Code:
#!/bin/bash
helpme() {
cat <<EOF
Usage: $0 <command> [parameters to command]
This command is a wrapper for jenkins pipeline. It tries to overcome jenkins
idiotic behaviour when calling programs without polluting the remaining part
of the toolkit.
The given command is executed with the fixed version of the given
parameters. Current fixes:
- 'null' is replaced with ''
EOF
} >&2
trap helpme EXIT
command="${1:?Missing command}"; shift
trap - EXIT
typeset -a params
for p in "$#"; do
# Jenkins pipeline uses 'null' when the parameter is undefined.
[[ "$p" = 'null' ]] && p=''
params+=("$p")
done
exec $command "${params[#]}"
Beware: prams+=("$p") seems not to be portable among shells: hence this ugly script is running #!/bin/bash.

Include file conditionally based on a bash script

I have a bash command that will return either 1 or 0. I want to run said command from puppet:
exec { 'Check if Thinkpad':
command => 'sudo dmidecode | grep -q ThinkPad && echo 1 || echo 0',
path => '/usr/bin/:/bin/bash/',
environment => "HOME=/root"
}
Is there a way I can include a file using puppet only if my command returned 1?
file { '/etc/i3/config':
source => 'puppet:///modules/i3/thinkpad',
owner => 'root',
group => 'root',
mode => '0644',
}
You can use an external fact to use the bash script as is. Inside the module's facts.d directory, you could place the script.
#!/bin/bash
if [ dmidecode | grep -q ThinkPad ]
echo 'is_thinkpad=true'
else
echo 'is_thinkpad=false'
fi
You can also use a custom fact inside the lib/facter directory of your module.
Facter.add(:is_thinkpad) do
confine kernel: linux
setcode do
`dmidecode | grep -q ThinkPad && echo true || echo false`
end
end
In both cases, the fact name of is_thinkpad follows the convention for the nomenclature of boolean facts for types of systems. You can then update the code in your manifest for this boolean.
if $facts['is_thinkpad'] == true {
file { '/etc/i3/config':
source => 'puppet:///modules/i3/thinkpad',
owner => 'root',
group => 'root',
mode => '0644',
}
}
This will provide you with the functionality you desire.
https://docs.puppet.com/facter/3.6/custom_facts.html#adding-custom-facts-to-facter
https://docs.puppet.com/facter/3.6/custom_facts.html#external-facts
You will probably need to turn your bash script into a "custom fact" -- which is something I've only done once and don't fully understand enough to teach you how.
I want to say that the easiest way to set up a custom fact is to put your script into /etc/facter/facts.d/ on the agent machine, and make sure it ends with a line that says
echo "thinkpadcheck=1"
or
echo "thinkpadcheck=0"
You can test it with (note: you must be root)
sudo facter -p | grep think
and it should return
thinkpadcheck => 1
But once you have done that, then your puppet script can say
if $thinkpadcheck == 1
{
file { '/etc/i3/config':
source => 'puppet:///modules/i3/thinkpad',
owner => 'root',
group => 'root',
mode => '0644',
}
}
else
{
notify { "thinkpadcheck failed for $hostname" : }
}
I'd like to share another method I found in the Puppet Cookbook 3rd edition (page 118):
message.rb
#!/usr/bin/env ruby
puts "This runs on the master if you are centralized"
Make your script executable with:
sudo chmod +x /usr/local/bin/message.rb
message.pp
$message = generate('/usr/local/bin/message.rb')
notify { $message: }
Then run:
puppet apply message.pp
This example uses a ruby script but any type of script including a basic shell script, as was needed in my case, can be used to set a variable in puppet.

Self-explaining version of bash expressions

Expressions like this are short, but not super-readable:
if [ -f .bash_profile ]; then
...
fi
There are also other possible flags for expressions, for instance:
Description
-d file
True if file is a directory.
-e file
True if file exists.
-f file
True if file exists and is a regular file.
-L file
True if file is a symbolic link.
-z string
True if string is empty. (most innatural IMO)
-n string
True if string is not empty.
... and others...
Are there longer self-explaining versions? Something like:
[ --file-exists .bash_profile ]
This is extremely well documented already. As you can see, there is no long-form version of those conditional expressions.
If you want to use these in a more readable way, you can always create your own functions:
function is_a_file() { test -f "$1"; }
function is_a_dir() { test -d "$1"; }
#etc.
if is_a_file /the/file/name
then
#do something
fi
test is the canonical name for the [ command that is typically used. Its return value becomes the return value of the function, so we can use it in exactly the same way in an if statement.
No, use comments or use something less cryptic like Python

Puppet: Exec from class when Exec from another class is successful

I want to call an Exec only when another Exec from a different class is executed successfully.
class mysql {
exec { 'load-sql':
command => 'mysql -uadmi -pxxx general < /vagrant/sites/ddbb/general.sql',
path => ['/bin', '/usr/bin'],
timeout => 0,
onlyif => "test -f /vagrant/sites/ddbb/general.sql",
}
exec { 'delete-general-sql':
command => 'sudo rm /vagrant/sites/ddbb/general.sql',
path => ['/bin', '/usr/bin'],
onlyif => "test -f /vagrant/sites/ddbb/general.sql",
require => Exec['load-sql'],
}
}
class sphinx {
exec { 'sphinx-create-all-index':
command => 'sudo indexer -c /etc/sphinxsearch/sphinx.conf --all --rotate',
require => Exec['load-sql'],
path => '/usr/bin/';
}
}
The command 'delete-general-sql' is executed only if 'load-sql' is executed successfully but 'sphinx-create-all-index'ignores the result of 'load-sql'...
Thanks in advance!
You mess up with require and onlyif.
Read about puppet ordering.
require
Causes a resource to be applied after the target resource.
so
require => Exec['load-sql'],
means, execute resource after execution of exec{'load-sql':} resource.
On the other hand onlyif in exec means:
If this parameter is set, then this exec will only run if the command has an exit code of 0.
So you must add onlyif with proper test (probably onlyif => "test -f /vagrant/sites/ddbb/general.sql) to 'sphinx-create-all-index'.
To make the dependent exec run only once the previous one did run, you can use subscribe and refreshonly.
exec { 'sphinx-create-all-index':
command => 'sudo indexer -c /etc/sphinxsearch/sphinx.conf --all --rotate',
subscribe => Exec['load-sql'],
refreshonly => true,
path => '/usr/bin/';
}
This has some caveats - you may have a hard time to get Puppet to execute this task again if something goes wrong the first time around.

passing parameters from external file (template) to puppet script

Hi I'm trying to execute an exe file using puppet script. My exe file is accepting 3 parameters like param1, param2 and param3. All I want is to pass these parameters through external file. How can I do this?
Here is my sample code:
exec { "executing exe file":
command => 'copyfile.exe "DestinatoinPath" "sourcefilename" "destinationfilename" ',
}
All I want is to pass all these values from external file and use it here.
Can someone help me to resolve this
Here is my trail:
Here is my directory structure:
puppet\modules\mymodule\manifests\myfile.pp and
puppet\modules\mymodule\templates\params.erb
and my erb file is having a value of path ex: d:\test1.txt e:\test1.txt testfilename
$myparams = template("mymodule/params.erb")
exec { "executing exe file":
command => '$myparams',
}
EDIT:
The root of the problem was trying to call the module manifest directly, thus the template lookup failed. The solution was not to use a module and specify the full template path.
There are 2 main ways to go about it:
Declare the variables in scope
#acceptable for a throwaway manifest
$path = "DestinationPath"
$source = "sourcefilename"
$destination "destinationfilename"
exec { "executing exe file":
command => 'copyfile.exe ${path} ${source} ${destination}',
}
Wrap it in a parameterized class/defined type
# parameterized class, included only once
class executing_exe_file ($path, $source, $destination) {
exec { "executing exe file":
command => 'copyfile.exe ${path} ${source} ${destination}',
}
}
OR
# defined resource, can be repeated multiple times
define executing_exe_file ($path, $source, $destination) {
exec { "executing exe file":
command => 'copyfile.exe ${path} ${source} ${destination}',
}
}
THEN
executing_exe_file { "executing exe file":
path: "DestinationPath",
source: "sourcefilename",
destination: "destinationfilename",
}
Also as a side note, you have to make sure copyfile.exe is fully qualified.

Resources