I have the following (simplified) recipe called java, to install Java of course.
File recipes/default.rb
include_recipe "install_java"
File recipes/install_java.rb
# Install RPM from yum repo via yum_install library function
yum_install("jdk1.7.0_51")
# List the directories in /usr/java
jdk_dir = `ls -ld /usr/java/jdk1.* | sort | tail -1`
if jdk_dir.empty?
raise "Missing JDK installation"
end
When I run the recipe by "chef-client -o recipe[java]"
Synchronizing Cookbooks:
- java
Compiling Cookbooks...
ls: /usr/java/jdk1.*: No such file or directory
=========================================================================== Recipe Compile Error in /var/chef/cache/cookbooks/java/recipes/default.rb ===========================================================================
RuntimeError
------------
Missing JDK installation
It seems like the yum_install() function is NOT being called. However, if I modify the install_java.rb recipe to just have
# Install RPM from yum repo via yum_install library function
yum_install("jdk1.7.0_51")
it works.
Why is this?
There are two stages to a chef run though. A compile phase, to organise what resources need to be run and resolve all variables. Then a run phase where each resource is actually executed.
Recipe: a file that contains a set of instructions (resources) to be executed. A recipe must be contained inside a Cookbook. Resource: a portion of code that declares an element of the system and what action should be executed.
The best thing to do is write a custom resource using unified_mode, instead of a recipe:
resources/install_java.rb:
unified_mode true
provides :install_java
action :install do
# Install RPM from yum repo via yum_install library function
yum_install("jdk1.7.0_51")
# List the directories in /usr/java
jdk_dir = `ls -ld /usr/java/jdk1.* | sort | tail -1`
if jdk_dir.empty?
raise "Missing JDK installation"
end
end
recipes/default.rb:
install_java "install my java -- you could make this name_property the version"
Unified mode eliminates the compile/converge two-phase parsing so that this works, but the code must be moved to a custom resource (which confers additional usability benefits and is a best practice anyway -- now the resource can grow properties and can be reused to install different versions of java, etc).
Ok, so Chef runs take two passes.
I like to call this the collection phase.
At this point, the actual ruby code in your recipe is run. That means any statements like
jdk_dir = ls -ld /usr/java/jdk1.* | sort | tail -1
are going to be executed at that point. However, the ruby code that creates a Chef resource yum_install("jdk1.7.0_51")
only creates the resources. Those resources, created by your recipe code, are then added to the Chef resource_collection, but the resource actions are NOT run yet.
I call this the resolution phase. At this point - after ALL recipes have run (creating resources, but not running actions) - we are now ready to actually run the resource actions. Chef starts with the first resource in the resource_collection and runs the specified action on that resource. It works through the collection, calling notifications as needed, until all resources' actions have been run. Then your run is complete.
So, in your case, you are trying to access the directory in the collection phase but you don't create the directory until the resolution phase. If you want to run ruby code during the resolution phase you can do so in a ruby_block
resource. For example:
ruby_block 'verify java is there' do
block do
if jdk_dir.empty?
raise "Missing JDK installation, reinstall"
end
end
end
If this ruby_block
resource is placed after your yum_install
(which should probably be yum_package
) resource, then it will get placed after the install resource in the collection_phase, and then executed during the resolution phase (i.e., run time) of the Chef run.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With