1: file '/tmp/my_file' do
2: content 'hello, world'
3: end
4:
5: my_file_data = ::File.read('/tmp/my_file')
6:
7: file '/tmp/other_file' do
8: content my_file_data
9: end
A naive reading of this recipe would suggest that three things happen: a new file called "/tmp/my_file" will be created (if missing) containing the string "hello, world", the file will be read into the my_file_data variable, and another file "/tmp/other_file" will be created with the same content.
That's not what actually happens here. In fact, this recipe won't converge at all unless /tmp/my_file has been previously created by something else. Instead, you'll get an Errno::ENOENT.
The ::File.read() call is evaluated in the compile phase, but the file resources are created in the execute phase, which happens after the compile phase has completed! If you need to access something created in the execute phase, the accessing code also needs to evaluate in the execute phase, or you can push the file resources into the compile phase. Moving resource convergence into the compile phase isn't ideal, though, so I generally advise the former over the latter.
Here's one way to make this recipe converge:
1: file '/tmp/my_file' do
2: content 'hello, world'
3: end
4:
5: my_file_data = nil
6: ruby_block 'read_file' do
7: block { my_file_data = ::File.read('/tmp/my_file') }
8: end
9:
10: file '/tmp/other_file' do
11: content lazy { my_file_data }
12: end
Three things are changed here.- The my_file_data variable is declared in the compile phase to make it scoped appropriately for the /tmp/other_file file resource.
- The reading of that file is put into a ruby_block resource to make it happen during the execute phase.
- The content of the /tmp/other_file resource is enclosed in a lazy block to delay evaluation of the my_file_data variable until the execute phase.
Keep in mind when your code is being evaluated when you write your recipes.