Wrapper cookbooks, templates, lazy

I ran into an interesting one today.

If you’re wrapping a (community) cookbook and your wrapper needs lazy {} to
move evaluation of something to the execute phase, the #resources method
doesn’t work.

Example

Cookbook foo, recipes/default.rb

file ‘/tmp/foo.txt’ do
owner node[‘foo’][‘user’]
end

Cookbook bar, recipes/default.rb
include_recipe ‘foo::default’

user 'new_user’
node.override[‘foo’][‘user’] = ‘new_user’

this doesn’t work:

resources(file: ‘/tmp/foo.txt’).owner( lazy { node[‘foo’][‘user’] })

you have to do this:

file ‘/tmp/foo.txt’ do
owner lazy { node[‘foo’][‘user’] }
end

Ideally, I’d be able to do something like this:

resources(file: ‘/tmp/foo.txt’).lazy(:owner)


Justin Dossey
Practice Owner
New Context Services, Inc

On Oct 16, 2014, at 10:49 AM, Justin Dossey justin.dossey@newcontext.com wrote:

I ran into an interesting one today.

If you're wrapping a (community) cookbook and your wrapper needs lazy {} to move evaluation of something to the execute phase, the #resources method doesn't work.

Example

Cookbook foo, recipes/default.rb

file '/tmp/foo.txt' do
owner node['foo']['user']
end

Cookbook bar, recipes/default.rb
include_recipe 'foo::default'

user 'new_user'
node.override['foo']['user'] = 'new_user'

this doesn't work:

resources(file: '/tmp/foo.txt').owner( lazy { node['foo']['user'] })

Use this:

resources(file: '/tmp/foo.txt').owner(Chef::DelayedEvaluator.new { node['foo']['user'] })

#lazy is just a helper method for that, but only available in the resource class scope.

--Noah

This excellent nugget of information led me to create a nifty little recipe
showing the agony we put Chef through in our daily lives.

Here's my recipe, bar::default (gist at
Chef recipe showing order of lazy evaluation · GitHub )

node.default.bar.tags = ['tag1-bar']

file "/tmp/bar.txt" do
content lazy { node.bar.tags.join(',') }
end

ruby_block 'update second tag' do
block { node.default.bar.tags << 'tag2-bar' }
only_if { node.default.bar.tags << 'tag3-bar' }
notifies :create, 'file[/tmp/bar.txt]', :delayed
end

r = resources(file: '/tmp/bar.txt')
r.content(Chef::DelayedEvaluator.new {
node.default.bar.tags << 'tag4-bar'
node.bar.tags.join(',')
})

Quickly: what will be written to my file /tmp/bar.txt?

kitchen converge output at

I wasn't sure what I was going to get-- that was the point of the
experiment-- but I was certainly surprised to get what I did!

On Thu, Oct 16, 2014 at 11:11 AM, Noah Kantrowitz noah@coderanger.net
wrote:

On Oct 16, 2014, at 10:49 AM, Justin Dossey justin.dossey@newcontext.com
wrote:

I ran into an interesting one today.

If you're wrapping a (community) cookbook and your wrapper needs lazy {}
to move evaluation of something to the execute phase, the #resources method
doesn't work.

Example

Cookbook foo, recipes/default.rb

file '/tmp/foo.txt' do
owner node['foo']['user']
end

Cookbook bar, recipes/default.rb
include_recipe 'foo::default'

user 'new_user'
node.override['foo']['user'] = 'new_user'

this doesn't work:

resources(file: '/tmp/foo.txt').owner( lazy { node['foo']['user'] })

Use this:

resources(file: '/tmp/foo.txt').owner(Chef::DelayedEvaluator.new {
node['foo']['user'] })

#lazy is just a helper method for that, but only available in the resource
class scope.

--Noah

--
Justin Dossey
Practice Owner
New Context Services, Inc