How to use current cookbook template dir to copy all templates recursively in a loop with chef

Solution 1:

WARNING: This solution uses a private interface which may change or be removed without warning. Chef does not currently (13.x) provide a supported way of solving this problem - consider an alternative approach to solving your requirement.

Now that you've been warned, here's how you would do it in a recipe:

# Get the Chef::CookbookVersion for the current cookbook
cb = run_context.cookbook_collection[cookbook_name]

# Loop over the array of files.
# 'templates' will also work.
cb.manifest['files'].each do |cookbookfile|
  Chef::Log("found: " + cookbookfile['name'])
end

I've shared an example recipe to show this in context.

In practice, this needs to be more complicated - for example, you may only want a subset of the files/templates in the cookbook, or need to transform paths in some way. Consider writing a library function that makes a list of the files you are interested in, and calling that from your recipe.

Solution 2:

Thanks "zts". I created one to create template directory with all .erb files inside it.

cb = run_context.cookbook_collection[cookbook_name]
# you may get 'files' OR 'templates' from manifest
cb.manifest['templates'].each do |tmpl_manifest|
  filepath = tmpl_manifest['path']
  next if not filepath =~ /#{cmk_colo}\/server\/checks\/.*erb/
  filename = tmpl_manifest['name'].split(".erb")[0]
  fileshortpath=filepath.split("/",3)[2]
  template "/opt/omd/sites/prod/etc/check_mk/conf.d/checks/#{filename}" do
    source  fileshortpath
    mode '0644'
    owner 'prod'
    group 'prod'
  end
end

this one gets full/long path to all .erb files in templates dir. If path matches to name of directory you want to loop though, it creates it via standard template recipe.

Solution 3:

Thanks to @zts & @Zaur. I thought I'd post my complete answer which ended up combining both. The biggest thing to note is that your cookbook name must be in quotes. That wasn't obvious to me and was a bit of a blocker in my case.

2nd, instead of using @Zaur's RegEx search for filtering file paths, I'm using a more "ruby-way" of doing string comparison to see if the path contains a specific directory:

# Get the Chef::CookbookVersion for this cookbook
cb = run_context.cookbook_collection['cookbook_name']

# Loop over the array of files
cb.manifest['files'].each do |cbf|
  # cbf['path'] is relative to the cookbook root, eg
  #   'files/default/foo.txt'
  # cbf['name'] strips the first two directories, eg
  #   'foo.txt'
  filepath = cbf['path']
  filename = cbf['name']

  next if not filepath.include? "directory-to-filter-for/"

  cookbook_file "/etc/service/conf.d/#{filename}" do
    source "directory-to-filter-for/#{filename}"
    mode 0600
    owner "root"
    group "root"
  end
end

I'm using this for files, but you can use templates as well. Just substitute the 'template' block instead of the 'file' block at line 14. Then change line 5 to use templates instead of files:

cb.manifest['templates'].each do |cbf|

Solution 4:

In Chef > 12.19 there is a breaking change (RFC 67) to the manifest which combines all files in a cookbook under :all_files

See https://chef.github.io/chef-rfc/rfc067-cookbook-segment-deprecation.html

To return a list of all templates, you now use:

run_context.cookbook_collection[:examplecookbook].files_for('templates')