-
-
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"]
endWhat 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...