Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Chef templates with recursive directories

Tags:

chef-infra

I'm very new to Chef and am trying to figure out templating (which seems really cool). In my old deploy structure, I had a directory which I simply wanted to copy over. It had a number of configuration parameters scattered about throughout the files in the directory. I have gone ahead and tried to abstract those parameters away into an attribute file (much cleaner), but am having trouble installing it with Chef. I have modified the extensions of all the files with ERB in them to end with .erb (I come from a Rails background, so this seems natural to me). For instance, I had a file named run.conf, and it is now named run.conf.erb.

Ideally I'd like to have one template block in the recipe which just copies over all the files in the directory and updates those .erb files (removing the .erb extension) with the variables I provide. Here is an example of where I'm at so far:

template "#{node["dcm4chee"]["home"]}" do
  source "server/"
  variables(
    "java_mem_opts" => node["dcm4chee"]["java_mem_opts"],
    "db_username" => node["dcm4chee"]["admin"]["username"],
    "db_password" => node["dcm4chee"]["admin"]["password"],
    "db_hostname" => node["mysql"]["hostname"],
    "db_port" => node["mysql"]["port"]
)
end

I have put a folder called server under templates/default and that folder contains the files I want templated. The #{node["dcm4chee"]["home"]} variable is the location of where I want to put the files on the target machine. Ideally I'd like to do this without naming specific files within the recipe because that way I don't have to touch the recipe if I modify the contents of the server directory for deployment.

Is this possible? If so what am I doing wrong? If not, what are my alternatives.

Thanks

EDIT

After thinking about this a bit, I tried to use some custom ruby code to do this. Here is my current attempt which is failing with a NoMethodError referring to tempate_dir from the initial call within the ruby_block.

def template_dir(file)
  Dir.foreach("server") do |file|
    if File.file?(file)
      template "#{node["dcm4chee"]["home"]}/#{file}" do
        source "server/#{file}"
          variables(
            "java_mem_opts" => node["dcm4chee"]["java_mem_opts"],
            "db_username" => node["dcm4chee"]["admin"]["username"],
            "db_password" => node["dcm4chee"]["admin"]["password"],
            "db_hostname" => node["mysql"]["hostname"],
           "db_port" => node["mysql"]["port"]
          )
      end
    else
      directory "#{node["dcm4chee"]["home"]}/#{file}" do
        action :create
      end
      template_dir(file)
    end
  end
end

ruby_block "template the whole server directory" do
  block do
    template_dir("server")
  end
end
like image 722
Jon Avatar asked Apr 04 '13 20:04

Jon


1 Answers

You could define template_dir inside of your ruby_block instead of at the top level- that will work just fine.

find is a part of the Ruby standard library, and will recursively walk through a directory. Using it would result in something slightly cleaner:

ruby_block "recursive templating" do
    block do
        require 'find'
        root = 'server'
        Find.find(root) do |file|
            if File.file?(file)
                template "#{node["dcm4chee"]["home"]}/#{file}" do
                    source file
                    variables(
                        "java_mem_opts" => node["dcm4chee"]["java_mem_opts"],
                        "db_username" => node["dcm4chee"]["admin"]["username"],
                        "db_password" => node["dcm4chee"]["admin"]["password"],
                        "db_hostname" => node["mysql"]["hostname"],
                        "db_port" => node["mysql"]["port"]
                    )
                end
            end
        end
    end
end

In general, if you're writing any sort of mildly complicated logic, you should consider writing an LWRP instead of putting it into your recipe. The two-phase compilation/execution thing causes a lot of unintuitive behavior (like the fact that the block can't look up template_dir), and because writing an LWRP lets you validate input, write tests, and do a better job of ensuring idempotency.

Also, I'm a little confused about the 'server' string you have- I'm not sure in what environment that will resolve to your templates directory. In any case, if you want to get access to a list of all the templates in your cookbook, there is an array available here: run_context.cookbook_collection[cookbook_name].template_filenames

like image 132
Henry Finucane Avatar answered Oct 13 '22 00:10

Henry Finucane