Skip to content

Instantly share code, notes, and snippets.

@arangamani
Last active February 20, 2023 02:44
Show Gist options
  • Save arangamani/4659646 to your computer and use it in GitHub Desktop.
Save arangamani/4659646 to your computer and use it in GitHub Desktop.
Dynamically update attribute of a Chef resource during converge phase (Node variable assignment in Compile vs Converge)
# 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.
@dbaboy
Copy link

dbaboy commented Feb 19, 2023

@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

@dbaboy
Copy link

dbaboy commented Feb 20, 2023

@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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment