Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to read cookbook file at recipe compilation time?

Tags:

chef-infra

This kind of thing is commonly seen in Chef recipes:

%w{foo bar baz}.each do |x|
  file "#{x}" do
    content "whatever"
  end
end

But I want to read the items to loop over from a file which is kept with the cookbook, for example:

File.open('files/default/blah.txt').each do |x|
  file "#{x}" do
    content "whatever"
  end
end

This works if I give the full path to blah.txt where chef-client happens to cache it, but it's not portable. It doesn't work if I write it like in the example, "hoping" that the current directory is the root of the cookbook. Is there a way to obtain the cookbook root directory as the recipes are compiled?

like image 363
Peter Eisentraut Avatar asked Jan 08 '14 21:01

Peter Eisentraut


2 Answers

In Chef 11, you can get clever and use Dir globbing to achieve your desired behavior:

  1. Disable lazy loading of assets. With lazy asset loading enabled, Chef will fetch assets (like cookbook files, templates, etc) as they are requested during the Chef Client run. In your use case, you need those assets to exist on the server before the recipe execution starts. Add the following to the client.rb:

    no_lazy_load true
    
  2. Find the path to the cookbook's cache on disk. This is a little bit of magic and experimentation, but:

    "#{Chef::Config[:file_cache_path]}/cookbooks/NAME"
    
  3. Get the correct file:

    path = "#{Chef::Config[:file_cache_path]}/cookbooks/NAME/files/default/blah.txt"
    File.readlines(path).each do |line|
      name = line.strip
    
      # Whatever chef execution here...
    end
    

You might also want to look at Cookbook.preferred_filename_on_disk if you care about using the File Specificity handlers.

like image 54
sethvargo Avatar answered Oct 21 '22 01:10

sethvargo


Another solution, for those who don't need the file content until converge time, but one that does not require any client.rb modifications, is to use cookbook_file to read the resource into your file_cache_path and then lazy load it. Here is an example where I read in a groovy script to be embedded into an xml template.

script_file = "#{Chef::Config['file_cache_path']}/bootstrap-machine.groovy"
cookbook_file script_file do
    source 'bootstrap-machine.groovy'
end

config_xml = "#{Chef::Config['file_cache_path']}/bootstrap-machine-job.xml"

template config_xml do
    source 'bootstrap-machine-job.xml.erb'
    variables(lazy {{
        :groovy_script => File.open(script_file).read
    }})
end
like image 27
Kenneth Baltrinic Avatar answered Oct 21 '22 03:10

Kenneth Baltrinic