diff --git a/README.md b/README.md index 52953a1..710cbff 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # Description This cookbook is designed to be able to run [Errbit](http://github.com/errbit/errbit). -Its github is at [chef-errbit](https://github.com/millisami/chef-errbit) +Its github is at [chef-errbit](https://github.com/klamontagne/chef-errbit) # Requirements @@ -9,12 +9,14 @@ Developed using chef 10.14.0 and it should work with higher versions. But not te The following Opscode cookbooks are dependencies: -* mongodb * git -* unicorn * apt * nginx +You also need a MongoDB installation, such as with the [mongodb cookbook](https://github.com/edelight/chef-mongodb). + +If you have other installations of rbenv on the node, you need to edit the node's user_installs as described in [chef-rbenv's documentation](https://github.com/fnichol/chef-rbenv#-rbenv-installed-for-a-specific-user-with-rubies). + # Usage Just to install the Errbit app, include the following in your wrapper cookbook's recipe diff --git a/Vagrantfile b/Vagrantfile index 9fc76ed..670740a 100644 --- a/Vagrantfile +++ b/Vagrantfile @@ -46,7 +46,6 @@ Vagrant.configure("2") do |config| } chef.run_list = [ - 'recipe[errbit::install_ruby]', 'recipe[nginx]', 'recipe[errbit::default]', 'recipe[errbit::bootstrap]' diff --git a/attributes/default.rb b/attributes/default.rb index ec1a4b0..1516a0d 100644 --- a/attributes/default.rb +++ b/attributes/default.rb @@ -7,15 +7,21 @@ # All rights reserved - Do Not Redistribute # + + default['errbit']['name'] = "errbit" -default['errbit']['user'] = "deployer" -default['errbit']['password'] = "$1$qqO27xay$dtmwY9NMmJiSa47xhUZm0." #errbit +default['errbit']['user'] = "errbit" default['errbit']['group'] = node['errbit']['user'] default['errbit']['deploy_to'] = "/home/#{default['errbit']['user']}/#{node['errbit']['name']}" default['errbit']['repo_url'] = "git://github.com/errbit/errbit.git" default['errbit']['revision'] = "master" default['errbit']['environment'] = "production" +# Local ruby to install via rbenv +default['errbit']['install_ruby'] = "1.9.3-p429" +default['rbenv']['user_installs'] = [{ 'user' => default['errbit']['user'] }] + + # errbit config.yml default['errbit']['config']['host'] = "errbit.example.com" default['errbit']['config']['enforce_ssl'] = false @@ -43,6 +49,14 @@ # app server (Optional: More info in README) default['errbit']['server'] = "unicorn" # or use others like puma +default[:errbit][:unicorn][:worker_timeout] = 60 +default[:errbit][:unicorn][:worker_processes] = 2 #[node[:cpu][:total].to_i * 4, 8].min +default[:errbit][:unicorn][:preload_app] = false +default[:errbit][:unicorn][:tcp_nodelay] = true +default[:errbit][:unicorn][:backlog] = 100 +default[:errbit][:unicorn][:tcp_nopush] = true +default[:errbit][:unicorn][:tries] = 3 + default['errbit']['secret_token'] = 'b9e131c733a2672c79af5699f26e0bc5fba23a40ec51d76c9271c00097f35aa4c0993e1150f08048f0b66bd141cbcb58ab28814e35eb281c3cb2374aac160203' diff --git a/metadata.json b/metadata.json index 36c927d..317f44f 100644 --- a/metadata.json +++ b/metadata.json @@ -9,14 +9,15 @@ "ubuntu": ">= 0.0.0" }, "dependencies": { - "mongodb": ">= 0.0.0", "git": ">= 0.0.0", - "unicorn": ">= 0.0.0", + "ruby_build": ">= 0.0.0", + "rbenv": ">= 0.0.0", "apt": ">= 0.0.0", "nginx": ">= 0.0.0", "build-essential": ">= 0.0.0" }, "recommendations": { + "mongodb": ">= 0.0.0" }, "suggestions": { }, @@ -33,4 +34,4 @@ "recipes": { }, "version": "0.4.0" -} \ No newline at end of file +} diff --git a/metadata.rb b/metadata.rb index e5b65b8..74e712e 100644 --- a/metadata.rb +++ b/metadata.rb @@ -6,11 +6,13 @@ long_description IO.read(File.join(File.dirname(__FILE__), 'README.md')) version "0.4.0" -depends "mongodb" depends "git" -depends "unicorn" +depends "ruby_build" +depends "rbenv" depends "apt" depends "nginx" depends "build-essential" +recommends "mongodb" + supports "ubuntu" diff --git a/recipes/install_ruby.rb b/recipes/install_ruby.rb deleted file mode 100644 index cfb7575..0000000 --- a/recipes/install_ruby.rb +++ /dev/null @@ -1,21 +0,0 @@ -# Helper recipe to install ruby while cooking -# This is added in the run_list in the Vagrantfile while cooking this cookbook - -package "python-software-properties" - -execute "apt-add-repository -y ppa:brightbox/ruby-ng" do - not_if "test -t /etc/apt/sources.list.d/brightbox-ruby-ng-#{node['lsb']['codename']}.list" -end - -execute "remove tmp list" do - command "cd /etc/apt/sources.list.d && rm ruby-packaged-source.list && rm ruby-packaged-source.list.save" - only_if "test -t /etc/apt/sources.list.d/ruby-packaged-source.list" -end - -execute "apt-get -y update" do - not_if "test -f /usr/bin/ruby" -end - -execute "apt-get -y install ruby1.9.3" do - not_if "test -f /usr/bin/ruby" -end diff --git a/recipes/setup.rb b/recipes/setup.rb index 86d74fb..cadc358 100644 --- a/recipes/setup.rb +++ b/recipes/setup.rb @@ -18,47 +18,54 @@ # limitations under the License. # -include_recipe "mongodb::10gen_repo" - node.set['build_essential']['compiletime'] = true include_recipe "build-essential" include_recipe "git" -gem_package "bundler" +include_recipe "nginx" group node['errbit']['group'] + user node['errbit']['user'] do action :create - comment "Deployer user" + comment "Errbit user" gid node['errbit']['group'] shell "/bin/bash" home "/home/#{node['errbit']['user']}" - password node['errbit']['password'] supports :manage_home => true - system true + system false end # Exporting the SECRET_TOKEN env var secret_token = rand(8**256).to_s(36).ljust(8,'a')[0..150] -# execute "set SECRET_TOKEN var" do -# command "echo 'export SECRET_TOKEN=#{secret_token}' >> ~/.bash_profile" -# not_if "grep SECRET_TOKEN ~/.bash_profile" -# end -file "/etc/profile.d/errbit_env.sh" do - mode "0644" - action :create_if_missing - content "export SECRET_TOKEN=#{secret_token}\nexport RAILS_ENV=production\nexport RACK_ENV=production\n" +execute "set SECRET_TOKEN var" do + user node['errbit']['user'] + command "echo 'export SECRET_TOKEN=#{secret_token}' >> /home/#{node['errbit']['user']}/.bash_profile" + not_if "grep SECRET_TOKEN /home/#{node['errbit']['user']}/.bash_profile" end -# execute "set RAILS_ENV var" do -# command "echo 'export RAILS_ENV=production' >> ~/.bash_profile" -# not_if "grep RAILS_ENV ~/.bash_profile" -# end +# setup rbenv (after git user setup) +%w{ ruby_build rbenv::user_install }.each do |requirement| + include_recipe requirement +end -# execute "set RACK_ENV var" do -# command "echo 'export RACK_ENV=production' >> ~/.bash_profile" -# not_if "grep RACK_ENV ~/.bash_profile" -# end +# Install appropriate Ruby with rbenv +rbenv_ruby node['errbit']['install_ruby'] do + action :install + user node['errbit']['user'] +end + +# Set as the rbenv default ruby +rbenv_global node['errbit']['install_ruby'] do + user node['errbit']['user'] +end + +# Install required Ruby Gems(via rbenv) +rbenv_gem "bundler" do + action :install + user node['errbit']['user'] + rbenv_version node['errbit']['install_ruby'] +end execute "update sources list" do command "apt-get update" @@ -75,7 +82,6 @@ directory node['errbit']['deploy_to'] do owner node['errbit']['user'] group node['errbit']['group'] - mode 00755 action :create recursive true end diff --git a/recipes/unicorn.rb b/recipes/unicorn.rb index 0e0562e..55a60dc 100644 --- a/recipes/unicorn.rb +++ b/recipes/unicorn.rb @@ -18,32 +18,26 @@ # limitations under the License. # -include_recipe 'unicorn' - -node.default[:unicorn][:worker_timeout] = 60 -node.default[:unicorn][:worker_processes] = 2 #[node[:cpu][:total].to_i * 4, 8].min -node.default[:unicorn][:preload_app] = false -node.default[:unicorn][:tcp_nodelay] = true -node.default[:unicorn][:backlog] = 100 -node.default[:unicorn][:tcp_nopush] = true -node.default[:unicorn][:tries] = 3 -# node.default[:unicorn][:delay] = 100 - Chef::Log.info "-" * 70 Chef::Log.info "Unicorn Config" -template "#{node['errbit']['deploy_to']}/shared/config/unicorn.conf" do - source "unicorn.conf.erb" +template "#{node['errbit']['deploy_to']}/shared/config/unicorn.rb" do + source "unicorn.rb.erb" owner node['errbit']['user'] group node['errbit']['group'] mode 00644 end template "/etc/init.d/unicorn_#{node['errbit']['name']}" do - source "unicorn.service.erb" + source "unicorn.init.erb" owner "root" group "root" mode 00755 + variables( + :user => node['errbit']['user'], + :deploy_to => node['errbit']['deploy_to'], + :env => node['errbit']['environment'] + ) end service "unicorn_#{node['errbit']['name']}" do @@ -59,5 +53,11 @@ # Restarting the unicorn service "unicorn_#{node['errbit']['name']}" do - action :restart + action :nothing + subscribes :restart, "template[#{node['errbit']['deploy_to']}/shared/config/config.yml]" + subscribes :restart, "template[/etc/init.d/unicorn_#{node['errbit']['name']}]" + subscribes :restart, "template[#{node['errbit']['deploy_to']}/shared/config/unicorn.rb]" + subscribes :restart, "template[#{node['errbit']['deploy_to']}/shared/config/config.yml]" + subscribes :restart, "template[#{node['errbit']['deploy_to']}/shared/config/mongoid.yml]" + subscribes :restart, "deploy_revision[#{node['errbit']['deploy_to']}]" end diff --git a/templates/default/unicorn.init.erb b/templates/default/unicorn.init.erb new file mode 100644 index 0000000..1b62eb1 --- /dev/null +++ b/templates/default/unicorn.init.erb @@ -0,0 +1,126 @@ +#! /bin/bash + +### BEGIN INIT INFO +# Provides: errbit +# Required-Start: $local_fs $remote_fs $network $syslog +# Required-Stop: $local_fs $remote_fs $network $syslog +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 +# Short-Description: Errbit +# Description: Errbit open source error catcher +### END INIT INFO + + +APP_ROOT="<%= @deploy_to %>" +APP_USER="<%= @user %>" +DAEMON_OPTS="-D -c $APP_ROOT/shared/config/unicorn.rb" +PID_PATH="$APP_ROOT/shared/tmp/pids" +SOCKET_PATH="$APP_ROOT/shared/tmp/sockets" +WEB_SERVER_PID="$APP_ROOT/shared/pids/unicorn.pid" + +NAME="errbit" +DESC="Errbit service" + +check_pid(){ + if [ -f $WEB_SERVER_PID ]; then + PID=`cat $WEB_SERVER_PID` + STATUS=`ps aux | grep $PID | grep -v grep | wc -l` + else + STATUS=0 + PID=0 + fi +} + +execute() { + sudo -u $APP_USER -H bash -l -c "cd \"$APP_ROOT/current\" ; $1" +} + +start() { + cd $APP_ROOT + check_pid + if [ "$PID" -ne 0 -a "$STATUS" -ne 0 ]; then + # Program is running, exit with error code 1. + echo "Error! $DESC $NAME is currently running!" + exit 1 + else + if [ `whoami` = root ]; then + execute "rm -f $SOCKET_PATH/errbit.socket" + execute "RAILS_ENV=<%= @env %> bundle exec unicorn $DAEMON_OPTS" + echo "$DESC started" + fi + fi +} + +stop() { + cd $APP_ROOT + check_pid + if [ "$PID" -ne 0 -a "$STATUS" -ne 0 ]; then + ## Program is running, stop it. + kill -QUIT `cat $WEB_SERVER_PID` + rm "$WEB_SERVER_PID" >> /dev/null + echo "$DESC stopped" + else + ## Program is not running, exit with error. + echo "Error! $DESC not started!" + exit 1 + fi +} + +restart() { + cd $APP_ROOT + check_pid + if [ "$PID" -ne 0 -a "$STATUS" -ne 0 ]; then + echo "Restarting $DESC..." + stop + sleep 2 + start + echo "$DESC restarted." + else + echo "$NAME not running" + start + fi +} + +status() { + cd $APP_ROOT + check_pid + if [ "$PID" -ne 0 -a "$STATUS" -ne 0 ]; then + echo "$DESC / Unicorn with PID $PID is running." + else + echo "$DESC is not running." + exit 1 + fi +} + +## Check to see if we are running as root first. +## Found at http://www.cyberciti.biz/tips/shell-root-user-check-script.html +if [ "$(id -u)" != "0" ]; then + echo "This script must be run as root" + exit 1 +fi + +case "$1" in + start) + start + ;; + stop) + stop + ;; + restart) + restart + ;; + reload|force-reload) + echo -n "Reloading $NAME configuration: " + kill -HUP `cat $PID` + echo "done." + ;; + status) + status + ;; + *) + echo "Usage: sudo service $NAME {start|stop|restart|reload}" >&2 + exit 1 + ;; +esac + +exit 0 diff --git a/templates/default/unicorn.conf.erb b/templates/default/unicorn.rb.erb similarity index 91% rename from templates/default/unicorn.conf.erb rename to templates/default/unicorn.rb.erb index 8c13964..70e9cff 100644 --- a/templates/default/unicorn.conf.erb +++ b/templates/default/unicorn.rb.erb @@ -1,4 +1,4 @@ -worker_processes <%= node[:unicorn][:worker_processes] %> +worker_processes <%= node[:errbit][:unicorn][:worker_processes] %> user "<%= node['errbit']['user'] %>" @@ -7,10 +7,10 @@ working_directory "<%= node['errbit']['deploy_to']%>/current" # listen on both a Unix domain socket and a TCP port, # use a shorter backlog for quicker failover when busy -listen "<%= node['errbit']['deploy_to']%>/shared/sockets/unicorn.sock", :backlog => <%= node[:unicorn][:backlog] %> +listen "<%= node['errbit']['deploy_to']%>/shared/sockets/unicorn.sock", :backlog => <%= node[:errbit][:unicorn][:backlog] %> listen 8080, :tcp_nopush => true -timeout <%= node[:unicorn][:worker_timeout] %> +timeout <%= node[:errbit][:unicorn][:worker_timeout] %> pid "<%= node['errbit']['deploy_to']%>/shared/pids/unicorn.pid" @@ -19,7 +19,7 @@ stdout_path "<%= node['errbit']['deploy_to']%>/shared/log/unicorn.stdout.log" # combine REE with "preload_app true" for memory savings # http://rubyenterpriseedition.com/faq.html#adapt_apps_for_cow -preload_app <%= node[:unicorn][:preload_app] %> +preload_app <%= node[:errbit][:unicorn][:preload_app] %> GC.copy_on_write_friendly = true if GC.respond_to?(:copy_on_write_friendly=) # ensure Unicorn doesn't use a stale Gemfile when restarting diff --git a/templates/default/unicorn.service.erb b/templates/default/unicorn.service.erb deleted file mode 100644 index eaad0d0..0000000 --- a/templates/default/unicorn.service.erb +++ /dev/null @@ -1,100 +0,0 @@ -#!/usr/bin/env ruby - -require 'digest/md5' - -ROOT_PATH="<%= node['errbit']['deploy_to'] %>" -APP_NAME="<%= node['errbit']['name'] %>" -PID_PATH="<%= node['errbit']['deploy_to'] %>/shared/pids/unicorn.pid" - -def run_and_print_command(command) - puts command - system(command) || exit(1) -end - -def run_and_ignore_exitcode_and_print_command(command) - puts command - system(command) -end - -def unicorn_running? - if File.exists?(PID_PATH) && (pid = File.read(PID_PATH).chomp) && system("ps aux | grep #{pid} | grep -v grep > /dev/null") - pid - else - false - end -end - -def different_gemfile? - if File.exists?("#{ROOT_PATH}/current/Gemfile") - dir = Dir["#{ROOT_PATH}/releases/*"] - previous_release_path = dir.sort[dir.size-2] - if !previous_release_path.nil? && File.exists?("#{previous_release_path}/Gemfile") - return Digest::MD5.hexdigest(File.read(ROOT_PATH + "/current/Gemfile")) != Digest::MD5.hexdigest(File.read("#{previous_release_path}/Gemfile")) - end - end - false -end - -def start_unicorn - puts "Gemfile detected - running Unicorn with bundle exec" - run_and_ignore_exitcode_and_print_command "cd #{ROOT_PATH}/current && bundle exec unicorn -E production -c #{ROOT_PATH}/shared/config/unicorn.conf -D" -end - -def stop_unicorn - if unicorn_running? - if run_and_ignore_exitcode_and_print_command "kill -QUIT `cat #{PID_PATH}`" - `rm #{PID_PATH}` - end - else - puts "You can't stop unicorn, because it's not running" - end -end - -def restart_unicorn - if unicorn_running? - run_and_ignore_exitcode_and_print_command "kill -USR2 `cat #{PID_PATH}`" - else - start_unicorn - end -end - -def clean_restart - if different_gemfile? - puts "Found a previous version with a different Gemfile: Doing a stop & start" - stop_unicorn if unicorn_running? - start_unicorn - else - puts "No previous version with a different Gemfile found. Assuming a quick restart without re-loading gems is save" - restart_unicorn - end -end - -def status_unicorn - if pid = unicorn_running? - puts "Unicorn #{APP_NAME} running with PID #{pid}" - return true - else - puts "Unicorn #{APP_NAME} not running" - return false - end -end - -case ARGV[0] -when "start" - puts "Starting Unicorn #{APP_NAME}" - start_unicorn -when "stop" - puts "Stopping Unicorn #{APP_NAME}" - stop_unicorn -when "status" - status_unicorn -when "restart" - restart_unicorn -when "clean-restart" - clean_restart -else - puts "Usage: {start|stop|status|restart|clean-restart}" - exit 1 -end - -exit 0