When working with Chef, many developers encounter unexpected execution orders between different resource types. Consider this common scenario:
ruby_block "block1" do
block do
puts "in block1"
end
action :create
end
remote_file "/tmp/foo" do
puts "in remote_file"
source "https://yahoo.com"
end
Despite appearing sequentially in the recipe, the output shows:
==> default: in remote_file
==> default: in block1
This isn't just about debug output - it affects actual functionality. When we need the ruby_block to set parameters for subsequent resources:
ruby_block "set_url" do
block do
node.default['download'] = {}
node.default['download']['source'] = 'https://google.com'
end
action :create
end
remote_file "/tmp/foo" do
source node['download']['source'] # Fails because ruby_block hasn't run yet
end
Chef operates in two distinct phases during a run:
- Compilation Phase: Resources are evaluated and added to the resource collection
- Execution Phase: Resources run in the order determined by dependency resolution
This explains why puts statements appear "out of order" - they're executed during compilation, while the actual resource actions happen later.
1. Explicit Run Order Using notifies
ruby_block "set_url" do
block do
node.default['download']['source'] = 'https://google.com'
end
action :nothing
end.run_action(:run)
remote_file "/tmp/foo" do
source node['download']['source']
end
2. Immediate Execution with run_action
ruby_block "set_url" do
block do
node.default['download']['source'] = 'https://google.com'
end
action :nothing
end.run_action(:run)
remote_file "/tmp/foo" do
source node['download']['source']
end
3. Using Lazy Evaluation
ruby_block "set_url" do
block do
node.default['download']['source'] = 'https://google.com'
end
action :run
end
remote_file "/tmp/foo" do
source lazy { node['download']['source'] }
end
- Always assume resources might not execute in written order
- For critical ordering, use explicit dependencies
- Consider splitting complex recipes into multiple files
- Use lazy evaluation when dealing with computed attributes
What you're observing is due to Chef's two-phase execution model:
- Compilation Phase: Resources are collected and evaluated
- Execution Phase: Resources are converged in order
This explains why your puts
statements appear out of order - they're being evaluated during compilation, while the actual resource execution happens later.
The key issue is that node.default['test']['foo']
is evaluated during compilation, before your ruby_block has executed:
# This gets evaluated IMMEDIATELY during compilation
remote_file "/tmp/foo" do
source node.default['test']['foo'] # currently nil/empty
end
The proper way to handle this is with lazy
evaluation:
ruby_block "set download URL" do
block do
node.normal['download_url'] = 'https://google.com'
end
end
remote_file "/tmp/foo" do
source lazy { node['download_url'] }
end
If you need the ruby_block to execute immediately:
ruby_block "block1" do
block do
node.default['test']['foo'] = 'https://google.com'
end
action :nothing
end.run_action(:create)
remote_file "/tmp/foo" do
source node['test']['foo']
end
For complex dependencies, use notifications:
ruby_block "set values" do
block do
node.default['source_url'] = calculate_url()
end
notifies :create, 'remote_file[/tmp/data]', :immediately
end
remote_file "/tmp/data" do
source lazy { node['source_url'] }
action :nothing
end
To verify execution order, use Chef's logging:
ruby_block "debug block" do
block do
Chef::Log.info("This executes during convergence")
end
end
log "compilation_message" do
message "This appears during compilation"
level :info
end