-
-
Save arangamani/4659646 to your computer and use it in GitHub Desktop.
# In Chef, when a resource is defined all its variables are evaluated during | |
# compile time and the execution of the resource takes place in converge phase. | |
# So if the value of a particular attribute is changed in converge | |
# (and not in compile) the resource will be executed with the old value. | |
# Example problem: | |
# Let's consider this situation where there are two steps involved in a recipe | |
# Step 1 is a Ruby block that changes a node attribute. Rubyblocks get executed | |
# in converge phase | |
# Step 2 is a Chef resource that makes use of the node attribute that was | |
# changed in Step 1 | |
# ============= Without any modification to normal behavior ================= # | |
node[:test][:content] = "old content" | |
# Step 1 | |
ruby_block "step1" do | |
block do | |
node[:test][:content] = "new content" | |
end | |
end | |
# Step 2 | |
file "/tmp/some_file" do | |
owner "root" | |
group "root" | |
content node[:test][:content] | |
end | |
# =========================================================================== # | |
# file resource will still have the old content as it is set in the compile | |
# phase. | |
# ========================== With modified code ============================= # | |
node[:test][:content] = "old content" | |
# Step 1 | |
ruby_block "step1" do | |
block do | |
node[:test][:content] = "new_content" | |
# Dynamically set the file resource's attribute | |
# Obtain the desired resource from resource_collection | |
file_r = run_context.resource_collection.find(:file => "/tmp/some_file") | |
# Update the content attribute | |
file_r.content node[:test][:content] | |
end | |
end | |
# Step 2 | |
file "/tmp/some_file" do | |
owner "root" | |
group "root" | |
content node[:test][:content] | |
end | |
# =========================================================================== # | |
# The file resource will now have the updated content. |
Would you mind updating this to use the newer chef 10+ syntax
node.default[:test][:content] = "old content"
instead of
node[:test][:content] = "old content"
Lazy Attribute Evaluation works just fine!
https://docs.getchef.com/resource_common.html#lazy-attribute-evaluation
lazy
evaluation will work perfectly fine. This was a workaround before the lazy
evaluation was implemented. I also left it as an example for understanding compile vs converge phases in chef.
I'm using a version of Chef that has a bug with resource_file which can't support lazy, so this helped me a ton!
Thank you so much for this!!!!!!! Same as above, it works for those resources that don't work with lazy attributes.
Thanks for posting this :)
This post help me resolve my issue.
After coming across to this now finally everything clicks, I have to say after spending roughly 10 hours and not understanding the issue I was going desperate and even thinking of rewriting all my cookbook with only Ruby calling Chef::Resource
in the code.
If you were in front of me I would hug you, a big thank you, you made someones life much better just before Christmas.
This is interesting thing. but still one thing I just can't understand well.
In 'With modified code' part #step 1, code under line of
"# Obtain the desired resource from resource_collection"
updates the dynamic resource that will be soon referred by #step 2, the modification finally get #step2 works.
My question is: why don't we just directly deal with the 'dynamic resource list' if we need to know the value of an updated resource since #step2 won't work?
Because chef is designed to provide resources and so end-users need to do extra coding to make it happen? I don't think so.
This is an excellent gist. I'm grateful I found it. +1
Excellent stuff. Thanks for sharing
@arangamani This is a great post. I have come across this via a fork. We have a use case where the content in the Ruby block is a GnuPG symmetric encryption key.
#-- The node attribute is initially set to 'nothing'
node.default['enc_pw'] = 'nothing'
# Define constants
GPG_E_OPTIONS = '--batch --passphrase /tmp/passphrase'.freeze
GPG_ENC_OPTIONS = '--cipher-algo AES256'.freeze
GPG_ENC_FILE = '/tmp/encrypted_pw.gpg'.freeze
#-- Converge phase
#-- The Ruby block will find the handle for the file resource and then update the file's content with the output of the bash command
ruby_block 'encrypt_pw' do
block do
node.default['enc_pw'] = shell_out!("echo -n 'som3Passw4^d' | /usr/bin/gpg #{GPG_E_OPTIONS} #{GPG_ENC_OPTIONS} --symmetric").stdout.strip
file_r = run_context.resource_collection.find(:file => "update_encrypted_pw_file_gpg")
file_r.content node['enc_pw']
end
end
#-- Compile phase
#-- Initially, during compile phase, the file will have its contents as 'nothing'
#-- But, later in converge phase, the content will be overwritten in the Ruby block.
# Update encrypted file
file 'update_encrypted_pw_file_gpg' do
content node['enc_pw']
path "#{GPG_ENC_FILE}"
action :create
end
FYI - Though we have chosen to write the contents directly to the file with a > redirect and then use the file resource to only update the permission
@arangamani A question - if the file resource is moved to before ruby block the the content is not updated anymore! Is it possible to explain that.
Is there some reason that
content lazy { node[:test][:content] }
won't work?