I can not believe this! I ran into a chef issue: file
content lazy
ruby block gets executed 3 times, but it is supposed to be only run once.
$ sudo cat /var/chef/cache/cookbooks/infra-tmp/recipes/default.rb
aaa = 'aaaaaa'
puts "=========before"
file '/tmp/a.pem' do
puts "==== inner begin"
content lazy { xxx(aaa) }
end
puts "=========end"
the xxx is a common function defined in infra-common libraries,
def xxx(aaa=nil)
puts "========xxx: #{aaa}"
puts caller
aaa
end
guess how many times the xxx get executed? 3!
$ sudo chef-client -o infra-tmp --skip-cookbook-sync
Starting Chef Client, version 13.12.3
...
Compiling Cookbooks...
=========before
==== inner begin
=========end
Converging 1 resources
Recipe: infra-tmp::default
* file[/tmp/a.pem] action create========xxx: aaaaaa
/var/chef/cache/cookbooks/infra-tmp/recipes/default.rb:9:in `block (2 levels) in from_file'
...
/opt/chef/embedded/lib/ruby/gems/2.4.0/gems/chef-13.12.3/lib/chef/provider/file.rb:191:in `managing_content?'
/opt/chef/embedded/lib/ruby/gems/2.4.0/gems/chef-13.12.3/lib/chef/provider/file.rb:94:in `load_current_resource'
/opt/chef/embedded/lib/ruby/gems/2.4.0/gems/chef-13.12.3/lib/chef/provider.rb:154:in `run_action'
...
========xxx: aaaaaa
/var/chef/cache/cookbooks/infra-tmp/recipes/default.rb:9:in `block (2 levels) in from_file'
...
/opt/chef/embedded/lib/ruby/gems/2.4.0/gems/chef-13.12.3/lib/chef/provider/file/content.rb:27:in `file_for_provider'
/opt/chef/embedded/lib/ruby/gems/2.4.0/gems/chef-13.12.3/lib/chef/file_content_management/content_base.rb:40:in `tempfile'
/opt/chef/embedded/lib/ruby/gems/2.4.0/gems/chef-13.12.3/lib/chef/provider/file.rb:450:in `tempfile'
/opt/chef/embedded/lib/ruby/gems/2.4.0/gems/chef-13.12.3/lib/chef/provider/file.rb:327:in `do_generate_content'
/opt/chef/embedded/lib/ruby/gems/2.4.0/gems/chef-13.12.3/lib/chef/provider/file.rb:140:in `action_create'
/opt/chef/embedded/lib/ruby/gems/2.4.0/gems/chef-13.12.3/lib/chef/provider.rb:171:in `run_action'
...
========xxx: aaaaaa
/var/chef/cache/cookbooks/infra-tmp/recipes/default.rb:9:in `block (2 levels) in from_file'
...
/opt/chef/embedded/lib/ruby/gems/2.4.0/gems/chef-13.12.3/lib/chef/provider/file/content.rb:29:in `file_for_provider'
/opt/chef/embedded/lib/ruby/gems/2.4.0/gems/chef-13.12.3/lib/chef/file_content_management/content_base.rb:40:in `tempfile'
/opt/chef/embedded/lib/ruby/gems/2.4.0/gems/chef-13.12.3/lib/chef/provider/file.rb:450:in `tempfile'
/opt/chef/embedded/lib/ruby/gems/2.4.0/gems/chef-13.12.3/lib/chef/provider/file.rb:327:in `do_generate_content'
/opt/chef/embedded/lib/ruby/gems/2.4.0/gems/chef-13.12.3/lib/chef/provider/file.rb:140:in `action_create'
/opt/chef/embedded/lib/ruby/gems/2.4.0/gems/chef-13.12.3/lib/chef/provider.rb:171:in `run_action'
...
I've checked the chef source code,
$ cat -n /opt/chef/embedded/lib/ruby/gems/2.4.0/gems/chef-13.12.3/lib/chef/provider/file/content.rb | grep -vE '^s*(#|$)'
18
19 require "chef/file_content_management/content_base"
20 require "chef/file_content_management/tempfile"
21
22 class Chef
23 class Provider
24 class File
25 class Content < Chef::FileContentManagement::ContentBase
26 def file_for_provider
27 if @new_resource.content
28 tempfile = Chef::FileContentManagement::Tempfile.new(@new_resource).tempfile
29 tempfile.write(@new_resource.content)
30 tempfile.close
31 tempfile
32 else
33 nil
34 end
35 end
36 end
37 end
38 end
39 end
Turns out it is because @new_resource.content
being referenced 3 times,
without using a local var to cache it.
Is this a bug of Chef?
Not sure whether other chef resource has same bug.
EDIT: I ended up with @var instance var to cache, instead of node.run_state which cause multi custom resources only use first cached value.
file ... do
...
content lazy {
@any_name || (@any_name = some_function(some_param))
}
...
end
question from:
https://stackoverflow.com/questions/65879139/can-not-believe-this-chef-lazy-block-get-executed-3-times 与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…