Today I joined the Food Fight Show for a conversation about Application Deployment. Along the way, the question came up about where to store application specific configuration files. Should they be stored in a Chef cookbook for setting up the system for the application? Or shoud they be stored in the application codebase itself?

The answer is either, as far as Chef is concerned. Chef’s template resource can render a template from a local file on disk, or retrieve the template from a cookbook. The latter is the most common pattern, so let’s examine the former, using a local file on disk.

For sake of discussion, let’s use a Rails application that needs a database.yml file rendered. Also, we’ll assume that information about the application (database user, password, server) we need is stored in a Chef data bag. Finally, we’re going to assume that the application is already deployed on the system somehow and we just want to render the database.yml.

The application source tree looks something like this:

myapp/
-> config/
    -> database.yml.erb

Note that there should not be a database.yml (non-.erb) here, as it will be rendered with Chef. The deployment of the app will end up in /srv, so the full path of this template is, for example, /srv/myapp/current/config/database.yml.erb. The content of the template may look like this:

<%= @rails_env %>:
  adapter: <%= @adapter %>
  host: <%= @host %>
  database: <%= @database %>
  username: <%= @username %>
  password: <%= @password %>
  encoding: 'utf8'
  reconnect: true

The Chef recipe looks like this. Note we’ll use a search to find the first node that should be the database master (there should only be one). For the adapter, we may have set an attribute in the role that selects the adapter to use.

results = search(:node, "role:myapp_database_master AND environment:#{node.chef_environment}")
db_master = results[0]

template "/srv/myapp/shared/database.yml" do
  source "/srv/myapp/current/config/database.yml.erb"
  local true
  variables(
    :rails_env => node.chef_environment,
    :adapter => db_master['myapp']['db_adapter'],
    :host => db_master['fqdn'],
    :database => "myapp_#{node.chef_environment}",
    :username => "myapp",
    :password => "SUPERSECRET",
  )
end

The rendered template, /srv/myapp/shared/database.yml, will look like this:

production:
  adapter: mysql
  host: domU-12-31-39-14-F1-C3.compute-1.internal
  database: myapp_production
  username: myapp
  password: SUPERSECRET
  encoding: utf8
  reconnect: true

This post is only part of the puzzle, mainly to explain what I mentioned on the Food Fight Show today. There are a number of unanswered questions like,

  • Should database.yml be .gitignore’d?
  • How do developers run the app locally?
  • How do I use this with Chef Solo?

As mentioned on the show, there’s currently a thread related to this topic on the Chef mailing list.