-
-
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. |
+1 for this. Thank you
Is there some reason that content lazy { node[:test][:content] }
won't work?
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.
Good job with this nice succinct gist describing the fundamental compile and converge phase concept in chef.