Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there any way to delay a resource's attribute resolution until the "execute" phase?

I have two LWRPs. The first deals with creating a disk volume, formatting it, and mounting it on a virtual machine, we'll call this resource cloud_volume. The second resource (not really important what it does) needs a UUID for the newly formatted volume which is a required attribute, we'll call this resource foobar.

The resources cloud_volume and foobar are used in a recipe something like the following.

volumes.each do |mount_point, volume|
  cloud_volume "#{mount_point}" do
    size volume['size']
    label volume['label']
    action [:create, :initialize]
  end
  foobar "#{mount_point}" do
    disk_uuid node[:volumes][mount_point][:uuid]   # This is set by cloud_volume
    action [:do_stuff]
  end
end

So, when I do a chef run I get a Required argument disk_identifier is missing! exception.

After doing some digging I discovered that recipes are processed in two phases, a compile phase and an execute phase. It looks like the issue is at compile time as that is the point in time that node[:volumes][mount_point][:uuid] is not set.

Unfortunately I can't use the trick that OpsCode has here as notifications are being used in the cloud_volume LWRP (so it would fall into the anti-pattern shown in the documentation)

So, after all this, my question is, is there any way to get around the requirement that the value of disk_uuid be known at compile time?

like image 802
Matt Avatar asked Jan 31 '13 19:01

Matt


2 Answers

A cleaner way would be to use Lazy Attribute Evaluation. This will evaluate node[:volumes][mount_point][:uuid] during execution time instead of compile

foobar "#{mount_point}" do
  disk_uuid lazy { node[:volumes][mount_point][:uuid] }
  action [:do_stuff]
end
like image 187
Greg Avatar answered Oct 16 '22 11:10

Greg


Disclaimer: this is the way to go with older Chef (<11.6.0), before they added lazy attribute evaluation.

Wrap your foobar resource into ruby_block and define foobar dynamically. This way after the compile stage you will have a ruby code in resource collection and it will be evaluated in run stage.

ruby_block "mount #{mount_point} using foobar" do
  block do
    res = Chef::Resource::Foobar.new( mount_point, run_context )
    res.disk_uuid node[:volumes][mount_point][:uuid]
    res.run_action :do_stuff
  end
end

This way node[:volumes][mount_point][:uuid] will not be known at compile time, but it also will not be accessed at compile time. It will only be accessed in running stage, when it should already be set.

like image 26
Draco Ater Avatar answered Oct 16 '22 11:10

Draco Ater