In chef, when a nested attribute that might not exist/or not crated yet, you can use rescue as a modifier to an if statement.
For example, assuming that only some of your nodes have node[‘install_wls’][‘isManaged’]defined:
if node['install_wls']['isManaged']
do the stuff
end rescue NoMethodError
will avoid the chef run bailing with “undefined method `[]’ for nil:NilClass” error.
Though there are any legit method errors within your if block to handle empty/nil/blank attributes, you’ve just caught an exception you shouldn’t.
I use this in cookbooks that have extra functionality if some dependency of other component recipe happens to be installed. For example, the odi cookbook depend on wls or odi depends on oracldb.
Sometime during automation of a large deployment process, we have to bootstrap a node , create environment and add the node in that particular environment on the fly.
Sometimes we have to deal with global variables like User passwords, database password, API Keys, middleware boot properties in our chef recipes which shouldn’t be exposed outside.
One solution is we have to keep all the secrets in a data bag and encrypt them using a random secret key and later distribute the key to other node where the secrets are accessed.
The other solution if using chef-vault which we will cover in a later topics.
In many cases we have to reuse same code again and again in our recipes. So to reduce this we can write our own library module and reuse it’s methods whenever required. This can help us use our own custom methods.
Like in my previous post we have to access the environment variables in many recipes. So we can use the same code as our library to create a module (lets say ProdEnvSetup)
Create a library file under library directory of cook-book.(cook-book/library/envsetup.rb) and add below code.
[code language=”ruby”]module ProdEnvSetup
def setupenv()
hash1 = {}
File.open(“/u01/data/wor/app/conf/conf.prop”) do |fp|
fp.each do |line|
key, value = line.chomp.split(“=”,2)
hash1[key] = value
end
end
hash1.each do |key,value|
skey = “#{key.to_s}”.gsub(/\s|”|’/, ”)
svalue = “#{value.to_s}”.gsub(/\s|”|’/, ”)
ENV[skey] = svalue
end
end
end
end[/code]
2. Using library inside recipe ( 1st way )
So if the same environment variables are required inside a recipe, we can directly include the module inside our recipe.
Use below code inside begining of recipe:
[code language=”ruby”]class Chef::Recipe
include ProdEnvSetup
end
setupenv() [/code]
2. Using library ( 2nd way )
We can add additional encapsulation to our module by keeping it inside a recipe and include that recipe in every recipe, wherever its required.
create a recipe setupenv.rb and add above codes in 1st way(above)
Many time we have to read a property file in in which the variable and value are comma separated and we have to set those in our environment variable to execute certain recipes.
e.g. property file (/u01/data/wor/app/conf/conf.prop)
ops_home = ‘/u01/data/work/app/ops-home-1.2.30’
node_instance = ‘/u01/data/work/app’
So here we have to read the file , create a hash and then save the LHS as key and RHS as value. Then we are good to expose them as environment variable.
Note : This is the approach I used, there may be other solution available.
Here the properties are = separated. It can be any separator.
This is a reusable function and can be called where ever required.
[code language=”ruby”]def setupenv()
hash1 = {}
File.open("/u01/data/wor/app/conf/conf.prop") do |fp|
fp.each do |line|
key, value = line.chomp.split("=",2)
hash1[key] = value
end
end</pre>
hash1.each do |key,value|
skey = "#{key.to_s}".gsub(/\s|"|’/, ”)
svalue = "#{value.to_s}".gsub(/\s|"|’/, ”)
ENV[skey] = svalue
end
end
end
end[/code]
Here setupenv( ) can be called anywhere the ENV variables are required.
Note : Here gsub(/\s|”|’/, ”) is used to trim leading and trailing space, single quote, double quote of the key and value.