-
-
Save finack/2055725 to your computer and use it in GitHub Desktop.
app = node[:rails][:app] | |
rails_base app[:name] do | |
ruby_ver app[:ruby_ver] | |
gemset app[:gemset] | |
end | |
%w{config log pids cached-copy bundle system}.each do |dir| | |
directory "#{app[:app_root]}/shared/#{dir}" do | |
owner app[:deploy_user] | |
group app[:deploy_user] | |
mode '0755' | |
recursive true | |
action :create | |
end | |
end | |
keys = Chef::EncryptedDataBagItem.load('rails', app[:name]) | |
create_database_config app[:name] do | |
app_root node[:rails][:app][:app_root] | |
deploy_user node[:rails][:app][:deploy_user] | |
databases node[:rails][:app][:db][:databases] | |
aliases node[:rails][:app][:db][:aliases] | |
passwords keys["database_passwords"] | |
end | |
deploy_revision "#{app[:app_root]}" do | |
repo app[:repo] | |
revision cluster_settings["revision"] | |
user app[:deploy_user] | |
group app[:deploy_user] | |
enable_submodules true | |
environment({ | |
"RAILS_ENV" => app[:environment], | |
}) | |
shallow_clone false | |
if node["rails"][app[:name]].attribute?('one_time_action') | |
# rollback, deploy, force_deploy | |
action node["rails"][app[:name]]["one_time_action"] | |
node["rails"][app[:name]].delete('one_time_action') | |
else | |
action :deploy | |
end | |
migrate true | |
migration_command "/usr/local/rvm/bin/appname_bundle exec rake db:migrate --trace" | |
before_migrate do | |
template "#{release_path}/config/environments/#{app[:environment]}.rb" do | |
source "#{release_path}/config/environments/chef_development.rb.erb" | |
local true | |
owner app[:deploy_user] | |
group app[:deploy_user] | |
mode '0400' | |
variables(:environment_settings => app[:environment_settings].to_hash) | |
end | |
rvm_shell "Bundle install and assets precompile" do | |
ruby_string "#{app[:ruby_ver]}@#{app[:gemset]}" | |
cwd release_path | |
user app[:deploy_user] | |
group app[:deploy_user] | |
# common_groups = %w{development test cucumber} | |
# bundle install --deployment --path #{app[:app_root]}/shared/bundle --without #{common_groups.join(' ')} | |
code %{ | |
bundle install --path #{app[:app_root]}/shared/bundle | |
} | |
end | |
end | |
before_restart do | |
rvm_shell "assets precompile" do | |
ruby_string "#{app[:ruby_ver]}@#{app[:gemset]}" | |
cwd release_path | |
user app[:deploy_user] | |
group app[:deploy_user] | |
# TODO I could not set the environment via the builtin command. Does not look like it is getting passed to popen4 | |
# So instead of `environment "RAILS_ENV" => app[:environment]` I have it in the code block | |
code %{ | |
export RAILS_ENV=#{app[:environment]} | |
bundle exec rake assets:precompile | |
} | |
end | |
end | |
symlink_before_migrate({ | |
"config/database.yml" => "config/database.yml", | |
"config/environments/#{app[:environment]}.rb" => "config/environments/#{app[:environment]}.rb" | |
}) | |
after_restart do | |
notify_airbrake release_path do | |
environment app[:environment] | |
repository app[:repo] | |
end | |
notify_newrelic release_path do | |
environment app[:environment] | |
end | |
end | |
end |
#Used to configure variables for the app per environment (instead of attributes). | |
#I have since refactored how this works so treat this as example code, aka I | |
#have not tested this and it is not complete. | |
#FWIW using libraries in the cookbook to set these variables | |
#for smaller apps I would keep this in the attributes | |
app_name = 'yourname' | |
default = { | |
name: app_name | |
gemset: app_name | |
ruby_ver: 'ruby-1.8.7-p352', | |
deploy_user: 'deployer', | |
environment: node[:environment], | |
#... | |
} | |
staging = { | |
app_root: '/export/staging/yourname', | |
#... | |
} | |
case node[:environment] | |
when "staging" | |
rails_attributes = Chef::Mixin::DeepMerge.merge(default, staging) | |
end | |
# Don't know why I saved this in a seperate hash before the deep merge... | |
original_node_attributes = node.default.to_hash | |
node.default[:rails] = Chef::Mixin::DeepMerge.merge(rails_attributes, original_node_attributes["rails"]) |
define :create_database_config, :passwords=>{} do | |
databases = Hash.new | |
params[:databases].each do |db_name| | |
if node[:rails][params[:name]][:db].has_key?(db_name) | |
values = node[:rails][params[:name]][:db][db_name].to_hash | |
if params[:passwords].has_key?(db_name.to_s) | |
values["password"] = params[:passwords].fetch(db_name.to_s) | |
end | |
name = values.has_key?(name) ? values[:name] : db_name | |
databases.merge!({ name.to_s => values }) | |
end | |
end | |
if params.has_key? :aliases | |
params[:aliases].each do |target,source| | |
databases[target.to_s] = databases[source.to_s] | |
end | |
end | |
file "#{params[:app_root]}/shared/config/database.yml" do | |
owner params[:deploy_user] | |
group params[:deploy_user] | |
mode '400' | |
content databases.to_yaml | |
end | |
end |
define :notify_airbrake, :name=>nil, :environment=>nil, :repository=>nil do | |
script "notifying airbrake of deploy" do | |
interpreter "ruby" | |
user "deployer" | |
cwd params[:name] | |
code <<-EOS | |
require 'rubygems' | |
unless File.exist?("#{params[:name]}/config/initializers/airbrake.rb") | |
puts "!!! airbrake.rb not found. Not notifying Airbrake of the deploy !!!" | |
exit 0 | |
end | |
# Setup the airbrake api_key | |
require 'airbrake' | |
require 'airbrake_tasks' | |
require "#{params[:name]}/config/initializers/airbrake.rb" | |
revision = `cd #{params[:name]} && git rev-parse HEAD`.chomp | |
print "Airbrake : " | |
AirbrakeTasks.deploy(:rails_env => '#{params[:environment]}', | |
:scm_revision => revision, | |
:scm_repository => '#{params[:repository]}', | |
:local_username => 'chef') | |
EOS | |
end | |
end |
define :notify_newrelic, :deploy_path=>nil, :environment=>nil do | |
script "notifying new relic of deploy" do | |
interpreter "ruby" | |
user "deployer" | |
cwd params[:name] | |
code <<-EOS | |
require 'rubygems' | |
unless File.exist?("#{params[:name]}/config/newrelic.yml") | |
puts "!!! newrelic.yml not found. Not notifying New Relic of the deploy !!!" | |
exit 0 | |
end | |
## When new relic is loaded it automagically loads up its configuration file | |
## And sets the config[:environment]. We are injecting the path to the configuration | |
## file and the environment via ENV variables. | |
ENV['APP_ROOT'] = "#{params[:name]}" | |
ENV['RAILS_ENV'] = "#{params[:environment]}" | |
require 'newrelic_rpm' | |
require 'new_relic/command' | |
config = NewRelic::Control.instance | |
revision = `git rev-parse HEAD`.chomp | |
deployment = NewRelic::Command::Deployments.new({:revision => revision, :user => 'chef'}) | |
print "New Relic : " | |
deployment.run | |
EOS | |
end | |
end |
define :rails_base, :ruby_ver=>nil, :gemset=>nil do | |
include_recipe "nodejs" | |
package "mysql-client" | |
package "libmysqlclient-dev" | |
gem_package "newrelic_rpm" | |
gem_package "airbrake" | |
directory '/export' do | |
owner 'root' | |
group 'root' | |
recursive true | |
action :create | |
only_if "test -e /export" | |
end | |
create_deploy_user | |
rvm_gemset params[:gemset] do | |
ruby_string params[:ruby_ver] | |
action :create | |
end | |
rvm_gem 'bundler' do | |
ruby_string "#{params[:ruby_ver]}@#{params[:gemset]}" | |
end | |
# Creates /usr/local/rvm/bin/appname_bundle | |
rvm_wrapper params[:name] do | |
ruby_string "#{params[:ruby_ver]}@#{params[:gemset]}" | |
binary 'bundle' | |
end | |
end |
{ | |
# Handwritten from memory, probably broken | |
"id": "yourname", | |
"database_passwords": { | |
"staging": "foo" | |
} | |
} | |
How the attributes will look like along with data_bag item as well?
Hey there!
Updated the code a bit. Hopefully that helped. I have been using one of the unicorn cookbooks (i think from opscode) and apache to serve all of this. I have been separating out the app deployment from the server as we also deploy delay jobs and other systems that need RoR but not unicorn. For example our run_lists kinda look like
- www_front_end = app, app_apache # Want apache and assets
- app_server = app, app_unicorn
- dj_server = app, app_dj
Hopefully this answered some of your questions. I will try to get to others but will be delayed :) Have fun!!
@finack, Thanks for the update and its really helpful.
But a few pieces left out to make it work for me.
First, the following definition to create the database.
create_database_config app[:name] do
app_root node[:rails][:app][:app_root]
deploy_user node[:rails][:app][:deploy_user]
databases node[:rails][:app][:db][:databases]
aliases node[:rails][:app][:db][:aliases]
passwords keys["database_passwords"]
end
What would be the structure of the attributes/default.rb
file?
Does the following work or I think I need to change the attributes?
default['rails']['app']['app_root'] = "/home/deployer"
default['rails']['railsbp']['db']['railsbp_prod']['encoding'] = "utf8"
default['rails']['railsbp']['db']['railsbp_prod']['reconnect'] = "true"
default['rails']['railsbp']['db']['railsbp_prod']['adapter'] = "mysql2"
And lastly, what that source "#{release_path}/config/environments/chef_development.rb.erb"
template looks like?
Yeah, I know I'm aksing a lot. But your help is directing me to the final deploy.
Can you address those queries?
And what is the app server it will use? I mean Unicorn, Nginx, et...