Skip to content

Instantly share code, notes, and snippets.

@jbgo
Created February 15, 2014 19:16
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jbgo/9023884 to your computer and use it in GitHub Desktop.
Save jbgo/9023884 to your computer and use it in GitHub Desktop.
Heroku-style config management with capistrano and chef
set :application, "app_name"
set :rails_root, File.expand_path('../..', __FILE__)
# ... more custom config
require 'capistrano/ext/multistage'
set :stages, %w(staging production)
set :default_stage, "staging"
set :rails_env, -> { stage }
# ... more custom config
require 'dotenv/capistrano'
# ... more custom config
load "#{fetch :rails_root}/config/deploy/recipes/config.rb"
# ... more custom config
require 'chef'
require 'chef/knife'
before "config:get", "config:load_chef"
before "config:set", "config:load_chef"
namespace :config do
desc "Show application config"
task :default do
top.config.get
end
desc "Get application config"
task :get do
env = data_bag_item[stage.to_s]['env']
env.keys.sort.each do |k|
puts "#{k}=#{env[k]}"
end
end
desc "Set application config"
task :set do
item = data_bag_item
item[stage.to_s]['env'].merge! env_updates_from_args
enc = encrypted_data_bag_item item
new_item = Chef::DataBagItem.from_hash enc
new_item.data_bag('apps')
new_item.save
top.config.apply
end
task :apply do
wait_for 5, 'waiting for config changes to propagate...'
sudo 'chef-client'
top.deploy.restart
end
task :load_chef do
knife = Chef::Knife.new
Chef::Config[:verbosity] = 1
knife.configure_chef
end
def wait_for(t=5, message=nil)
logger.info message if message
t.times { sleep 1; print '.' }
print "\n"
end
def get_args_for_task(task_name)
args = ARGV.dup
_ = args.shift until _ == task_name
args
end
def env_updates_from_args
args = get_args_for_task 'config:set'
Hash[args.map do |arg|
m = arg.match(/\A(\w+)=(.*)\z/)
raise "Not a valid environment variable: #{arg}" if m.nil?
[m[1], m[2]]
end]
end
def encrypted_data_bag_item(item)
Chef::EncryptedDataBagItem.encrypt_data_bag_item(
item, encrypted_data_bag_secret).to_hash
end
def data_bag_item
@_data_bag_item ||= Chef::EncryptedDataBagItem.load(
'apps', 'app_name', encrypted_data_bag_secret).to_hash
end
def encrypted_data_bag_secret
File.read(Chef::Config[:encrytped_data_bag_secret]).chomp
end
end
app_user = node['app_name']['app_user']
stage = node.chef_environment
conf = Chef::EncryptedDataBagItem.load('apps', 'app_name')[stage]
template "/home/apps/app_name/shared/.env" do
source "env.erb"
owner app_user
group app_user
mode '0600'
action :create
variables(env_vars: conf['env'])
end
<% @env_vars.each do |key, val| %>
<%= key %>=<%= val %>
<% end %>
{
"id": "app_name",
"production": {
"env": {
"KEY1": "value1",
"KEY2": "value2",
}
},
"staging": {
"env": {
"KEY1": "staging-value1",
"KEY2": "staging-value2",
}
}
}
gem 'dotenv-rails'
group :deployment do
gem 'capistrano', '~>2.15'
gem 'chef'
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment