├── .travis.yml
├── Gemfile
├── .gitignore
├── lib
├── eycap
│ ├── version.rb
│ ├── recipes.rb
│ ├── recipes
│ │ ├── apache.rb
│ │ ├── passenger.rb
│ │ ├── monit.rb
│ │ ├── tomcat.rb
│ │ ├── resque.rb
│ │ ├── rvm.rb
│ │ ├── ssl.rb
│ │ ├── juggernaut.rb
│ │ ├── delayed_job.rb
│ │ ├── backgroundrb.rb
│ │ ├── ferret.rb
│ │ ├── memcached.rb
│ │ ├── slice.rb
│ │ ├── bundler.rb
│ │ ├── solr.rb
│ │ ├── templates
│ │ │ └── maintenance.rhtml
│ │ ├── nginx.rb
│ │ ├── unicorn.rb
│ │ ├── mongrel.rb
│ │ ├── sphinx.rb
│ │ ├── database.rb
│ │ └── deploy.rb
│ └── lib
│ │ ├── ey_logger_hooks.rb
│ │ ├── rvm_helper.rb
│ │ └── ey_logger.rb
├── capistrano
│ └── recipes
│ │ └── deploy
│ │ └── strategy
│ │ ├── unshared_remote_cache.rb
│ │ └── filtered_remote_cache.rb
└── eycap.rb
├── test
├── fixtures
│ └── recipes
│ │ └── default.rb
├── eycap_test.rb
└── minitest_helper.rb
├── Rakefile
├── eycap.gemspec
├── README.markdown
└── History.txt
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: ruby
2 | rvm:
3 | - 1.9.3
--------------------------------------------------------------------------------
/Gemfile:
--------------------------------------------------------------------------------
1 | source "https://rubygems.org"
2 | gemspec
3 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | pkg
2 | *.gem
3 | .rbenv-version
4 | .DS_Store
5 | Gemfile.lock
6 |
--------------------------------------------------------------------------------
/lib/eycap/version.rb:
--------------------------------------------------------------------------------
1 | module Eycap
2 | VERSION = '0.6.12'
3 | end
4 |
5 |
--------------------------------------------------------------------------------
/lib/eycap/recipes.rb:
--------------------------------------------------------------------------------
1 | # This is here to maintain backward compatibility for the require "eycap/recipe"
2 | # statement at the beginning of each deploy.rb distributed before eycap 0.6.x
3 |
4 | require 'eycap'
--------------------------------------------------------------------------------
/lib/eycap/recipes/apache.rb:
--------------------------------------------------------------------------------
1 | Capistrano::Configuration.instance(:must_exist).load do
2 | namespace :apache do
3 | [:stop, :start, :restart, :reload].each do |action|
4 | desc "#{action.to_s.capitalize} Apache"
5 | task action, :roles => :web do
6 | sudo "/etc/init.d/apache2 #{action.to_s}"
7 | end
8 | end
9 | end
10 | end
--------------------------------------------------------------------------------
/test/fixtures/recipes/default.rb:
--------------------------------------------------------------------------------
1 | require 'capistrano'
2 |
3 | module Capistrano::Recipes
4 | module Default
5 | def self.load_into(configuration)
6 | configuration.load do
7 | task :default do
8 | set :message, 'this is a fixture class of a default Capistrano object'
9 | message
10 | end
11 | end
12 | end
13 | end
14 | end
--------------------------------------------------------------------------------
/lib/eycap/recipes/passenger.rb:
--------------------------------------------------------------------------------
1 | Capistrano::Configuration.instance(:must_exist).load do
2 | namespace :passenger do
3 | desc <<-DESC
4 | Restart the passenger module to reload the application after deploying.
5 | DESC
6 | task :restart, :roles => :app, :except => {:no_release => true} do
7 | sudo "touch #{current_path}/tmp/restart.txt"
8 | end
9 | end
10 | end
--------------------------------------------------------------------------------
/test/eycap_test.rb:
--------------------------------------------------------------------------------
1 | require 'minitest_helper'
2 |
3 | class TestEycap < MiniTest::Unit::TestCase
4 |
5 | describe "first test" do
6 | Capistrano::Configuration.instance = Capistrano::Configuration.new
7 | load_capistrano_recipe(Capistrano::Recipes::Default)
8 |
9 | it 'loads the specified recipe into the instance configuration' do
10 | Capistrano::Configuration.instance.must_have_task "default"
11 | end
12 |
13 | end
14 |
15 | end
--------------------------------------------------------------------------------
/Rakefile:
--------------------------------------------------------------------------------
1 | # The bundler rake tasks help automate the distribution of this gem.
2 | require 'bundler'
3 | Bundler::GemHelper.install_tasks
4 |
5 | # Define a default 'rake' alias for minitest so that when you type
6 | # rake in the root of the eycap folder you will run the tests.
7 | # https://github.com/jimweirich/rake/blob/master/lib/rake/testtask.rb
8 |
9 | require "rake/testtask"
10 |
11 | Rake::TestTask.new do |t|
12 | t.libs << "test" << "lib"
13 | t.pattern = "test/**/*_test.rb"
14 | end
15 |
16 | task :default => :test
17 |
--------------------------------------------------------------------------------
/lib/capistrano/recipes/deploy/strategy/unshared_remote_cache.rb:
--------------------------------------------------------------------------------
1 | require 'capistrano/recipes/deploy/strategy/remote_cache'
2 |
3 | module Capistrano
4 | module Deploy
5 | module Strategy
6 | class UnsharedRemoteCache < RemoteCache
7 | def check!
8 | super.check do |d|
9 | d.remote.writable(repository_cache)
10 | end
11 | end
12 |
13 | private
14 |
15 | def repository_cache
16 | configuration[:repository_cache]
17 | end
18 | end
19 | end
20 | end
21 | end
22 |
--------------------------------------------------------------------------------
/lib/eycap/recipes/monit.rb:
--------------------------------------------------------------------------------
1 | Capistrano::Configuration.instance(:must_exist).load do
2 |
3 | namespace :monit do
4 | desc "Get the status of your mongrels"
5 | task :status, :roles => :app do
6 | @monit_output ||= { }
7 | sudo "/usr/bin/monit status" do |channel, stream, data|
8 | @monit_output[channel[:server].to_s] ||= [ ]
9 | @monit_output[channel[:server].to_s].push(data.chomp)
10 | end
11 | @monit_output.each do |k,v|
12 | puts "#{k} -> #{'*'*55}"
13 | puts v.join("\n")
14 | end
15 | end
16 | end
17 | end
--------------------------------------------------------------------------------
/lib/eycap/recipes/tomcat.rb:
--------------------------------------------------------------------------------
1 | Capistrano::Configuration.instance(:must_exist).load do
2 | namespace :tomcat do
3 | desc "Start tomcat"
4 | task :start, :roles => [:app], :only => {:tomcat => true} do
5 | sudo "/etc/init.d/tomcat start"
6 | end
7 | desc "Stop tomcat"
8 | task :stop, :roles => [:app], :only => {:tomcat => true} do
9 | sudo "/etc/init.d/tomcat stop"
10 | end
11 | desc "Restart tomcat"
12 | task :restart, :roles => [:app], :only => {:tomcat => true} do
13 | sudo "/etc/init.d/tomcat restart"
14 | end
15 | end
16 | end
--------------------------------------------------------------------------------
/lib/eycap/lib/ey_logger_hooks.rb:
--------------------------------------------------------------------------------
1 | require File.join(File.dirname(__FILE__), "ey_logger")
2 |
3 | # These tasks are setup to use with the logger as post commit hooks.
4 | Capistrano::Configuration.instance(:must_exist).load do
5 | namespace :ey_logger do
6 | task :upload_log_to_slice, :except => { :no_release => true} do
7 | logger = Capistrano::EYLogger
8 | run "mkdir -p #{shared_path}/deploy_logs"
9 | put File.open(logger.log_file_path).read, "#{shared_path}/deploy_logs/#{logger.remote_log_file_name}"
10 | end
11 | end
12 | end
13 |
14 | Capistrano::EYLogger.post_process_hook("ey_logger:upload_log_to_slice")
--------------------------------------------------------------------------------
/lib/eycap/recipes/resque.rb:
--------------------------------------------------------------------------------
1 | Capistrano::Configuration.instance(:must_exist).load do
2 |
3 | namespace :resque do
4 | desc "After deploy:restart we want to restart the workers"
5 | task :restart, :roles => [:app], :only => {:resque => true} do
6 | sudo "monit restart all -g resque_#{application}"
7 | end
8 |
9 | desc "After update_code we want to symlink the resque.yml"
10 | task :symlink, :roles => [:app], :only => {:resque => true} do
11 | run "if [ -f #{shared_path}/config/resque.yml ]; then ln -nfs #{shared_path}/config/resque.yml #{latest_release}/config/resque.yml; fi"
12 | end
13 | # after "deploy:symlink_configs", "resque:symlink" add this to the deploy.rb file
14 | end
15 |
16 | end
--------------------------------------------------------------------------------
/lib/eycap/recipes/rvm.rb:
--------------------------------------------------------------------------------
1 | Capistrano::Configuration.instance(:must_exist).load do
2 | namespace :rvm do
3 | desc <<-DESC
4 | Create rvm wrappers for background tasks,
5 | in any script adding rvm support looks like:
6 |
7 | rvm_path=/usr/local/rvm
8 | if [[ -s "$rvm_path/environments/${application}" ]]
9 | then PATH="$rvm_path/wrappers/${application}:$PATH"
10 | fi
11 | DESC
12 | task :create_wrappers, :roles => :app, :except => {:no_bundle => true} do
13 | run_rvm_or <<-SHELL
14 | rvm alias create #{application} #{fetch(:rvm_ruby_string,nil)}
15 | rvm wrapper #{application} --no-links --all
16 | SHELL
17 | end
18 |
19 | after "bundler:bundle_gems","rvm:create_wrappers"
20 | end
21 | end
22 |
--------------------------------------------------------------------------------
/lib/eycap/recipes/ssl.rb:
--------------------------------------------------------------------------------
1 | Capistrano::Configuration.instance(:must_exist).load do
2 |
3 | namespace :ssl do
4 | desc "create csr and key for ssl certificates"
5 | task :create, :roles => :app, :except => {:no_release => true} do
6 | sudo "mkdir -p /data/ssl/"
7 | set(:length) { Capistrano::CLI.ui.ask("key length (1024 or 2048): ") }
8 | set(:country) { Capistrano::CLI.ui.ask("Country Code (2 letters): ") }
9 | set(:state) { Capistrano::CLI.ui.ask("State/Province: ") }
10 | set(:city) { Capistrano::CLI.ui.ask("City: ") }
11 | set(:domain) { Capistrano::CLI.ui.ask("Common Name (domain): ") }
12 | run "cd /data/ssl/ && openssl req -new -nodes -days 365 -newkey rsa:#{length} -subj '/C=#{country}/ST=#{state}/L=#{city}/CN=#{domain}' -keyout #{domain}.com.key -out #{domain}.com.csr"
13 | end
14 | end
15 | end
--------------------------------------------------------------------------------
/lib/eycap/recipes/juggernaut.rb:
--------------------------------------------------------------------------------
1 | Capistrano::Configuration.instance(:must_exist).load do
2 |
3 | namespace :juggernaut do
4 | desc "After update_code you want to symlink the juggernaut.yml file into place"
5 | task :symlink_configs, :roles => [:app], :except => {:no_release => true, :juggernaut => false} do
6 | run <<-CMD
7 | cd #{latest_release} &&
8 | ln -nfs #{shared_path}/config/juggernaut.yml #{latest_release}/config/juggernaut.yml &&
9 | ln -nfs #{shared_path}/config/juggernaut_hosts.yml #{latest_release}/config/juggernaut_hosts.yml
10 | CMD
11 | end
12 | [:start,:stop,:restart].each do |op|
13 | desc "#{op} juggernaut server"
14 | task op, :roles => [:app], :except => {:no_release => true, :juggernaut => false} do
15 | sudo "/usr/bin/monit #{op} all -g juggernaut_#{application}"
16 | end
17 | end
18 | end
19 | end
20 |
--------------------------------------------------------------------------------
/lib/eycap/recipes/delayed_job.rb:
--------------------------------------------------------------------------------
1 | Capistrano::Configuration.instance(:must_exist).load do
2 | namespace :dj do
3 | desc <<-DESC
4 | Start the Delayed Job queue along with any in the same monit_group.
5 | DESC
6 | task :start, :roles => [:app], :only => {:dj => true} do
7 | sudo "/usr/bin/monit start all -g dj_#{monit_group}"
8 | end
9 |
10 | desc <<-DESC
11 | Restart the Delayed Job queue along with any in the same monit_group.
12 | DESC
13 | task :restart, :roles => [:app], :only => {:dj => true} do
14 | sudo "/usr/bin/monit restart all -g dj_#{monit_group}"
15 | end
16 |
17 | desc <<-DESC
18 | Stop all monit group members, of which delayed job can be a part of.
19 | DESC
20 | task :stop, :roles => [:app], :only => {:dj => true} do
21 | sudo "/usr/bin/monit stop all -g dj_#{monit_group}"
22 | end
23 |
24 | end #namespace
25 | end #Capistrano::Configuration
26 |
--------------------------------------------------------------------------------
/lib/eycap/recipes/backgroundrb.rb:
--------------------------------------------------------------------------------
1 | Capistrano::Configuration.instance(:must_exist).load do
2 |
3 | namespace :bdrb do
4 | desc "After update_code you want to reindex"
5 | task :reindex, :roles => [:app], :only => {:backgroundrb => true} do
6 | run "#{fetch(:engineyard_bin, "/engineyard/bin")}/searchd #{application} reindex"
7 | end
8 |
9 | desc "Start Backgroundrb"
10 | task :start, :roles => [:app], :only => {:backgroundrb => true} do
11 | sudo "/usr/bin/monit start all -g backgroundrb_#{application}"
12 | end
13 | desc "Stop Backgroundrb"
14 | task :stop, :roles => [:app], :only => {:backgroundrb => true} do
15 | sudo "/usr/bin/monit stop all -g backgroundrb_#{application}"
16 | end
17 | desc "Restart Backgroundrb"
18 | task :restart, :roles => [:app], :only => {:backgroundrb => true} do
19 | sudo "/usr/bin/monit restart all -g backgroundrb_#{application}"
20 | end
21 | end
22 |
23 | end
24 |
--------------------------------------------------------------------------------
/lib/eycap/recipes/ferret.rb:
--------------------------------------------------------------------------------
1 | Capistrano::Configuration.instance(:must_exist).load do
2 |
3 | namespace :ferret do
4 | desc "After update_code you want to symlink the index and ferret_server.yml file into place"
5 | task :symlink_configs, :roles => [:app], :except => {:no_release => true, :ferret => false} do
6 | run <<-CMD
7 | cd #{latest_release} &&
8 | ln -nfs #{shared_path}/config/ferret_server.yml #{latest_release}/config/ferret_server.yml &&
9 | if [ -d #{latest_release}/index ]; then mv #{latest_release}/index #{latest_release}/index.bak; fi &&
10 | ln -nfs #{shared_path}/index #{latest_release}/index
11 | CMD
12 | end
13 | [:start,:stop,:restart].each do |op|
14 | desc "#{op} ferret server"
15 | task op, :roles => [:app], :except => {:no_release => true, :ferret => false} do
16 | sudo "/usr/bin/monit #{op} all -g ferret_#{application}"
17 | end
18 | end
19 | end
20 | end
21 |
--------------------------------------------------------------------------------
/lib/eycap/lib/rvm_helper.rb:
--------------------------------------------------------------------------------
1 | Capistrano::Configuration.instance(:must_exist).load do
2 |
3 | def reformat_code(code)
4 | code.split("\n").map(&:strip).join("; ")
5 | end
6 |
7 | # Allow parallel execution of rvm and non rvm commands
8 | def run_rvm_or(command_rvm, command_else = "true")
9 | parallel do |session|
10 | command_else = reformat_code(command_else)
11 | if Capistrano.const_defined?(:RvmMethods)
12 | # command_with_shell is defined in RvmMethods so rvm/capistrano has to be required first
13 | command_rvm = command_with_shell(reformat_code(command_rvm), fetch(:rvm_shell, "bash"))
14 | rvm_role = fetch(:rvm_require_role, nil)
15 | if rvm_role # mixed
16 | session.when "in?(:#{rvm_role})", command_rvm
17 | session.else command_else
18 | else # only rvm
19 | session.else command_rvm
20 | end
21 | else # no rvm
22 | session.else command_else
23 | end
24 | end
25 | end
26 |
27 | end
28 |
--------------------------------------------------------------------------------
/test/minitest_helper.rb:
--------------------------------------------------------------------------------
1 | # Let's include the eycap gem.
2 | # FIXME: when you uncomment the require to bring in eycap it starts to error:
3 | #
4 | # capistrano/lib/capistrano/configuration/loading.rb:18:
5 | # in `instance': Please require this file from within a Capistrano recipe (LoadError)
6 | #
7 | # require File.dirname(__FILE__) + '/../lib/eycap'
8 |
9 | # This is a short and sweet way to bootstrap minitest.
10 | # It also lets them keep it under a "service" if they want to make changes to
11 | # the autorun method.
12 | # https://github.com/seattlerb/minitest/blob/master/lib/minitest/autorun.rb
13 |
14 | require 'rubygems'
15 | require 'bundler/setup'
16 |
17 | require 'minitest/autorun'
18 |
19 | # This library has assertions and expectations already written that can help
20 | # to test capistrano recipes.
21 | require 'minitest-capistrano'
22 |
23 | # Let's add capistrano, since that's what we need to deploy.
24 | require 'capistrano'
25 |
26 | # Load a default fixture capistrano object.
27 | require 'fixtures/recipes/default'
28 |
--------------------------------------------------------------------------------
/lib/eycap.rb:
--------------------------------------------------------------------------------
1 | require 'eycap/lib/ey_logger'
2 | require 'eycap/lib/ey_logger_hooks'
3 | require 'eycap/lib/rvm_helper'
4 | require 'eycap/recipes/apache'
5 | require 'eycap/recipes/backgroundrb'
6 | require 'eycap/recipes/bundler'
7 | require 'eycap/recipes/database'
8 | require 'eycap/recipes/delayed_job'
9 | require 'eycap/recipes/deploy'
10 | require 'eycap/recipes/ferret'
11 | require 'eycap/recipes/juggernaut'
12 | require 'eycap/recipes/memcached'
13 | require 'eycap/recipes/mongrel'
14 | require 'eycap/recipes/monit'
15 | require 'eycap/recipes/nginx'
16 | require 'eycap/recipes/passenger'
17 | require 'eycap/recipes/resque'
18 | require 'eycap/recipes/rvm'
19 | require 'eycap/recipes/slice'
20 | require 'eycap/recipes/solr'
21 | require 'eycap/recipes/sphinx'
22 | require 'eycap/recipes/ssl'
23 | require 'eycap/recipes/tomcat'
24 | require 'eycap/recipes/unicorn'
25 |
26 | Capistrano::Configuration.instance(:must_exist).load do
27 | default_run_options[:pty] = true if respond_to?(:default_run_options)
28 | set :keep_releases, 3
29 | set :runner, defer { user }
30 | end
31 |
--------------------------------------------------------------------------------
/lib/eycap/recipes/memcached.rb:
--------------------------------------------------------------------------------
1 | Capistrano::Configuration.instance(:must_exist).load do
2 | namespace :memcached do
3 | desc "Start memcached"
4 | task :start, :roles => [:app], :only => {:memcached => true} do
5 | sudo "/etc/init.d/memcached start"
6 | end
7 | desc "Stop memcached"
8 | task :stop, :roles => [:app], :only => {:memcached => true} do
9 | sudo "/etc/init.d/memcached stop"
10 | end
11 | desc "Restart memcached"
12 | task :restart, :roles => [:app], :only => {:memcached => true} do
13 | sudo "/etc/init.d/memcached restart"
14 | end
15 | desc "Flush memcached - this assumes memcached is on port 11211"
16 | task :flush, :roles => [:app], :only => {:memcached => true} do
17 | sudo "echo 'flush_all' | nc -q 1 localhost 11211"
18 | end
19 | desc "Symlink the memcached.yml file into place if it exists"
20 | task :symlink_configs, :roles => [:app], :only => {:memcached => true }, :except => { :no_release => true } do
21 | run "if [ -f #{shared_path}/config/memcached.yml ]; then ln -nfs #{shared_path}/config/memcached.yml #{latest_release}/config/memcached.yml; fi"
22 | end
23 | end
24 | end
25 |
--------------------------------------------------------------------------------
/eycap.gemspec:
--------------------------------------------------------------------------------
1 | # -*- encoding: utf-8 -*-
2 | require File.expand_path('../lib/eycap/version', __FILE__)
3 |
4 | Gem::Specification.new do |gem|
5 | gem.authors = ["Engine Yard", "Tyler Bird", "Matt Dolian", "Christopher Rigor", "Mike Riley", "Joel Watson", "Corey Donohoe", "Mutwin Kraus", "Erik Jones", "Daniel Neighman", "Dylan Egan", "Dan Peterson", "Tim Carey-Smith"]
6 | gem.email = ["appsupport@engineyard.com"]
7 | gem.description = %q{Capistrano recipes for the Engine Yard Managed platform.}
8 | gem.summary = %q{Recipes that help automate the processes of the Engine Yard stack for users of the Managed platform.}
9 | gem.homepage = "http://github.com/engineyard/eycap"
10 |
11 | gem.files = `git ls-files`.split($\)
12 | gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
13 | gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
14 | gem.name = "eycap"
15 | gem.require_paths = ["lib"]
16 | gem.version = Eycap::VERSION
17 |
18 | gem.add_dependency "capistrano", ">= 2.2.0"
19 |
20 | gem.add_development_dependency "rake"
21 | gem.add_development_dependency "minitest"
22 | gem.add_development_dependency "minitest-capistrano"
23 | end
--------------------------------------------------------------------------------
/lib/eycap/recipes/slice.rb:
--------------------------------------------------------------------------------
1 | Capistrano::Configuration.instance(:must_exist).load do
2 |
3 | namespace :slice do
4 | desc "Tail the Rails logs for your environment"
5 | task :tail_environment_logs, :roles => :app do
6 | run "tail -f #{shared_path}/log/#{rails_env}.log" do |channel, stream, data|
7 | puts # for an extra line break before the host name
8 | puts "#{channel[:server]} -> #{data}"
9 | break if stream == :err
10 | end
11 | end
12 | desc "Tail the Mongrel logs for your environment"
13 | task :tail_mongrel_logs, :roles => :app do
14 | run "tail -f #{shared_path}/log/mongrel*.log" do |channel, stream, data|
15 | puts # for an extra line break before the host name
16 | puts "#{channel[:server]} -> #{data}"
17 | break if stream == :err
18 | end
19 | end
20 | desc "Tail the apache logs for your environment"
21 | task :tail_apache_logs, :roles => [:app, :web] do
22 | run "tail -f /var/log/apache2/#{application}.*.access.log" do |channel, stream, data|
23 | puts # line break
24 | puts "#{channel[:server]} -> #{data}"
25 | break if stream == :err
26 | end
27 | end
28 | end
29 | end
30 |
--------------------------------------------------------------------------------
/lib/eycap/recipes/bundler.rb:
--------------------------------------------------------------------------------
1 | Capistrano::Configuration.instance(:must_exist).load do
2 |
3 | set :bundle_without, "test development" unless exists?(:bundle_without)
4 |
5 | namespace :bundler do
6 | desc "Automatically installed your bundled gems if a Gemfile exists"
7 | task :bundle_gems, :roles => :app, :except => {:no_bundle => true} do
8 | only_with_rvm = <<-SHELL
9 | if [ -f #{release_path}/Gemfile ]
10 | then cd #{release_path} && bundle install --without=#{bundle_without} --system
11 | fi
12 | SHELL
13 | only_without_rvm = <<-SHELL
14 | mkdir -p #{shared_path}/bundled_gems
15 | if [ -f #{release_path}/Gemfile ]
16 | then cd #{release_path} && bundle install --without=#{bundle_without} --binstubs #{release_path}/bin --path #{shared_path}/bundled_gems --quiet --deployment
17 | fi
18 | if [ ! -h #{release_path}/bin ]
19 | then ln -nfs #{release_path}/bin #{release_path}/ey_bundler_binstubs
20 | fi
21 | SHELL
22 | run_rvm_or only_with_rvm, only_without_rvm
23 | end
24 | task :symlink_bundle_config, :roles => :app do
25 | run_rvm_or "true", "mkdir -p #{shared_path}/bundle && ln -sf #{shared_path}/bundle #{release_path}/.bundle"
26 | end
27 | before "bundler:bundle_gems","bundler:symlink_bundle_config"
28 | after "deploy:symlink_configs","bundler:bundle_gems"
29 | end
30 | end
31 |
--------------------------------------------------------------------------------
/lib/eycap/recipes/solr.rb:
--------------------------------------------------------------------------------
1 | Capistrano::Configuration.instance(:must_exist).load do
2 |
3 | namespace :solr do
4 | desc "After update_code you want to symlink the index and ferret_server.yml file into place"
5 | task :symlink_configs, :roles => [:app], :except => {:no_release => true} do
6 | run <<-CMD
7 | cd #{latest_release} && ln -nfs #{shared_path}/config/solr.yml #{latest_release}/config/solr.yml
8 | CMD
9 | end
10 |
11 | [:start,:stop,:restart].each do |op|
12 | desc "#{op} ferret server"
13 | task op, :roles => [:app], :only => {:solr => true} do
14 | sudo "/usr/bin/monit #{op} all -g solr_#{application}"
15 | end
16 | end
17 |
18 | namespace :tail do
19 | desc "Tail the Solr logs this environment"
20 | task :logs, :roles => [:app], :only => {:solr => true} do
21 | run "tail -f /var/log/engineyard/solr/#{application}.log" do |channel, stream, data|
22 | puts # for an extra line break before the host name
23 | puts "#{channel[:server]} -> #{data}"
24 | break if stream == :err
25 | end
26 | end
27 | desc "Tail the Solr error logs this environment"
28 | task :errors, :roles => [:app], :only => {:solr => true} do
29 | run "tail -f /var/log/engineyard/solr/#{application}.err.log" do |channel, stream, data|
30 | puts # for an extra line break before the host name
31 | puts "#{channel[:server]} -> #{data}"
32 | break if stream == :err
33 | end
34 | end
35 | end
36 | end
37 | end
38 |
--------------------------------------------------------------------------------
/lib/eycap/recipes/templates/maintenance.rhtml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
9 | System down for maintenance
10 |
11 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 | The system is down for <%= reason ? reason : "maintenance" %>
43 | as of <%= Time.now.strftime("%H:%M %Z") %>.
44 |
45 |
46 | It'll be back <%= deadline ? deadline : "shortly" %>.
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
--------------------------------------------------------------------------------
/lib/eycap/recipes/nginx.rb:
--------------------------------------------------------------------------------
1 | Capistrano::Configuration.instance(:must_exist).load do
2 |
3 | namespace :nginx do
4 | desc "Start Nginx on the app slices."
5 | task :start, :roles => :app, :except => {:nginx => false} do
6 | sudo "nohup /etc/init.d/nginx start 2>&1 | cat"
7 | end
8 |
9 | desc "Restart the Nginx processes on the app slices."
10 | task :restart , :roles => :app, :except => {:nginx => false} do
11 | sudo "nohup /etc/init.d/nginx restart 2>&1 | cat"
12 | end
13 |
14 | desc "Stop the Nginx processes on the app slices."
15 | task :stop , :roles => :app, :except => {:nginx => false} do
16 | sudo "/etc/init.d/nginx stop"
17 | end
18 |
19 | desc "Reload the Nginx config on the app slices."
20 | task :reload , :roles => :app, :except => {:nginx => false} do
21 | sudo "/etc/init.d/nginx reload"
22 | end
23 |
24 | desc "Upgrade the Nginx processes on the app slices."
25 | task :upgrade , :roles => :app, :except => {:nginx => false} do
26 | sudo "/etc/init.d/nginx upgrade"
27 | end
28 |
29 | desc "Test the Nginx config on the app slices."
30 | task :configtest , :roles => :app, :except => {:nginx => false} do
31 | sudo "/etc/init.d/nginx configtest"
32 | end
33 |
34 | desc "Tail the nginx error logs on the app slices"
35 | task :tail_error, :roles => :app, :except => {:nginx => false} do
36 | run "tail -f /var/log/engineyard/nginx/error.log" do |channel, stream, data|
37 | puts "#{channel[:server]}: #{data}" unless data =~ /^10\.[01]\.0/ # skips lb pull pages
38 | break if stream == :err
39 | end
40 | end
41 |
42 | end
43 | end
--------------------------------------------------------------------------------
/lib/eycap/recipes/unicorn.rb:
--------------------------------------------------------------------------------
1 | Capistrano::Configuration.instance(:must_exist).load do
2 | namespace :unicorn do
3 | desc <<-DESC
4 | Start the Unicorn Master. This uses the :use_sudo variable to determine whether to use sudo or not. By default, :use_sudo is set to true.
5 | DESC
6 | task :start, :roles => [:app], :except => {:unicorn => false} do
7 | sudo "/usr/bin/monit start all -g #{monit_group}"
8 | end
9 |
10 | desc <<-DESC
11 | Restart the Unicorn processes on the app server by starting and stopping the master. This uses the :use_sudo variable to determine whether to use sudo or not. By default, :use_sudo is set to true.
12 | DESC
13 | task :restart, :roles => [:app], :except => {:unicorn => false} do
14 | sudo "/usr/bin/monit restart all -g #{monit_group}"
15 | end
16 |
17 | desc <<-DESC
18 | Stop the Unicorn processes on the app server. This uses the :use_sudo
19 | variable to determine whether to use sudo or not. By default, :use_sudo is
20 | set to true.
21 | DESC
22 | task :stop, :roles => [:app], :except => {:unicorn => false} do
23 | sudo "/usr/bin/monit stop all -g #{monit_group}"
24 | end
25 |
26 | desc <<-DESC
27 | Reloads the unicorn works gracefully - Use deploy task for deploys
28 | DESC
29 | task :reload, :roles => [:app], :except => {:unicorn => false} do
30 | run "#{fetch(:engineyard_bin, "/engineyard/bin")}/unicorn #{application} reload"
31 | end
32 |
33 | desc <<-DESC
34 | Adds a Unicorn worker - Beware of causing your host to swap, this setting isn't permanent
35 | DESC
36 | task :aworker, :roles => [:app], :except => {:unicorn => false} do
37 | run "#{fetch(:engineyard_bin, "/engineyard/bin")}/unicorn #{application} aworker"
38 | end
39 |
40 | desc <<-DESC
41 | Removes a unicorn worker (gracefully)
42 | DESC
43 | task :rworker, :roles => [:app], :except => {:unicorn => false} do
44 | run "#{fetch(:engineyard_bin, "/engineyard/bin")}/unicorn #{application} rworker"
45 | end
46 |
47 | desc <<-DESC
48 | Deploys app gracefully with USR2 and unicorn.rb combo
49 | DESC
50 | task :deploy, :roles => [:app], :except => {:unicorn => false} do
51 | run "#{fetch(:engineyard_bin, "/engineyard/bin")}/unicorn #{application} deploy"
52 | end
53 | end
54 | end
55 |
--------------------------------------------------------------------------------
/lib/capistrano/recipes/deploy/strategy/filtered_remote_cache.rb:
--------------------------------------------------------------------------------
1 | require 'capistrano/recipes/deploy/scm/base'
2 | require 'capistrano/recipes/deploy/strategy/remote'
3 |
4 | module Capistrano
5 | module Deploy
6 | module SCM
7 | class Subversion < Base
8 | def switch(revision, checkout)
9 | "cd #{checkout} && #{scm :switch, verbose, authentication, "-r#{revision}", repository}"
10 | end
11 | end
12 | end
13 |
14 | module Strategy
15 |
16 | # Implements the deployment strategy that keeps a cached checkout of
17 | # the source code on each remote server. Each deploy simply updates the
18 | # cached checkout, and then filters a copy through tar to remove unwanted .svn directories,
19 | # finally leaving a pristeen, export-like copy at the destination.
20 | class FilteredRemoteCache < Remote
21 | # Executes the SCM command for this strategy and writes the REVISION
22 | # mark file to each host.
23 | def deploy!
24 | update_repository_cache
25 | tar_copy_repository_cache
26 | end
27 |
28 | def check!
29 | super.check do |d|
30 | d.remote.writable(shared_path)
31 | end
32 | end
33 |
34 | private
35 |
36 | def repository_cache
37 | configuration[:repository_cache] || "/var/cache/engineyard/#{configuration[:application]}"
38 | end
39 |
40 | def update_repository_cache
41 | logger.trace "checking if the cached copy repository root matches this deploy, then updating it"
42 | command = "if [ -d #{repository_cache} ] && ! echo '#{configuration[:repository]}' | grep -q `svn info #{repository_cache} | grep 'Repository Root' | awk '{print $3}'`; then " +
43 | "rm -rf #{repository_cache} && #{source.checkout(revision, repository_cache)}; " +
44 | "elif [ -d #{repository_cache} ]; then #{source.switch(revision, repository_cache)}; " +
45 | "else #{source.checkout(revision, repository_cache)}; fi"
46 | scm_run(command)
47 | end
48 |
49 | def tar_copy_repository_cache
50 | logger.trace "copying and filtering .svn via tar from cached version to #{configuration[:release_path]}"
51 | run "mkdir #{configuration[:release_path]} && tar c --exclude=#{configuration[:filter_spec] || ".svn"} -C #{repository_cache} . | tar xC #{configuration[:release_path]} && #{mark}"
52 | end
53 | end
54 |
55 | end
56 | end
57 | end
58 |
--------------------------------------------------------------------------------
/lib/eycap/recipes/mongrel.rb:
--------------------------------------------------------------------------------
1 | Capistrano::Configuration.instance(:must_exist).load do
2 | namespace :mongrel do
3 | desc <<-DESC
4 | Start Mongrel processes on the app server. This uses the :use_sudo variable to determine whether to use sudo or not. By default, :use_sudo is
5 | set to true.
6 | DESC
7 | task :start, :roles => [:app], :except => {:mongrel => false} do
8 | sudo "/usr/bin/monit start all -g #{monit_group}"
9 | end
10 |
11 | desc <<-DESC
12 | Restart the Mongrel processes on the app server by starting and stopping the cluster. This uses the :use_sudo
13 | variable to determine whether to use sudo or not. By default, :use_sudo is set to true.
14 | DESC
15 | task :restart, :roles => [:app], :except => {:mongrel => false} do
16 | sudo "/usr/bin/monit restart all -g #{monit_group}"
17 | end
18 |
19 | desc <<-DESC
20 | Stop the Mongrel processes on the app server. This uses the :use_sudo
21 | variable to determine whether to use sudo or not. By default, :use_sudo is
22 | set to true.
23 | DESC
24 | task :stop, :roles => [:app], :except => {:mongrel => false} do
25 | sudo "/usr/bin/monit stop all -g #{monit_group}"
26 | end
27 |
28 | desc <<-DESC
29 | Start mongrels in a loop, with a defer of [default] 30 seconds between each single mongrel restart.
30 | DESC
31 | task :rolling_restart, :roles => [:app], :except => {:mongrel => false} do
32 |
33 | set :mongrel_restart_delay, 30
34 |
35 | # need a script due to weird escapes run by sudo "X".
36 | script = File.open("/tmp/rolling.reboot", 'w+')
37 | script.puts "#!/bin/bash"
38 | script.puts "export monit_group=#{monit_group}"
39 | script.puts "export mongrel_restart_delay=#{mongrel_restart_delay}"
40 | # here's the need for single quoted - sed ? - (no escaping).
41 | script.puts 'for port in $(monit summary | grep mongrel | sed -r \'s/[^0-9]*([0-9]+).*/\1/\'); do echo "Executing monit restart mongrel_${monit_group}_${port}"; /usr/bin/monit restart mongrel_${monit_group}_${port}; echo "sleeping $mongrel_restart_delay"; sleep ${mongrel_restart_delay}; done'
42 | script.close
43 |
44 | upload(script.path, script.path, :via=> :scp)
45 |
46 | #it's in the script, on the remote server, execute it.
47 | sudo "chmod +x #{script.path}"
48 | sudo "#{script.path}"
49 | #cleanup
50 | sudo "rm #{script.path}"
51 | require 'fileutils' ; FileUtils.rm(script.path)
52 | puts "Done."
53 | end
54 |
55 | end #namespace
56 | end #Capistrano::Configuration
57 |
--------------------------------------------------------------------------------
/lib/eycap/recipes/sphinx.rb:
--------------------------------------------------------------------------------
1 | Capistrano::Configuration.instance(:must_exist).load do
2 |
3 | namespace :sphinx do
4 | desc "After update_code you want to configure, then reindex"
5 | task :configure, :roles => [:app], :only => {:sphinx => true}, :except => {:no_release => true} do
6 | run "#{fetch(:engineyard_bin, "/engineyard/bin")}/searchd #{application} configure"
7 | end
8 |
9 | desc "After configure you want to reindex"
10 | task :reindex, :roles => [:app], :only => {:sphinx => true} do
11 | run "#{fetch(:engineyard_bin, "/engineyard/bin")}/searchd #{application} reindex"
12 | end
13 |
14 | desc "Start Sphinx Searchd"
15 | task :start, :roles => [:app], :only => {:sphinx => true} do
16 | sudo "/usr/bin/monit start all -g sphinx_#{application}"
17 | end
18 |
19 | desc "Stop Sphinx Searchd"
20 | task :stop, :roles => [:app], :only => {:sphinx => true} do
21 | sudo "/usr/bin/monit stop all -g sphinx_#{application}"
22 | end
23 |
24 | desc "Restart Sphinx Searchd"
25 | task :restart, :roles => [:app], :only => {:sphinx => true} do
26 | sudo "/usr/bin/monit restart all -g sphinx_#{application}"
27 | end
28 |
29 | task :symlink, :roles => [:app], :only => {:sphinx => true}, :except => {:no_release => true} do
30 | run "if [ -d #{latest_release}/config/ultrasphinx ]; then mv #{latest_release}/config/ultrasphinx #{latest_release}/config/ultrasphinx.bak; fi"
31 | run "ln -nfs #{shared_path}/config/ultrasphinx #{latest_release}/config/ultrasphinx"
32 | end
33 | end
34 |
35 | namespace :acts_as_sphinx do
36 | desc "After update_code you to to reindex"
37 | task :reindex, :roles => [:app], :only => {:sphinx => true} do
38 | run "#{fetch(:engineyard_bin, "/engineyard/bin")}/acts_as_sphinx_searchd #{application} reindex"
39 | end
40 | end
41 |
42 | namespace :thinking_sphinx do
43 | desc "After update_code you want to configure, then reindex"
44 | task :configure, :roles => [:app], :only => {:sphinx => true}, :except => {:no_release => true} do
45 | run "#{fetch(:engineyard_bin, "/engineyard/bin")}/thinking_sphinx_searchd #{application} configure #{rails_env}"
46 | end
47 |
48 | desc "After configure you want to reindex"
49 | task :reindex, :roles => [:app], :only => {:sphinx => true} do
50 | run "#{fetch(:engineyard_bin, "/engineyard/bin")}/thinking_sphinx_searchd #{application} reindex #{rails_env}"
51 | end
52 |
53 | task :symlink, :roles => [:app], :only => {:sphinx => true}, :except => {:no_release => true} do
54 | run "if [ -d #{latest_release}/config/thinkingsphinx ]; then mv #{latest_release}/config/thinkingsphinx #{latest_release}/config/thinkingsphinx.bak; fi"
55 | run "ln -nfs #{shared_path}/config/thinkingsphinx #{latest_release}/config/thinkingsphinx"
56 | run "ln -nfs #{shared_path}/config/sphinx.yml #{latest_release}/config/sphinx.yml"
57 | end
58 | end
59 |
60 | namespace :ultrasphinx do
61 | desc "After update_code you want to configure, then reindex"
62 | task :configure, :roles => [:app], :only => {:sphinx => true}, :except => {:no_release => true} do
63 | run "#{fetch(:engineyard_bin, "/engineyard/bin")}/ultrasphinx_searchd #{application} configure"
64 | end
65 |
66 | desc "After configure you want to reindex"
67 | task :reindex, :roles => [:app], :only => {:sphinx => true} do
68 | run "#{fetch(:engineyard_bin, "/engineyard/bin")}/ultrasphinx_searchd #{application} reindex"
69 | end
70 |
71 | task :symlink, :roles => [:app], :only => {:sphinx => true}, :except => {:no_release => true} do
72 | run "if [ -d #{latest_release}/config/ultrasphinx ]; then mv #{latest_release}/config/ultrasphinx #{latest_release}/config/ultrasphinx.bak; fi"
73 | run "ln -nfs #{shared_path}/config/ultrasphinx #{latest_release}/config/ultrasphinx"
74 | end
75 | end
76 | end
77 |
--------------------------------------------------------------------------------
/lib/eycap/recipes/database.rb:
--------------------------------------------------------------------------------
1 | require 'erb'
2 |
3 | Capistrano::Configuration.instance(:must_exist).load do
4 |
5 | namespace :db do
6 | task :backup_name, :roles => :db, :only => { :primary => true } do
7 | now = Time.now
8 | run "mkdir -p #{shared_path}/db_backups"
9 | backup_time = [now.year,now.month,now.day,now.hour,now.min,now.sec].join('-')
10 | set :backup_file, "#{shared_path}/db_backups/#{environment_database}-snapshot-#{backup_time}.sql"
11 | end
12 |
13 | desc "Clone Production Database to Staging Database."
14 | task :clone_prod_to_staging, :roles => :db, :only => { :primary => true } do
15 |
16 | # This task currently runs only on traditional EY offerings.
17 | # You need to have both a production and staging environment defined in
18 | # your deploy.rb file.
19 |
20 | backup_name unless exists?(:backup_file)
21 | run("cat #{shared_path}/config/database.yml") { |channel, stream, data| @environment_info = YAML.load(data)[rails_env] }
22 | dump
23 |
24 | if ['mysql', 'mysql2'].include? @environment_info['adapter']
25 | run "gunzip < #{backup_file}.gz | mysql -u #{dbuser} -p -h #{staging_dbhost} #{staging_database}" do |ch, stream, out|
26 | ch.send_data "#{dbpass}\n" if out=~ /^Enter password:/
27 | end
28 | else
29 | run "gunzip < #{backup_file}.gz | psql -W -U #{dbuser} -h #{staging_dbhost} #{staging_database}" do |ch, stream, out|
30 | ch.send_data "#{dbpass}\n" if out=~ /^Password/
31 | end
32 | end
33 | run "rm -f #{backup_file}.gz"
34 | end
35 |
36 | desc "Backup your MySQL or PostgreSQL database to shared_path+/db_backups"
37 | task :dump, :roles => :db, :only => {:primary => true} do
38 | backup_name unless exists?(:backup_file)
39 | on_rollback { run "rm -f #{backup_file}" }
40 | run("cat #{shared_path}/config/database.yml") { |channel, stream, data| @environment_info = YAML.load(data)[rails_env] }
41 |
42 | if ['mysql', 'mysql2'].include? @environment_info['adapter']
43 | dbhost = @environment_info['host']
44 | if rails_env == "production"
45 | dbhost = environment_dbhost.sub('-master', '') + '-replica' if dbhost != 'localhost' # added for Solo offering, which uses localhost
46 | end
47 | run "mysqldump --add-drop-table -u #{dbuser} -h #{dbhost} -p #{environment_database} | gzip -c > #{backup_file}.gz" do |ch, stream, out |
48 | ch.send_data "#{dbpass}\n" if out=~ /^Enter password:/
49 | end
50 | else
51 | run "pg_dump -W -c -U #{dbuser} -h #{environment_dbhost} #{environment_database} | gzip -c > #{backup_file}.gz" do |ch, stream, out |
52 | ch.send_data "#{dbpass}\n" if out=~ /^Password:/
53 | end
54 | end
55 | end
56 |
57 | desc "Sync your production database to your local workstation"
58 | task :clone_to_local, :roles => :db, :only => {:primary => true} do
59 | backup_name unless exists?(:backup_file)
60 | dump
61 | get "#{backup_file}.gz", "/tmp/#{application}.sql.gz"
62 | development_info = YAML.load(ERB.new(File.read('config/database.yml')).result)['development']
63 |
64 | if ['mysql', 'mysql2'].include? development_info['adapter']
65 | run_str = "gunzip < /tmp/#{application}.sql.gz | mysql -u #{development_info['username']} --password='#{development_info['password']}' -h #{development_info['host']} #{development_info['database']}"
66 | else
67 | run_str = ""
68 | run_str += "PGPASSWORD=#{development_info['password']} " if development_info['password']
69 | run_str += "gunzip < /tmp/#{application}.sql.gz | psql -U #{development_info['username']} "
70 | run_str += "-h #{development_info['host']} " if development_info['host']
71 | run_str += development_info['database']
72 | end
73 | %x!#{run_str}!
74 | run "rm -f #{backup_file}.gz"
75 | end
76 | end
77 |
78 | end
79 |
--------------------------------------------------------------------------------
/lib/eycap/lib/ey_logger.rb:
--------------------------------------------------------------------------------
1 | require 'tmpdir'
2 | require 'fileutils'
3 | module Capistrano
4 |
5 | class Logger
6 |
7 | def ey_log(level, message, line_prefix = nil)
8 | EYLogger.log(level, message, line_prefix) if EYLogger.setup?
9 | log_without_ey_logging(level, message, line_prefix)
10 | end
11 |
12 | unless method_defined?(:log_without_ey_logging)
13 | alias_method :log_without_ey_logging, :log
14 | alias_method :log, :ey_log
15 | end
16 |
17 | def close
18 | device.close if @needs_close
19 | EYLogger.close if EYLogger.setup?
20 | end
21 | end
22 |
23 | class EYLogger
24 |
25 | # Sets up the EYLogger to beging capturing capistrano's logging. You should pass the capistrno configuration
26 | # and the deploy type as a string. The deploy type is for reporting purposes only but must be included.
27 | def self.setup(configuration, deploy_type, options = {})
28 | @_configuration = configuration
29 | @_deploy_type = deploy_type.gsub(/:/, "_")
30 | @_log_path = options[:deploy_log_path] || Dir.tmpdir
31 | @_log_path << "/" unless @_log_path =~ /\/$/
32 | FileUtils.mkdir_p(@_log_path)
33 | @_setup = true
34 | @_success = true
35 | end
36 |
37 | def self.log(level, message, line_prefix=nil)
38 | return nil unless setup?
39 | @release_name = @_configuration[:release_name] if @release_name.nil?
40 | @_log_file_path = @_log_path + @release_name + ".log" unless @_log_file_path
41 | @_deploy_log_file = File.open(@_log_file_path, "w") if @_deploy_log_file.nil?
42 |
43 | indent = "%*s" % [Logger::MAX_LEVEL, "*" * (Logger::MAX_LEVEL - level)]
44 | message.each_line do |line|
45 | if line_prefix
46 | @_deploy_log_file << "#{indent} [#{line_prefix}] #{line.strip}\n"
47 | else
48 | @_deploy_log_file << "#{indent} #{line.strip}\n"
49 | end
50 | end
51 | end
52 |
53 | def self.post_process
54 | unless ::Interrupt === $!
55 | puts "\n\nPlease wait while the log file is processed\n"
56 | # Should dump the stack trace of an exception if there is one
57 | error = $!
58 | unless error.nil?
59 | @_deploy_log_file << error.message << "\n"
60 | @_deploy_log_file << error.backtrace.join("\n")
61 | @_success = false
62 | end
63 | self.close
64 |
65 | hooks = [:any]
66 | hooks << (self.successful? ? :success : :failure)
67 | puts "Executing Post Processing Hooks"
68 | hooks.each do |h|
69 | @_post_process_hooks[h].each do |key|
70 | @_configuration.parent.find_and_execute_task(key)
71 | end
72 | end
73 | puts "Finished Post Processing Hooks"
74 | end
75 | end
76 |
77 | # Adds a post processing hook.
78 | #
79 | # Provide a task name to execute. These tasks are executed after capistrano has actually run its course.
80 | #
81 | # Takes a key to control when the hook is executed.'
82 | # :any - always executed
83 | # :success - only execute on success
84 | # :failure - only execute on failure
85 | #
86 | # ==== Example
87 | # Capistrano::EYLogger.post_process_hook( "ey_logger:upload_log_to_slice", :any)
88 | #
89 | def self.post_process_hook(task, key = :any)
90 | @_post_process_hooks ||= Hash.new{|h,k| h[k] = []}
91 | @_post_process_hooks[key] << task
92 | end
93 |
94 | def self.setup?
95 | !!@_setup
96 | end
97 |
98 | def self.deploy_type
99 | @_deploy_type
100 | end
101 |
102 | def self.successful?
103 | !!@_success
104 | end
105 |
106 | def self.failure?
107 | !@_success
108 | end
109 |
110 | def self.log_file_path
111 | @_log_file_path
112 | end
113 |
114 | def self.remote_log_file_name
115 | @_log_file_name ||= "#{@_configuration[:release_name]}-#{@_deploy_type}-#{self.successful? ? "SUCCESS" : "FAILURE"}.log"
116 | end
117 |
118 | def self.close
119 | @_deploy_log_file.flush unless @_deploy_log_file.nil?
120 | @_deploy_log_file.close unless @_deploy_log_file.nil?
121 | @_setup = false
122 | end
123 |
124 | end
125 | end
126 |
--------------------------------------------------------------------------------
/lib/eycap/recipes/deploy.rb:
--------------------------------------------------------------------------------
1 | require File.join(File.dirname(__FILE__), "..", "lib", "ey_logger.rb")
2 | Capistrano::Configuration.instance(:must_exist).load do
3 |
4 | namespace :deploy do
5 | # This is here to hook into the logger for deployment tasks
6 | ["deploy", "deploy:long", "deploy:migrations", "deploy:migrate", "deploy:update_code"].each do |tsk|
7 | before(tsk) do
8 | Capistrano::EYLogger.setup( self, tsk )
9 | at_exit{ Capistrano::EYLogger.post_process if Capistrano::EYLogger.setup? }
10 | end
11 | end
12 |
13 | desc "Link the database.yml and mongrel_cluster.yml files into the current release path."
14 | task :symlink_configs, :roles => :app, :except => {:no_release => true} do
15 | run <<-CMD
16 | cd #{latest_release} &&
17 | ln -nfs #{shared_path}/config/database.yml #{latest_release}/config/database.yml &&
18 | ln -nfs #{shared_path}/config/mongrel_cluster.yml #{latest_release}/config/mongrel_cluster.yml
19 | CMD
20 | end
21 |
22 | desc <<-DESC
23 | Run the migrate rake task. By default, it runs this in most recently \
24 | deployed version of the app. However, you can specify a different release \
25 | via the migrate_target variable, which must be one of :latest (for the \
26 | default behavior), or :current (for the release indicated by the \
27 | `current' symlink). Strings will work for those values instead of symbols, \
28 | too. You can also specify additional environment variables to pass to rake \
29 | via the migrate_env variable. Finally, you can specify the full path to the \
30 | rake executable by setting the rake variable. The defaults are:
31 |
32 | set :rake, "rake"
33 | set :framework, "merb"
34 | set :merb_env, "production"
35 | set :migrate_env, ""
36 | set :migrate_target, :latest
37 | DESC
38 | task :migrate, :roles => :db, :only => { :primary => true } do
39 | rake = fetch(:rake, "rake")
40 |
41 | framework = fetch(:framework, "rails")
42 | if framework.match(/^rails$/i)
43 | app_env = fetch(:rails_env, "production")
44 | else
45 | app_env = fetch("#{framework.downcase}_env".to_sym, "production")
46 | end
47 |
48 | migrate_env = fetch(:migrate_env, "")
49 | migrate_target = fetch(:migrate_target, :latest)
50 |
51 | directory = case migrate_target.to_sym
52 | when :current then current_path
53 | when :latest then current_release
54 | else raise ArgumentError, "unknown migration target #{migrate_target.inspect}"
55 | end
56 |
57 | run "cd #{directory}; #{rake} #{framework.upcase}_ENV=#{app_env} #{migrate_env} db:migrate ;"
58 | end
59 |
60 | desc "Display the maintenance.html page while deploying with migrations. Then it restarts and enables the site again."
61 | task :long do
62 | transaction do
63 | update_code
64 | web.disable
65 | symlink
66 | migrate
67 | end
68 |
69 | restart
70 | web.enable
71 | end
72 |
73 | desc "Restart the Mongrel processes on the app slices."
74 | task :restart, :roles => :app do
75 | mongrel.restart
76 | end
77 |
78 | desc "Start the Mongrel processes on the app slices."
79 | task :spinner, :roles => :app do
80 | mongrel.start
81 | end
82 |
83 | desc "Start the Mongrel processes on the app slices."
84 | task :start, :roles => :app do
85 | mongrel.start
86 | end
87 |
88 | desc "Stop the Mongrel processes on the app slices."
89 | task :stop, :roles => :app do
90 | mongrel.stop
91 | end
92 |
93 | namespace :web do
94 | desc <<-DESC
95 | Present a maintenance page to visitors. Disables your application's web \
96 | interface by writing a "maintenance.html" file to each web server. The \
97 | servers must be configured to detect the presence of this file, and if \
98 | it is present, always display it instead of performing the request.
99 |
100 | By default, the maintenance page will just say the site is down for \
101 | "maintenance", and will be back "shortly", but you can customize the \
102 | page by specifying the REASON and UNTIL environment variables:
103 |
104 | $ cap deploy:web:disable \\
105 | REASON="hardware upgrade" \\
106 | UNTIL="12pm Central Time"
107 |
108 | Further customization copy your html file to shared_path+'/system/maintenance.html.custom'.
109 | If this file exists it will be used instead of the default capistrano ugly page
110 | DESC
111 | task :disable, :roles => :web, :except => { :no_release => true } do
112 | maint_file = "#{shared_path}/system/maintenance.html"
113 | require 'erb'
114 | on_rollback { run "rm #{shared_path}/system/maintenance.html" }
115 |
116 | reason = ENV['REASON']
117 | deadline = ENV['UNTIL']
118 |
119 | template = File.read(File.join(File.dirname(__FILE__), "templates", "maintenance.rhtml"))
120 | result = ERB.new(template).result(binding)
121 |
122 | put result, "#{shared_path}/system/maintenance.html.tmp", :mode => 0644
123 | run "if [ -f #{shared_path}/system/maintenance.html.custom ]; then cp #{shared_path}/system/maintenance.html.custom #{maint_file}; else cp #{shared_path}/system/maintenance.html.tmp #{maint_file}; fi"
124 | end
125 | end
126 | end
127 |
128 | end
129 |
--------------------------------------------------------------------------------
/README.markdown:
--------------------------------------------------------------------------------
1 | # eycap [](http://travis-ci.org/engineyard/eycap)
2 |
3 | ## Description
4 |
5 | The Engine Yard capistrano tasks are for use specifically with Engine Yard Managed services. But can be used as examples for building other tasks as well.
6 |
7 | ## Requirements
8 |
9 | * [Capistrano](https://github.com/capistrano/capistrano) >= 2.2.0
10 |
11 | * NOTE: When using a git repository use Capistrano >= 2.5.3.
12 |
13 | ## Install
14 |
15 | Use your `Gemfile` and `bundler` to both document and install the `eycap` gem to your application. We also recommend the following gems to be configured along side `eycap`. Add these to your `Gemfile`:
16 |
17 | ```ruby
18 | group :development, :test do
19 | gem 'eycap', :require => false
20 | gem 'capistrano', '~> 2.15'
21 | gem 'net-ssh', '~> 2.7.0'
22 | end
23 | ```
24 |
25 | Then run bundle install to install the `eycap` and other gem(s).
26 |
27 | $ bundle install
28 |
29 | Then in your `deploy.rb` file you'll need to add the following require statement to the begininng of the file:
30 |
31 | ```
32 | require "eycap/recipes"
33 | ```
34 |
35 | ## Usage
36 |
37 | ### Configuration
38 |
39 | Your initial deploy.rb will be provided for you when your servers are provisioned on Engine Yard Managed. In order to deploy your application, you can go to the `RAILS_ROOT` folder and run:
40 |
41 | $ capify .
42 |
43 | This generates the `Capfile` and the `config/deploy.rb` file for you. You'll replace the `config/deploy.rb` file with the `deploy.rb` given to you by Engine Yard.
44 |
45 | For deploying Rails 3.1 or greater apps using the [asset pipeline](https://github.com/engineyard/eycap/wiki/Asset-Pipeline) read more on the linked page.
46 |
47 | ### Setup restart server
48 |
49 | Mongrel is the default server, to override this default you'll need to define the following in your `deploy.rb` file:
50 |
51 | ```ruby
52 | namespace :deploy do
53 |
54 | task :restart, :roles => :app do
55 | # mongrel.restart
56 | end
57 |
58 |
59 | task :spinner, :roles => :app do
60 | # mongrel.start
61 | end
62 |
63 |
64 | task :start, :roles => :app do
65 | # mongrel.start
66 | end
67 |
68 |
69 | task :stop, :roles => :app do
70 | # mongrel.stop
71 | end
72 |
73 | end
74 | ```
75 |
76 | Replace the commented out with your server (passenger, unicorn, thin, puma, etc.) and then it will override the default of mongrel.
77 |
78 | ### Deploying to Environment
79 |
80 | To ensure your environments are ready to deploy, check on staging.
81 |
82 | $ cap staging deploy:check
83 |
84 | This will determine if all requirements are met to deploy. Sometimes if the default folders are not setup you may be able to repair by running:
85 |
86 | $ cap staging deploy:setup
87 |
88 | If you cannot get `deploy:check` to pass, please open a [new support ticket](https://support.cloud.engineyard.com/tickets/new) and let us know.
89 |
90 | Now you're ready to do a test deploy.
91 |
92 | Optionally, `cap deploy:cold` will run your migrations and start (instead of restart) your app server.
93 |
94 | $ cap staging deploy:cold
95 |
96 | Or if you have already dumped a copy of your data to staging or do not want to run migrations you can simply do a deploy.
97 |
98 | $ cap staging deploy
99 |
100 | And to do all this on production, just change the environment name and you'll be all set.
101 |
102 | $ cap production deploy
103 |
104 | ## Eycap Commands
105 |
106 | For a list of all available commands, run:
107 |
108 | $ cap -T
109 |
110 | This will show you not only the default capistrano commands but also the ones you get by including the eycap gem.
111 |
112 | ## Custom binaries path
113 |
114 | In rare cases (`unicorn` / `sphinx`) it is required to set custom path for binaries when using
115 | development versions of scripts. It is as easy as:
116 |
117 | ```ruby
118 | set :engineyard_bin, "/engineyard/custom"
119 | ```
120 |
121 | The default is `/engineyard/bin` and is just fine in normal deployment.
122 |
123 | ## Pull Requests
124 |
125 | If you'd like to contribute to the eycap gem please create a fork, then send a pull request and a member of the eycap team will review it.
126 |
127 | ## Issues
128 |
129 | When you run into a problem please check the [issues](/issues) to see if one has been reported. If not, please report the issue and we'll get to work on fixing it.
130 |
131 | ## License
132 |
133 | Copyright (c) Engine Yard
134 |
135 | Permission is hereby granted, free of charge, to any person obtaining
136 | a copy of this software and associated documentation files (the
137 | "Software"), to deal in the Software without restriction, including
138 | without limitation the rights to use, copy, modify, merge, publish,
139 | distribute, sublicense, and/or sell copies of the Software, and to
140 | permit persons to whom the Software is furnished to do so, subject to
141 | the following conditions:
142 |
143 | The above copyright notice and this permission notice shall be
144 | included in all copies or substantial portions of the Software.
145 |
146 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
147 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
148 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
149 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
150 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
151 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
152 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
153 |
--------------------------------------------------------------------------------
/History.txt:
--------------------------------------------------------------------------------
1 | == 0.6.12/ 2014-06-14
2 | * #44 Share the bundle config instead of creating it on multiple slices - dholdren
3 |
4 | == 0.6.11/ 2014-02-21
5 | * #46 Fix path issue in bundle_config - krutten.
6 |
7 | == 0.6.10 / 2014-02-20
8 | * #45 Bundler will sometimes try and verify the dependancy tree - jcstringer.
9 | * This adds the ``--deployment`` flag to the ``bundle`` command.
10 |
11 | == 0.6.9 / 2013-05-29
12 | * #43 eycap v0.6.8 fails to detect rvm gem path with rvm-capistrano - Michal Papis.
13 |
14 | == 0.6.8 / 2013-05-29
15 | * #42 allow custom path for ey binaries - Michal Papis.
16 | * Added bundle_config task for bundler - Tyler Bird.
17 | * Change default output of bundle install to --quiet for speed increase - Tyler Bird.
18 |
19 | == 0.6.7 / 2013-03-25
20 | * follow up to #37, pull #40 works to ensure rvm and non-rvm work properly - Michal Papis.
21 | * #39 Joe does some good house cleaning, thanks!
22 | * and a Delayed Job tweak by Kevin on #41, thank you.
23 |
24 | == 0.6.6 / 2013-03-15
25 | * adding rvm support to eycap #37 - thank you Michal Papis!
26 |
27 | == 0.6.5 / 2012-12-07
28 | * removed unnecessary if/then statement see closed pull request #36
29 | * Merged pull request #34 - include rails_env in Thinking Sphinx recipe
30 |
31 | == 0.6.4 / 2012-10-04
32 | * Fix the incorrect pluralization of "eycap/recipes" file because eycap would not include itself anymore.
33 |
34 | == 0.6.3 / 2012-09-21
35 | * Changing all /engineyard/bin/unicorn tasks in unicorn recipe to run.
36 |
37 | == 0.6.2 / 2012-09-21
38 | * Added minitest framework.
39 | * Change deploy user task in unicorn recipe from sudo to run.
40 |
41 | == 0.6.1 / 2012-09-06
42 | * The Yes We Can patch, which maintains backwards compatibility for the eycap require statement.
43 |
44 | == 0.6.0 / 2012-09-06
45 | * Bundler and a clean gemspec now manage the release of eycap.
46 | * Use the :bundle_without to optionally specify groups more than 'test and development' only.
47 |
48 | == 0.5.23 / 2012-05-31
49 | * Changed README to markdown format.
50 | * Improved README to give instructions on general setup and usage.
51 | * Refactored default bundle install behavior, adding :no_bundle flag.
52 | * Merged pull request #32 - log more deploy commands.
53 |
54 | == 0.5.22 / 2011-12-02
55 | * merged pull request #28 - only perform the bundler.bundle_gems task on app servers that handle releases
56 | * merged pull request #30 - fix for race condition where binstubs is on an NFS volume
57 |
58 | == 0.5.21 / 2011-11-29
59 | * merged pull request #26 - symlink ey_bundler_binstubs to bin
60 |
61 | == 0.5.20 / 2011-07-21
62 | * merged pull request #25 - added :except clause for non-nginx app servers
63 | * merged pull request #24 - exclude 'test' and 'development' in bundler
64 |
65 | == 0.5.19 / 2011-07-19
66 | * Removed redundant bundle exec from rake db:migrate task as bundler will inject 'bundle exec rake' it since 1.0.8.
67 |
68 | == 0.5.18 / 2011-05-24
69 | * Added task to tail apache logs. (lightcap)
70 | * Using --binstubs to put executables in app_root/bin. (lightcap)
71 | * Added unicorn recipe to stop/start/restart. (timo3377)
72 | * Fix for eylogger and ruby 1.9.x. (atavistock)
73 | * Changed resque restart to sudo instead of run. (mdolian)
74 |
75 | == 0.5.17 / 2011-04-13
76 | * Fixed it so resque:restart is not called for any deploy.
77 |
78 | == 0.5.16 / 2011-04-08
79 | * Added improvement for postgres db:clone_to_local recipe.
80 | * Added correct requirement and symlink changes for resque recipe.
81 |
82 | == 0.5.15 / 2011-04-08
83 | * Added mysql2 support for database clone recipes.
84 |
85 | == 0.5.14 / 2011-04-06
86 | * Added resque recipe.
87 |
88 | == 0.5.13 / 2011-03-07
89 | * turned off EY weather notification.
90 |
91 | == 0.5.12 / 2011-03-07
92 | * turned off EY weather notification.
93 |
94 | == 0.5.11 / 2011-02-16
95 | * changed the exception variable for DJ recipe
96 |
97 | == 0.5.10 / 2011-02-16
98 | * added Delayed Job monit restart commands
99 |
100 | == 0.5.9 / 2011-01-28
101 | * bug fix for bundle install command
102 |
103 | == 0.5.8 / 2009-12-21
104 | * updated database clone to local task - adding ability to use mysql2 adapter.
105 |
106 | == 0.5.7 / 2009-12-21
107 | * updated database tasks - new xCloud infrastructure requires staging databases to use master not replica for tasks
108 |
109 | == 0.5.6 / 2009-6-17
110 | * updated bundler task so it won't install test or development gems
111 | * updated database tasks - based on Tyler Poland's update
112 |
113 | == 0.5.5 / 2009-3-16
114 | * fixed 2 bugs that are in 0.5.4 with the SSL and bundler recipes
115 | * use this version with bundler version 0.9.2
116 |
117 | == 0.5.4 / 2009-3-16
118 | * fixed gem bundler issue
119 |
120 | == 0.5.3 / 2009-1-27
121 | * created task cap ssl:create
122 | * use this version with bundler version 0.8
123 |
124 | == 0.5.2 / 2009-12-17
125 | * renamed task cap slice:tail_production_logs to cap slice:tail_environment_logs
126 |
127 | == 0.5.1 / 2009-11-12
128 | * using bundler's cache instead of symlinking on each deploy.
129 |
130 | == 0.5.0 / 2009-10-07
131 | * moved from github to gemcutter for hosting.
132 |
133 | == 0.4.16 / 2009-09-30
134 | * apps ping weather app before and after each deploy
135 |
136 | == 0.4.15 / 2009-09-02
137 | * added include in gemspec for bundler file
138 |
139 | == 0.4.14 / 2009-08-31
140 | * restored functionality to remove temporary compressed sql file after db:clone_prod_to_staging
141 | * renamed gz extension files to bz2 in db:clone_to_local
142 | * memcached: fix netcat not hanging up on a flush
143 |
144 | == 0.4.13 / 2009-07-30
145 | * changed Gemfile to absolute path
146 | * merged changes from square/master for unshared_remote_cache cached deploy strategy
147 |
148 | == 0.4.12 / 2009-06-26
149 | * removed condition for dbhost that was useless and tested db:clone_prod_to_staging
150 |
151 | == 0.4.11 / 2009-06-25
152 | * changed nginx start and restart to give output to cap
153 | * fixed db:clone_to_local task
154 |
155 | == 0.4.10 / 2009-06-24
156 | * using nohup on nginx start and restart
157 |
158 | == 0.4.9 / 2009-06-22
159 | * added nginx reload, upgrade and configtest - thanks Randy (ydnar)
160 |
161 | == 0.4.8 / there is no 0.4.8, just like there is no spoon.
162 |
163 | == 0.4.7 / 2009-05-12
164 | * fixed bug in clone_prod_to_staging and clone_to_local db tasks for postgres
165 | where the regex matching the password prompt for the restore was wrong
166 |
167 | == 0.4.6 / 2009-03-24
168 | * fixed bug to restore clone_prod_to_staging using the compressed file
169 |
170 | == 0.4.5 / 2009-03-03
171 | * happy square root day!
172 | * added the staging restore to db:clone_prod_to_staging
173 |
174 | == 0.4.4 / 2009-03-03
175 | * happy square root day!
176 | * fixed the *correct* database.rb file for the db:clone_prod_to_staging
177 |
178 | == 0.4.3 / 2009-02-11
179 | * updated db:dump command for Engine Yard Solo offering, fixing a bug where
180 | the dbname wasn't included.
181 |
182 | == 0.4.2 / 2009-02-11
183 | * added condition to determine if the db:dump command is run against our
184 | traditional offering or the new cloud offering
185 | * fixed bug where if a production db host name doesn't have a -master at the
186 | end it won't run the db:clone_prod_to_staging correctly
187 |
188 | == 0.4.1 / 2009-01-09
189 | * fixed bug for passenger:restart
190 |
191 | == 0.4.0 / 2009-01-09
192 | * add passenger:restart task
193 | * add apache stop, start, restart and reload tasks
194 | * Don't display database passwords in the logs, output, etc. use stdin instead
195 |
196 | == 0.3.11 / 2008-11-09
197 | * filtered_remote_cache uses svn switch
198 |
199 | == 0.3.9 / 2008-09-29
200 | * add reindex task for /engineyard/bin/acts_as_sphinx_searchd
201 | * add reindex and configure task for /engineyard/bin/ultrasphinx_searchd
202 | * add reindex and configure task for /engineyard/bin/thinking_sphinx_searchd
203 |
204 | == 0.3.8 / 2008-09-22
205 | * add PostgreSQL support to the database.rb recipe.
206 |
207 | == 0.3.7 / 2008-08-23
208 | * fix from customer for filtered_remote_cache to just use plain grep.
209 |
210 | == 0.3.6 / 2008-07-17
211 | * features updated VERSION
212 |
213 | == 0.3.5 / 2008-07-17
214 | * filtered_remote_cache uses cached checkout's repository root for comparison for speedier tagged/branched deploys
215 |
216 | == 0.3.4 / 2008-06-17
217 | * rake install_gem_no_doc for faster install
218 | * set role to db for backup_name task
219 | * add migration support for merb
220 |
221 | == 0.3.3 / 2008-05-07
222 | * add ey_logger to log deploys to server
223 |
224 | == 0.3.2 / 2008-04-29
225 | * adding db:clone_to_local task to take a dump on the slice, fetch it, and load it into your dev db on your workstation
226 | * only clone from replica database
227 | * remove call to variable in task desc
228 | * fix ferret symlinking
229 | * gemspec for github
230 |
231 | == 0.3.1 / 2008-03-17
232 | * Make the custom maintenance pages actually work!
233 |
234 | == 0.3.0 / 2008-03-05
235 | * Adding custom maintenance pages is now as easy as copying a custom html file to #{shared_path}/system/maintenance.html.custom
236 |
237 | == 0.2.10 / 2008-03-02
238 | * Symlink memcached.yml only on :memcached => true, except :no_release => true
239 |
240 | == 0.2.9 / 2008-02-27
241 | * Fix a bug with ultrasphinx:configure running on multiple hosts
242 |
243 | == 0.2.8 / 2008-02-20
244 | * Add tasks for solr starting,stopping, and log tailing.
245 | * Add monit namespace and make appservers php/merb compatible
246 | * Make ultrasphinx symlinking a little smarter
247 | * Add tomcat tasks for spongecell
248 |
249 | == 0.2.6 / 2008-02-14
250 | * Make mongrel restarts only apply to mongrel slices
251 |
252 | == 0.2.5 / 2008-02-11
253 | * Added db cloning task
254 |
255 | == 0.2.4 / 2008-02-06
256 | * Symlink memcached.yml in on deploy if you enable the callback
257 |
258 | == 0.2.3 / 2008-02-01
259 | * Make log tailing environmentally aware. Also add tasks to tail mongrel logs
260 |
261 | == 0.2.2 / 2008-01-25
262 | * sphinx:configure ultrasphinx configuration task
263 |
264 | == 0.2.1 / 2008-01-25
265 | * override default deploy recipe start/stop tasks
266 |
267 | == 0.2.0 / 2008-01-23
268 | * sphinx:symlink ultrasphinx configuration directory
269 |
270 | == 0.1.9 / 2008-01-21
271 | * Correct memcached tasks.
272 |
273 | == 0.1.8 / 2008-01-20
274 | * Add memcached tasks
275 |
276 | == 0.1.7 / 2008-01-19
277 | * fix symlink_configs task
278 |
279 | == 0.1.6 / 2008-01-19
280 | * add restart tasks for backgroundrb
281 |
282 | == 0.1.5 / 2008-01-18
283 | * fixed bug in filtered_remote_cache that prevented a changed checkout URL from taking over the cache
284 |
285 | == 0.1.4 / 2008-01-17
286 | * added sphinx:[reindex|start|stop|restart] matches only app servers with :sphinx => true
287 |
288 | == 0.1.3 / 2008-01-17
289 | * filtered_remote_cache to removes the cached copy of the source URL changed
290 |
291 | == 0.1.2 / 2008-01-15
292 | * added filtered_remote_cache capistrano deployment strategy
293 |
294 | == 0.1.1 / 2008-01-15
295 | * removed database tasks until problem with 'defer' is solved
296 |
297 | == 0.1.0 / 2008-01-14
298 | * Bugfix for empty :application variable
299 |
300 | == 0.0.1 / 2008-01-14
301 | * Initial release
302 |
--------------------------------------------------------------------------------