Skip to content

Instantly share code, notes, and snippets.

@mmrwoods
Last active January 1, 2016 05:39
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save mmrwoods/8100006 to your computer and use it in GitHub Desktop.
Save mmrwoods/8100006 to your computer and use it in GitHub Desktop.
Dumping ground for capistrano recipes...
namespace :account do
desc "Change the deploy user password"
task :reset_password do
deploy_user = user
deploy_pass = generate_password
as_sudo_user do
find_servers.each do |server|
if read_file("/etc/passwd", :hosts => server.host).lines.grep(/#{deploy_user}:/).any?
run "echo \"#{deploy_user}:#{deploy_pass}\" | #{sudo} chpasswd", :hosts => server.host
logger.important "Password for deploy user set to '#{deploy_pass}' on #{server.host}"
else
logger.important "Password not set on #{server.host}, deploy user not found"
end
end
end
end
namespace :setup do
desc "Creates deploy user and configures pubkey auth"
task :default do
create_user_account
set_group_membership
configure_ssh_keys
end
task :create_user_account do
deploy_user = user
deploy_pass = generate_password
as_sudo_user do
find_servers.each do |server|
if read_file("/etc/passwd", :hosts => server.host).lines.grep(/#{deploy_user}:/).empty?
sudo "useradd -m #{deploy_user}", :hosts => server.host
logger.important "Deploy user created on #{server.host}"
run "echo \"#{deploy_user}:#{deploy_pass}\" | #{sudo} chpasswd", :hosts => server.host
ui.say "Note: password for deploy user set to #{deploy_pass}"
else
logger.important "Deploy user not created on #{server.host}, account exists"
end
end
end
end
task :set_group_membership do
deploy_user = user
as_sudo_user do
find_servers.each do |server|
# FIXME: this tries to add the user to the apache group on servers without apache
%w{apache rvm}.each do |group|
unless read_file("/etc/group", :hosts => server.host).lines.grep(/#{group}/).empty?
# groups exists, ensure user is member
unless capture("groups #{deploy_user}", :hosts => server.host).chomp.match(/\b#{group}\b/)
# user not in group, add...
sudo "usermod -a -G #{group} #{deploy_user}"
end
if group == "apache"
# set apache as default/login group
sudo "usermod -g apache #{deploy_user}", :hosts => server.host
end
end
end
end
end
end
task :configure_ssh_keys do
deploy_user = user
sshdir = "/home/#{deploy_user}/.ssh"
pubkey = `cat ~/.ssh/id_rsa.pub`
as_sudo_user do
sudo "mkdir -p #{sshdir}"
sudo "touch #{sshdir}/authorized_keys"
sudo "mv #{sshdir}/authorized_keys /tmp/"
sudo "chown #{user} /tmp/authorized_keys"
sudo "echo \"#{pubkey}\" >> /tmp/authorized_keys"
sudo "mv /tmp/authorized_keys #{sshdir}/"
sudo "chmod 700 #{sshdir}"
sudo "chmod 600 #{sshdir}/authorized_keys"
sudo "chown -R #{deploy_user} #{sshdir}"
end
end
end
namespace :check do
desc "Checks that pubkey auth works and you can run commands as root"
task :default do
auth_keys
sudo_user
end
task :auth_keys do
run "echo `whoami`"
end
task :sudo_user do
as_sudo_user do
# FIXME: should not be aware of rvm
sudo "echo `whoami`", :shell => fetch(:rvm_install_shell,:bash)
end
end
end
end
# reload after making configuration changes
after "apache:configure", "apache:reload"
after "apache:setup", "apache:reload"
after "apache:deploy:setup", "apache:reload"
_cset(:apache_vhost_template) { "vhost_http.conf" }
_cset(:apache_server_name) do
ui.ask("Enter server name for apache vhost (e.g. www.myapp.com):")
end
namespace :apache do
desc "Installs apache web server using yum"
task :install, :roles => :web do
yum :install, :httpd, "httpd-devel", :mod_ssl
service :httpd, :start
end
desc "Restarts apache/httpd service"
task :restart, :roles => :web do
service :httpd, :restart
end
task :reload, :roles => :web do
# Note: reloading is roughly equivalent to a graceful restart
service :httpd, :reload
end
namespace :configure do
desc "Configures apache web server"
task :default do
start_on_boot
remove_welcome_page
upload_security_conf
end
task "start_on_boot", :roles => :web do
as_sudo_user do
sudo "chkconfig httpd on"
end
end
task "remove_welcome_page", :roles => :web do
as_sudo_user do
sudo "rm -rf /etc/httpd/conf.d/welcome.conf"
end
end
task :upload_security_conf, :roles => :web do
as_sudo_user do
upload_resource "apache/security.conf", "/etc/httpd/conf.d/security.conf", :sudo => true
copy_permissions "/etc/httpd/conf/httpd.conf", "/etc/httpd/conf.d/security.conf"
end
end
end
namespace :setup do
desc "Prepares apache for deployments in general"
task :default do
configure_virtual_hosts
configure_ssl
end
task :configure_virtual_hosts, :roles => :web do
as_sudo_user do
config_file = "/etc/httpd/conf/httpd.conf"
tmp_file = "/tmp/httpd.conf"
if capture("tail -n 50 #{config_file}").lines.grep("Include vhost.d/*").empty?
sudo "mkdir -p /etc/httpd/vhost.d"
sudo "sed '/^NameVirtualHost/d' #{config_file} | sed '/Include vhost.d/d' > #{tmp_file}"
sudo "echo 'NameVirtualHost *:80' >> #{tmp_file}"
sudo "echo 'NameVirtualHost *:443' >> #{tmp_file}"
sudo "echo 'Include vhost.d/*.conf' >> #{tmp_file}"
keep_original config_file
sudo "mv #{tmp_file} #{config_file}"
copy_original_permissions config_file
else
logger.important "Warning: apache already configured for name based virtual hosts"
end
end
end
task :configure_ssl, :roles => :web do
as_sudo_user do
config_file = "/etc/httpd/conf.d/ssl.conf"
tmp_file = "/tmp/ssl.conf"
unless read_file(config_file).lines.grep(/^<VirtualHost/).empty?
# strip virtual host declaration from general ssl configuration
sudo "sed -n '/^<VirtualHost/q;p' #{config_file} > #{tmp_file}"
keep_original config_file
sudo "mv #{tmp_file} #{config_file}"
copy_original_permissions config_file
end
# upload SSL certificates and shared configuration file
sudo "mkdir -p /etc/httpd/ssl"
Dir.glob("config/deploy/resources/ssl/*.*").each do |path|
resource = path.sub("config/deploy/resources/","")
upload_resource resource, "/tmp/#{File.basename(path)}"
sudo "mv /tmp/#{File.basename(path)} /etc/httpd/ssl/"
end
sudo "chown root:root /etc/httpd/ssl/*"
sudo "chmod 644 /etc/httpd/ssl/*"
sudo "chmod 600 /etc/httpd/ssl/*.key"
end
end
end
namespace :deploy do
desc "Prepares apache for a specific deploy target"
task :setup, :roles => :web do
as_sudo_user do
template_name = fetch(:apache_vhost_template)
destination = "/etc/httpd/vhost.d/#{fetch(:apache_server_name)}.conf"
upload_template template_name, destination, :sudo => true
end
end
end
end
namespace :backup do
desc "Installs daily filesystem backup script"
task :install do
as_sudo_user do
ui.say "Uploading backup script..."
upload_resource "backup/dumb_ass_backup.rb", "/usr/local/sbin/dumb_ass_backup.rb",
:mode => "700", :owner => "root", :group => "root"
end
end
namespace :configure do
desc "Configures daily backups for each server"
task :default do
write_configuration
upload_crontab
end
task :write_configuration do
as_sudo_user do
# default configuration for dumb ass backup script
config = {
"sources" => {
"etc" => "/etc",
"crontabs" => "/var/spool/cron"
},
"destination" => "/var/local/backup"
}
find_servers.each do |server|
ui.say "Generating backup configuration..."
if has_directory("/var/lib/pgsql", :hosts => server.host)
# postgres installed, add backups to sources
config["sources"]["postgres"] = "/var/lib/pgsql/**/backups/*"
end
if has_directory("/var/lib/aide", :hosts => server.host)
# aide installed, add db file to sources
config["sources"]["aide"] = "/var/lib/aide/aide.db.gz"
end
content = config.to_yaml
ui.say "Creating destination directory..."
sudo "mkdir -p #{config["destination"]}", :hosts => server.host
ui.say "Uploading backup configuration..."
destination = "/tmp/dumb_ass_backup.yml"
upload_content content, destination, :hosts => server.host, :mode => "644"
sudo "chown root:root /tmp/dumb_ass_backup.yml", :hosts => server.host
sudo "mv /tmp/dumb_ass_backup.yml /usr/local/etc/", :hosts => server.host
end
end
end
task :upload_crontab do
as_sudo_user do
ui.say "Uploading backup crontab file..."
upload_resource "backup/crontab", "/etc/cron.d/backup",
:mode => "644", :owner => "root", :group => "root"
end
end
end
end
require 'yaml'
require 'bundler/capistrano'
require 'capistrano/ext/multistage'
require 'active_support/core_ext/numeric/time'
require 'securerandom'
default_run_options[:pty] = true
ssh_options[:forward_agent] = true
set :scm, :git
set(:deploy_to) { "/var/www/#{application}" }
set :user, "deploy"
set :rake, "bundle exec rake"
set :use_sudo, false
set(:bundle_flags) {
# install gem bundle from local cache if packaged gems found
if system("git ls-files | grep 'vendor/cache/' > /dev/null")
"--deployment --local"
else
"--deployment"
end
}
set(:deploy_via) { :remote_cache }
set(:copy_exclude) { ".git" }
# deploy current branch by default
set(:branch) do
current_branch = `git branch`.lines.grep(/\*/).first.delete("* ").chomp
if current_branch != "master"
logger.important "Setting branch to '#{current_branch}'"
print "Continuing in "
9.downto(0) do |n|
print n; sleep 1; print "\b"
end
print "\n"
end
current_branch
end
# User for tasks that require sudo. By default, tasks that need to
# run commands as a user with sudo privileges will prompt for the
# username. Override this behaviour by just setting the sudo user.
set(:sudo_user) do
ui.ask("Enter user for sudo: "){|q| q.validate = /\w/}
end
def ui
Capistrano::CLI.ui
end
def test_remotely(expression, opts={})
self.send(
opts[:sudo] ? :sudo_capture : :capture,
"test #{expression}; echo $?",
opts
).chomp == "0"
end
def has_file(path, opts={})
test_remotely "-f #{path}", opts
end
def has_directory(path, opts={})
test_remotely "-d #{path}", opts
end
def has_stdout(expression, opts={})
self.send(
opts[:sudo] ? :sudo_capture : :capture,
"#{expression} 2> /dev/null",
opts
).chomp != ""
end
def has_repo(repo_id)
has_stdout("yum repolist | egrep '^\**#{repo_id}'")
end
def read_file(path, opts={})
self.send(
opts[:sudo] ? :sudo_capture : :capture,
"cat #{path}",
opts
).chomp
end
def generate_password(length=8)
chars = 'abcdefghjkmnpqrstuvwxyzABCDEFGHJKLMNOPQRSTUVWXYZ23456789'
password = ''
length.times { |i| password << chars[rand(chars.length)] }
password
end
# Helper method to temporarily switch users in the middle of a run.
# The primary use case is to switch to a user other than the deploy
# user before attempting to execute a command via sudo, as the
# deploy user does not always have sufficient privileges to run
# commands as root, yet we have tasks which require root privileges.
#
# Shamelessly stolen from Paul Gross's blog post at:
# http://www.pgrs.net/2008/08/06/switching-users-during-a-capistrano-deploy/
def as_user(new_user, &block)
old_user = fetch(:user)
if old_user == new_user
yield
return
end
set :user, new_user
sessions.values.each { |session| session.close }
sessions.clear
yield
set :user, old_user
sessions.values.each { |session| session.close }
sessions.clear
end
def as_sudo_user(&block)
# hack: force the password prompt to appear immediately
# note: setting :sudo_prompt does not change the prompt
silence_logger do
as_user(sudo_user) { sudo "echo foo", :shell => :sh }
end
as_user(sudo_user, &block)
end
def silence_logger
old_log_level = logger.level
logger.level = Logger::IMPORTANT
yield
ensure
logger.level = old_log_level
end
# Note: cannot use capture with sudo
def sudo_capture(cmd, opts={})
tmp_file = "/tmp/#{SecureRandom.hex}"
sudo "#{cmd} > #{tmp_file}", opts
output = capture("cat #{tmp_file}", opts)
ensure
sudo "rm -rf #{tmp_file}", opts
end
def yum(action, *packages)
# possible TODO: prevent install action from upgrading automagically
as_sudo_user do
# FIXME: should not be aware of rvm
sudo "yum -y #{action.to_s} #{packages.join(' ')}", :shell => fetch(:rvm_install_shell,:bash)
end
end
def service(name, action)
as_sudo_user do
sudo "service #{name} #{action}"
end
end
def update_configuration(config_file, param, value)
# TODO: raise exception or at least warn when setting not found
logger.important "update config #{param}=#{value} -> #{config_file}"
keep_original config_file
tmp_file = "/tmp/#{SecureRandom.hex}"
# FIXME: will match multiple lines if param found both commented out and not
sudo "sed 's/^#*[ \t]*#{param}[ \t]*=.*/#{param}\=#{value}/' #{config_file} > #{tmp_file}"
copy_permissions config_file, tmp_file
sudo "mv #{tmp_file} #{config_file}"
rescue
sudo "rm -rf #{tmp_file}" if defined? tmp_file
raise $!
end
def upload_template(template_name, destination, opts={})
path = "config/deploy/templates/#{template_name}.erb"
content = ERB.new(File.read(path), 0, "%<>").result(binding)
logger.important "upload template #{path} -> #{destination}"
ui.say ui.color(content.strip, :blue)
if opts[:sudo] || opts[:owner] || opts[:group]
tmp_file = "/tmp/#{SecureRandom.hex}"
put content, tmp_file, opts
sudo "chown #{opts[:owner]} #{tmp_file}" if opts[:owner]
sudo "chgrp #{opts[:group]} #{tmp_file}" if opts[:group]
sudo "mv #{tmp_file} #{destination}"
else
put content, destination, opts
end
end
def upload_content(content, destination, opts={})
logger.important "upload content -> #{destination}"
ui.say ui.color(content.strip, :blue)
if opts[:sudo] || opts[:owner] || opts[:group]
tmp_file = "/tmp/#{SecureRandom.hex}"
put content, tmp_file, opts
sudo "chown #{opts[:owner]} #{tmp_file}" if opts[:owner]
sudo "chgrp #{opts[:group]} #{tmp_file}" if opts[:group]
sudo "mv #{tmp_file} #{destination}"
else
put content, destination, opts
end
end
def upload_resource(resource, destination, opts={})
path = "config/deploy/resources/#{resource}"
logger.important "upload resource #{path} -> #{destination}"
if opts[:sudo] || opts[:owner] || opts[:group]
tmp_file = "/tmp/#{SecureRandom.hex}"
upload path, tmp_file, opts
sudo "chown #{opts[:owner]} #{tmp_file}" if opts[:owner]
sudo "chgrp #{opts[:group]} #{tmp_file}" if opts[:group]
sudo "mv #{tmp_file} #{destination}"
else
upload path, destination, opts
end
end
def copy_permissions(reference, target)
sudo "chmod --reference=#{reference} #{target}"
sudo "chown --reference=#{reference} #{target}"
end
def keep_original(path)
# keep a copy of the original config file, and don't overwrite it once created
path = capture("readlink -f #{path}").chomp if path.match(/^~/) # expand path if relative to home dir
sudo "cp -p --no-clobber #{path} #{path}.original"
end
def copy_original_permissions(path)
copy_permissions("#{path}.original", path)
end
def package_gem(name, version=">=0")
tmp_dir = "/tmp/#{name}-pack"
target_dir = "#{Dir.pwd}/vendor/#{name}"
run_locally "rm -rf #{tmp_dir}"
run_locally "mkdir -p #{target_dir}"
run_locally "mkdir -p #{tmp_dir}/cache"
Dir.chdir("#{tmp_dir}/cache") do
run_locally "gem fetch #{name} --version '#{version}'"
gem_file = `ls`.chomp
gem_spec = YAML.load(`gem specification #{gem_file}`)
gem_spec.runtime_dependencies.each do |dep|
run_locally "gem fetch #{dep.name} --version '#{dep.requirement}'"
end
end
run_locally "rm -rf #{target_dir}/cache"
run_locally "mv #{tmp_dir}/cache #{target_dir}/"
puts "Cleaning up..."
run_locally "rm -rf #{tmp_dir}"
end
def upload_gem(name)
as_sudo_user do
vendor_path = "#{Dir.pwd}/vendor"
sudo "mkdir -p #{shared_path}/#{name}/cache"
sudo "chown -R #{user} #{shared_path}/#{name}"
puts Dir.glob("#{vendor_path}/#{name}/cache/*.gem")
Dir.glob("#{vendor_path}/#{name}/cache/*.gem").each do |path|
# note: do not use upload helper - upload used as task name
transfer :up, path, "#{shared_path}/#{name}/cache/#{File.basename(path)}"
end
end
end
def install_gem(name, version=">=0")
# note: run as sudo user because deploy user may not yet exist
as_sudo_user do
# if gems have been packed and uploaded, install from local gem files
if has_directory("#{shared_path}/#{name}/cache")
capture("ls -1 #{shared_path}/#{name}/cache/*.gem").split("\n").map(&:strip).each do |gemfile|
run "gem install #{gemfile} --version '#{version}' --no-ri --no-rdoc --local --force"
end
else
run "gem install #{name} --version '#{version}' --no-ri --no-rdoc"
end
end
end
def get_app_version
# use tagged version when deploying to master
# use branch name and abbreviated commit sha otherwise
head = `git log origin/#{branch} --abbrev-commit --oneline -n1 | cut -f1 -d' '`.chomp
version = `git describe origin/#{branch} --tags --match "v[0-9]*" 2> /dev/null`.chomp.sub(/^v/,'')
if branch == 'master' && version.length > 0 # note: allow for no tagged versions yet
return version
else
return "#{branch}-g#{head}"
end
end
def get_history
# TODO: raise if head not tagged
deploy_tags = `git tag | egrep ^#{stage}-`.split("\n")
changes = {}
previous_tag = nil
deploy_tags.each do |current_tag|
commit_range = previous_tag.nil? ? "#{current_tag}" : "#{previous_tag}..#{current_tag}"
changes[current_tag] = `git log #{commit_range} --no-merges --format=format:"%h %s (%an)"`.chomp
previous_tag = current_tag
end
return "".tap do |output|
changes.keys.sort.reverse.each do |tag|
output << "#{tag}\n"
output << "#{changes[tag]}\n"
output << "\n"
end
end
end
def get_changes
history_file = File.join(current_release, "HISTORY")
raise "History file not found, cannot get changes" unless has_file(history_file)
cmds = []
cmds << "tail -n +2 #{history_file}" # get history file without first line/tag
cmds << "awk '/^#{stage}-/{exit}1'" # get changes until next tag
cmds << "sed '/^$/d'" # remove blank lines
cmds << "sed '/^#{stage}-/d'" # remove the tags
capture(cmds.join(" | ")).chomp
end
def get_deployer
if system('which git 2>&1 > /dev/null')
deployer = `git config user.name`.chomp
else
deployer = `whoami`.chomp
end
end
desc "Create a deployable build of the app with all dependencies"
task :build do
puts "Obtaining version info from tags..."
app_version = get_app_version
puts "Building version #{app_version}"
tmp_dir = "tmp/#{application}-#{app_version}"
puts "Cloning repository..."
run_locally "rm -rf #{tmp_dir}"
run_locally "git clone --local --no-hardlinks . #{tmp_dir}"
run_locally "rm -rf #{tmp_dir}/.git"
if ui.agree("Include bundler, passenger, rvm and ruby in build?")
puts "Packaging bundler..."
find_and_execute_task "bundler:pack"
run_locally "cp -Rp vendor/bundler #{tmp_dir}/vendor/"
puts "Packaging passenger gems..."
find_and_execute_task "passenger:pack"
run_locally "cp -Rp vendor/passenger #{tmp_dir}/vendor/"
puts "Packaging rvm and ruby archives..."
find_and_execute_task "rvm_offline:pack"
run_locally "cp -Rp vendor/rvm #{tmp_dir}/vendor/"
end
puts "Creating version file..."
run_locally "echo #{app_version} > #{tmp_dir}/VERSION"
puts "Creating history file..."
File.open("#{tmp_dir}/HISTORY", "w") { |f| f.puts get_history }
puts "Creating build archive..."
run_locally "cd tmp && tar -czf #{application}-#{app_version}.tar.gz #{application}-#{app_version}"
if ui.agree("Tag this build?")
git.prepare_tree
git.tag_build
end
puts "Cleaning up..."
run_locally "rm -rf #{tmp_dir}"
puts "Build created at #{Dir.pwd}/tmp/#{application}-#{app_version}.tar.gz"
end
namespace :bundler do
desc "Installs bundler gem on app servers"
task :install, :roles => :app do
install_gem :bundler
end
desc "Packages bundler gem to vendor/bundler"
task :pack do
package_gem :bundler
end
desc "Uploads packaged bundler gem to app servers"
task :upload, :roles => :app do
upload_gem :bundler
end
end
require 'active_support'
class CampfireRoom
def initialize(config)
@token = config[:token]
@hostname = "#{config[:account]}.campfirenow.com"
@room_id = get_room_id(config[:room])
end
def speak(message)
send_request("https://#{@hostname}/room/#{@room_id}/speak.json", {:message => {:body => "#{message}"}})
end
private
def send_request(url, content=nil)
curl_opts = ["--max-time 5", "--silent", "--user #{@token}:X"]
if content
curl_opts << "--header 'Content-Type: application/json'"
curl_opts << "--data '#{ActiveSupport::JSON.encode(content)}'"
end
response = `curl #{curl_opts.join(' ')} #{url}`
return ActiveSupport::JSON.decode(response)
end
def get_room_id(name)
room = get_rooms.select{|room| room["name"] == name}.first
raise "Room '#{name}' not found" if room.nil?
return room["id"]
end
def get_rooms
response = send_request("https://#{@hostname}/rooms.json")
rooms = response["rooms"]
raise "No rooms found" if rooms.nil?
return rooms
end
end
namespace :campfire do
namespace :deploy do
desc "Sends deploy notification to campfire"
task :notify do
raise "Campfire options not set" unless fetch(:campfire_options, nil)
begin
message = "[#{application}] #{get_deployer} deployed #{get_app_version} to #{stage}"
CampfireRoom.new(campfire_options).speak(message)
rescue Exception
puts "Campfire deploy notification failed with error #{$!}"
end
end
end
end
# variables for database.yml (TODO: handle multiple adapters/dbtypes)
_cset(:db_user) { "#{application}_#{stage}" }
_cset(:db_name) { "#{application}_#{stage}" }
_cset(:db_pass) { generate_password }
_cset(:db_host) do
if roles[:db].servers.size == 1
"localhost"
else
# Note: assumes that caspistrano will only ever know about the master db server
roles[:db].servers.select{|server| server.options[:no_release] }.first.host
end
end
after "deploy:finalize_update", "db:yaml:symlink"
namespace :db do
namespace :yaml do
desc "Symlink database.yml for this release"
task :symlink, :roles => :app, :except => { :no_release => true } do
run "ln -nfs #{shared_path}/config/database.yml #{release_path}/config/database.yml"
end
task :write, :except => { :no_release => true } do
if has_file("#{shared_path}/config/database.yml")
logger.important "Skipping upload of shared database.yml, destination file exists"
else
run "mkdir -p #{shared_path}/config"
upload_template "database.yml", "#{shared_path}/config/database.yml"
end
end
task :read, :roles => :db, :only => { :primary => true } do
content = read_file("#{shared_path}/config/database.yml")
config = YAML.load(content)[rails_env]
set :db_user, config["username"]
set :db_name, config["database"]
set :db_host, config["host"]
set :db_pass, config["password"]
end
task :delete, :except => { :no_release => true } do
run "rm -f #{shared_path}/config/database.yml"
end
end
end
<%= rails_env %>:
adapter: postgresql
encoding: utf8
pool: 5
host: <%= db_host %>
database: <%= db_name %>
username: <%= db_user %>
password: <%= db_pass %>
# Recipe allows you to set environment variables in capistrano
# and upload a .env file containing name value pairs on deploy.
#
# Set environment variables using standard capistrano variables
# with an dotenv_ prefix, e.g. set :dotenv_foo, "bar"
#
# Names for environment variables written to the .env file are
# generated by replacing the dotenv_ prefix with the application
# name, e.g. with app name "myapp", set(:dotenv_foo,"bar") would
# result in name value pair "MYAPP_FOO=bar"
namespace :dotenv do
namespace :deploy do
desc "Generate and upload .env file"
task :upload, :roles => :app do
# generate content for .env file
content = variables.keys.select{|key|
# only variables with env_ prefix
key.to_s.match(/^dotenv_/)
}.map{|key|
# generate name value pair, replacing env_ prefix
# with application name to generate env var name
name = key.to_s.sub(/^dotenv_/,"#{fetch(:application)}_").upcase
value = fetch(key)
"#{name}=#{value}"
}.join("\n")
# upload .env file to root of release path
ui.say "Uploading .env file..."
upload_content content, "#{release_path}/.env"
end
end
end
_cset(:duplicity_remote_host) do
ui.ask("Enter hostname of duplicity target:")
end
_cset(:duplicity_remote_user) do
ui.ask("Enter username for #{duplicity_remote_host}:")
end
_cset(:duplicity_passphrase) do
ui.ask("Enter GPG passphrase for symmetric encryption:")
end
_cset(:duplicity_remote_port) { 22 }
_cset(:duplicity_remote_path) { "duplicity" } # note: relative to default dir
_cset(:duplicity_local_path) { "/var/local/backup" }
# Use pexpect backend by default because host key checking is
# broken with the current version of paramiko on centos 6.
# Explicity set cipher algorithm to AES256 because GnuPG default
# is CAST5 and we don't want to modify gnupg.conf to overide it.
_cset(:duplicity_options) do
"--verbosity info --ssh-backend pexpect --gpg-options '--cipher-algo=aes256'"
end
namespace :duplicity do
desc "Installs duplicity and dependencies"
task :install do
yum :install, :duplicity, :sshpass, :gnupg2
end
namespace :configure do
desc "Configures duplicity backups"
task :default do
check_ssh_connection
generate_ssh_keys
ssh_copy_id
do_test_backup
upload_duplicity_cron
end
task :check_ssh_connection do
as_sudo_user do
find_servers.each do |server|
begin
@ssh_password ||= ui.ask("Enter password for #{duplicity_remote_user}@#{duplicity_remote_host}: ") { |q| q.echo = false }
tmp_file = "/tmp/#{SecureRandom.hex}"
put @ssh_password, "#{tmp_file}", :hosts => server.host
begin
sudo "sshpass -f #{tmp_file} ssh #{duplicity_remote_user}@#{duplicity_remote_host} -p #{duplicity_remote_port} -o StrictHostKeyChecking=no \"echo OK\""
ensure
sudo "rm -rf #{tmp_file}", :hosts => server.host
end
rescue Capistrano::CommandError
logger.important "SSH connection failed from #{server.host} to #{duplicity_remote_port}"
unless duplicity_remote_port.to_i == 22
logger.info "Check that firewall rules allow outbound tcp traffic to #{duplicity_remote_host} on port #{duplicity_remote_port}"
end
exit 1
end
end
end
end
task :generate_ssh_keys do
as_sudo_user do
find_servers.each do |server|
if has_file("/root/.ssh/id_rsa", :hosts => server.host, :sudo => true)
logger.info "Skipping key generation for root@#{server.host}, found existing RSA keys"
else
logger.important "Generating RSA keys for root@#{server.host}"
sudo "ssh-keygen -t rsa -N '' -f /root/.ssh/id_rsa", :hosts => server.host
end
end
end
end
task :ssh_copy_id do
as_sudo_user do
find_servers.each do |server|
begin
sudo "ssh #{duplicity_remote_user}@#{duplicity_remote_host} -p #{duplicity_remote_port} -o PasswordAuthentication=no -o PreferredAuthentications=publickey \"echo OK\"", :hosts => server.host
logger.info "Skipping ssh-copy-id for root@#{server.host}, public key authentication already configured"
rescue Capistrano::CommandError
logger.important "Copying public key to #{duplicity_remote_host}"
# use sshpass and scp install public key to authorized keys file on remote host
# Note: ssh-copy-id doesn't work with rsync.net (relies on ~ expansion which is not supported)
@ssh_password ||= ui.ask("Enter password for #{duplicity_remote_user}@#{duplicity_remote_host}: ") { |q| q.echo = false }
tmp_file = "/tmp/#{SecureRandom.hex}"
put @ssh_password, "#{tmp_file}", :hosts => server.host
begin
if sudo_capture("sshpass -f #{tmp_file} ssh #{duplicity_remote_user}@#{duplicity_remote_host} -p #{duplicity_remote_port} \"test -f .ssh/authorized_keys\"; echo $?", :hosts => server.host).chomp == "0"
# found authorized keys file on backup target, append to file
sudo "sshpass -f #{tmp_file} scp -P #{duplicity_remote_port} #{duplicity_remote_user}@#{duplicity_remote_host}:.ssh/authorized_keys /tmp/", :hosts => server.host
sudo "chown #{user} /tmp/authorized_keys"
sudo "cat /root/.ssh/id_rsa.pub >> /tmp/authorized_keys"
else
# no authorized keys file on backup target, create new file
sudo "cp /root/.ssh/id_rsa.pub /tmp/authorized_keys"
end
sudo "sshpass -f #{tmp_file} scp -P #{duplicity_remote_port} /tmp/authorized_keys #{duplicity_remote_user}@#{duplicity_remote_host}:.ssh/", :hosts => server.host
sudo "ssh #{duplicity_remote_user}@#{duplicity_remote_host} -p #{duplicity_remote_port} \"chmod 600 .ssh/authorized_keys; chown #{duplicity_remote_user}:#{duplicity_remote_user} .ssh/authorized_keys\"", :hosts => server.host
sudo "rm -rf /tmp/authorized_keys", :hosts => server.host
ensure
sudo "rm -rf #{tmp_file}", :hosts => server.host
end
end
end
end
end
task :do_test_backup do
as_sudo_user do
find_servers.each do |server|
local_tmp_dir = "/tmp/#{SecureRandom.hex}"
sftp_base_url = "sftp://#{duplicity_remote_user}@#{duplicity_remote_host}:#{duplicity_remote_port}"
remote_tmp_dir = "tmp/duplicity" # note: relative to default path (usually user's home directory)
begin
sudo "mkdir -p #{local_tmp_dir}"
sudo "touch #{local_tmp_dir}/file{1..100}.tmp"
options = "--no-encryption #{duplicity_options}"
logger.important "Creating test backup on #{duplicity_remote_host}"
sudo "duplicity full #{options} #{local_tmp_dir} #{sftp_base_url}/#{remote_tmp_dir}"
logger.important "Verifying test backup on #{duplicity_remote_host}"
sudo "duplicity verify #{options} #{sftp_base_url}/#{remote_tmp_dir} #{local_tmp_dir}"
ensure
sudo "rm -rf #{local_tmp_dir}"
sudo "ssh #{duplicity_remote_user}@#{duplicity_remote_host} -p #{duplicity_remote_port} 'rm -rf #{remote_tmp_dir}'"
end
end
end
end
task :upload_duplicity_cron do
as_sudo_user do
upload_template "duplicity.cron", "/etc/cron.daily/duplicity.cron",
:mode => "700", :owner => "root", :group => "root"
end
end
end
end
namespace :email do
namespace :deploy do
desc "Sends deploy notification to email recipients"
task :notify, :roles => :app, :except => { :no_release => true } do
raise "Email notification failed, CHANGES file not found" unless has_file("#{release_path}/CHANGES")
if fetch(:email_recipients, nil).nil?
logger.important "Email notification not sent - no recipients configured"
else
subject = "[#{application}] #{get_deployer} deployed #{get_app_version} to #{stage}"
Array(fetch(:email_recipients)).each do |recipient|
# note: to avoid send mail as application/octet-stream (see mail manpage),
# use sed to remove carriage returns and cat -v to print other control chars
run "sed 's/\\r$//' #{release_path}/CHANGES | cat -v | mail -s '#{subject}' #{recipient}", :once => true
end
end
end
end
end
# based on capistrano-deploytags, simplified and extracted out to allow tagging
# to be conditionally configured and be used to tag builds in addition to deploys.
# Also updated code to ensure that we tag the real revision that's deployed.
namespace :git do
set(:git_user) { `git config user.name`.chomp }
set(:git_tag) { "#{stage}-#{Time.now.strftime("%Y.%m.%d-%H%M%S")}" }
def delete_git_tag
run_locally "git tag -d #{git_tag}"
run_locally "git push origin :refs/tags/#{git_tag}"
end
desc "Prepare git tree so we can tag on successful deployment"
task :prepare_tree do
logger.important "Preparing git tree for tagging"
# FIXME: is this really necessary??
unless `git fetch && git diff #{branch} --shortstat`.strip.empty?
logger.important "Failed to prepare git tree for tagging"
raise "Pending git changes, stash or commit your changes and try again"
end
run_locally "git checkout #{branch}"
run_locally "git pull origin #{branch}"
end
desc "Add git tag for successful deployment"
task :tag_deploy do
on_rollback { delete_git_tag }
logger.important "Tagging deploy"
run_locally "git tag -a #{git_tag} -m \"#{git_user} deployed #{get_app_version} to #{stage}\" #{real_revision}"
run_locally "git push --tags"
end
desc "Add git tag for successful build"
task :tag_build do
on_rollback { delete_git_tag }
logger.important "Tagging build"
run_locally "git tag -a #{git_tag} -m \"#{git_user} built #{get_app_version} for #{stage}\" HEAD"
run_locally "git push --tags"
end
end
# ABORTED GPG, STORED ONLY FOR FUTURE REFERENCE
# always force an export of the secret key after generating
after "gpg:gen_key", "gpg:export_secret_keys"
namespace :gpg do
desc "Installs GnuPG and dependencies"
task :install do
yum :install, :gpg, :haveged
end
desc "Configures GnuPG and dependencies"
task :configure do
as_sudo_user do
# install and run haveged to ensure sufficient entropy is available
# to avoid blocking IO issues with /dev/random when generating keys
sudo "chkconfig haveged on"
service :haveged, :start
end
end
desc "Generates key pairs on each server"
task :gen_key do
as_sudo_user do
# ensure gpg-agent is running so we can generate keys
# see http://www.netroby.com/view.php?id=3571#.U2Ndpq1dXR0
find_servers.each do |server|
if capture("ps -A | grep gpg-agent 2> /dev/null | cat", :hosts => server.host).chomp == ""
sudo "gpg-agent --daemon --use-standard-socket"
end
hostname = capture("hostname", :hosts => server.host).chomp
logger.log Capistrano::Logger::IMPORTANT, "Generating GPG keys for #{hostname}"
gpg_key_name = hostname
if sudo_capture("gpg --list-secret-keys root@#{hostname} 2> /dev/null | cat", :hosts => server.host).chomp != ""
logger.log Capistrano::Logger::IMPORTANT, "WARNING: Existing private key found matching root@#{hostname}"
logger.info "Skipping GPG key generation for root@#{hostname}"
next
end
# generate params file for unattended key generation
# see https://www.gnupg.org/documentation/manuals/gnupg-devel/Unattended-GPG-key-generation.html
content = %{
%echo Generating key for #{hostname}
Key-Type: default
Subkey-Type: default
Name-Real: #{hostname}
Name-Email: root@#{hostname}
Expire-Date: 0
# Do a commit here, so that we can later print "done" :-)
%commit
%echo done
}
tmp_file = "/tmp/#{SecureRandom.hex}"
put content, "#{tmp_file}", :hosts => server.host
begin
sudo "gpg --batch --gen-key #{tmp_file}", :hosts => server.host
ensure
sudo "rm -rf #{tmp_file}", :hosts => server.host
end
end
end
end
desc "Exports secret keys from each server"
task :export_secret_keys do
as_sudo_user do
find_servers.each do |server|
hostname = capture("hostname", :hosts => server.host).chomp
logger.log Capistrano::Logger::IMPORTANT, "Exporting secret keys from #{hostname}"
content = sudo_capture("gpg --armour --export-secret-keys")
filename = "#{hostname}.gpg.asc"
path = File.join(Dir.pwd, filename)
File.open(path, "w"){|f| f.puts content}
logger.log Capistrano::Logger::IMPORTANT, "Export saved as #{path}"
logger.info "Move this export file to a secure backup location!"
end
end
end
end
<%= deploy_to %>/shared/log/*.log {
daily
rotate 7
delaycompress
missingok
notifempty
sharedscripts
postrotate
/sbin/service httpd reload > /dev/null 2>/dev/null || true
endscript
}
after "deploy:setup", "logrotation:deploy:setup"
namespace :logrotation do
namespace :deploy do
desc "Generates logrotate configuration for app"
task :setup, :roles => :app do
as_sudo_user do
destination = "/etc/logrotate.d/#{application}_#{stage}"
upload_template "logrotate", destination, :sudo => true
end
end
end
end
after "mod_security:install", "apache:reload"
after "mod_security:remove", "apache:reload"
after "mod_security:configure", "apache:reload"
namespace :mod_security do
desc "Installs mod_security apache module using yum"
task :install, :roles => :web do
yum :install, :mod_security, :mod_security_crs
end
task :remove, :roles => :web do
yum :remove, :mod_security
end
task :reinstall, :roles => :web do
yum :reinstall, :mod_security, :mod_security_crs
end
desc "Configures mod_security apache module"
task :configure, :roles => :web do
as_sudo_user do
# Remove rulesets we don't want (TODO: don't be so brutal)
sudo "rm -f /etc/httpd/modsecurity.d/activated_rules/modsecurity_crs_20_protocol_violations.conf"
sudo "rm -f /etc/httpd/modsecurity.d/activated_rules/modsecurity_crs_21_protocol_anomalies.conf"
sudo "rm -f /etc/httpd/modsecurity.d/activated_rules/modsecurity_crs_30_http_policy.conf"
sudo "rm -f /etc/httpd/modsecurity.d/activated_rules/modsecurity_crs_41_sql_injection_attacks.conf"
sudo "rm -f /etc/httpd/modsecurity.d/activated_rules/modsecurity_crs_41_xss_attacks.conf"
sudo "rm -f /etc/httpd/modsecurity.d/activated_rules/modsecurity_crs_50_outbound.conf"
end
end
end
LoadModule passenger_module <%= passenger_root %>/ext/apache2/mod_passenger.so
PassengerRoot <%= passenger_root %>
PassengerRuby <%= passenger_ruby %>
PassengerFriendlyErrorPages off
Header always unset "X-Powered-By"
Header always unset "X-Runtime"
after "passenger:configure", "apache:reload"
_cset(:passenger_version) { "~>3" }
namespace :passenger do
desc "Installs passenger module for apache"
task :install, :roles => :web do
# note: run as sudo user because deploy user may not yet exist
as_sudo_user do
install_gem :passenger, fetch(:passenger_version)
run "passenger-install-apache2-module --auto"
end
end
desc "Configures passenger module for apache"
task :configure, :roles => :web do
rvm_home = "/usr/local/rvm"
gem_home = "#{rvm_home}/gems/ruby-#{rvm_ruby_string}"
as_sudo_user do
# note: deploy user may not yet exist
passenger_gem = capture("ls -1 #{gem_home}/gems | grep passenger").split("\n").last.chomp
set :passenger_root, gem_home + "/gems/" + passenger_gem
set :passenger_ruby, "#{rvm_home}/wrappers/ruby-#{rvm_ruby_string}/ruby"
upload_template "passenger.conf", "/etc/httpd/conf.d/passenger.conf", :sudo => true
copy_permissions("/etc/httpd/conf/httpd.conf", "/etc/httpd/conf.d/passenger.conf")
end
end
desc "Packages passenger gem and dependencies to vendor/passenger"
task :pack do
package_gem :passenger, fetch(:passenger_version)
end
desc "Uploads packaged passenger gem and dependencies to servers"
task :upload, :roles => :web do
upload_gem :passenger
end
end
after "postfix:configure", "postfix:restart"
_cset(:postconf_inet_protocols) { "ipv4" }
_cset(:postconf_relayhost) { nil }
namespace :postfix do
desc "Installs postfix using yum"
task :install, :roles => :app do
yum :install, :postfix
end
desc "Restarts postfix service"
task :restart, :roles => :app do
service :postfix, :restart
end
namespace "configure" do
desc "Configures postfix server using postconf utility"
task :default do
start_on_boot
update_configuration
end
task "start_on_boot", :roles => :app do
as_sudo_user do
sudo "chkconfig postfix on"
end
end
desc "Updates postfix configuration using postconf utility"
task :update_configuration, :roles => :app do
config_file = "/etc/postfix/main.cf"
tmp_file = "/tmp/main.cf"
as_sudo_user do
keep_original config_file
variables.keys.select{|k| k.to_s.match(/^postconf_/) }.each do |key|
sudo "postconf -e \"#{key.to_s.sub('postconf_', '')}=#{fetch(key)}\""
end
end
end
end
end
# When installing on some RHEL servers, we ran into an issue with the loading
# of shared libraries. Though the postgresql installation had placed a config
# file in /etc/ld.so.conf.d/, the configuration didn't seem to reload and it
# was necessary to run ldconfig manually to ensure shared libs would be found.
after "postgres:install" do
as_sudo_user do
sudo "ldconfig"
end
end
after "postgres:configure", "postgres:restart"
after "postgres:setup", "postgres:restart"
namespace :postgres do
namespace :install do
desc "Installs postgres client and server using yum"
task :default do
add_yum_repo
install_client
install_libs
install_server
symlink_pg_config
initdb
end
task :add_yum_repo do
# TODO: exclude postgresql from CentOS/RHEL base and updates repos
as_sudo_user do
# TODO: run on each server individually - tho unlikely,
# servers could in theory run different distros
if has_file("/etc/centos-release")
package = "pgdg-centos92-9.2-6.noarch.rpm"
else
package = "pgdg-redhat92-9.2-6.noarch.rpm"
end
# note: rpm exits with non-zero status if package already installed
sudo "rpm -ivh http://yum.postgresql.org/9.2/redhat/rhel-6-x86_64/#{package} ; echo ''"
end
end
task :install_client, :roles => [:app, :db] do
yum :install, :postgresql92
end
task :install_libs, :roles => [:app, :db] do
yum :install, "postgresql92-devel"
end
task :install_server, :roles => :db, :only => { :no_release => true } do
yum :install, "postgresql92-server", "postgresql92-contrib"
end
task :symlink_pg_config, :roles => :app do
# pg gem needs to run pg_config, so just symlink it on the app servers.
# Note: when installed via yum, not all postgres binaries are available
# in the user's path, as only those that can be used with multiple
# versions are symlinked. See the following blog post for an explanation:
# http://people.planetpostgresql.org/devrim/index.php?/archives/43-How-to-install-PostgreSQL-9.0-Beta-1-to-FedoraCentOSRHEL.html
as_sudo_user do
if capture("which pg_config > /dev/null ; echo $?").chomp == "0"
logger.important "Warning: pg_config found in path, symlink not created"
else
sudo "ln -s /usr/pgsql-9.2/bin/pg_config /usr/bin/pg_config"
end
end
end
task :initdb, :roles => :db, :only => { :no_release => true } do
as_sudo_user do
sudo "service postgresql-9.2 initdb"
end
end
end
desc "Restarts postgresql-9.2 service"
task :restart, :roles => :db, :only => { :no_release => true } do
service "postgresql-9.2", :restart
end
namespace :configure do
desc "Configures postgres server"
task :default do
start_on_boot
upload_backup_cron
upload_maintenance_cron
upload_auth_config
configure_listen_addresses
end
task "start_on_boot", :roles => :db, :only => { :no_release => true } do
as_sudo_user do
sudo "chkconfig postgresql-9.2 on"
end
end
task :upload_backup_cron, :roles => :db, :only => { :no_release => true } do
as_sudo_user do
upload_resource "postgres/postgresql-backup.cron",
"/etc/cron.daily/postgresql-backup.cron",
:mode => "755", :owner => "root", :group => "root"
end
end
task :upload_maintenance_cron, :roles => :db, :only => { :no_release => true } do
as_sudo_user do
upload_resource "postgres/postgresql-maintenance.cron",
"/etc/cron.weekly/postgresql-maintenance.cron",
:mode => "755", :owner => "root", :group => "root"
end
end
task :upload_auth_config, :roles => :db, :only => { :no_release => true } do
as_sudo_user do
config_file = "/var/lib/pgsql/9.2/data/pg_hba.conf"
keep_original config_file
upload_resource "postgres/pg_hba.conf", config_file, :sudo => true
copy_original_permissions config_file
end
end
task :configure_listen_addresses, :roles => :db, :only => { :no_release => true } do
puts "DB host is #{fetch(:db_host)}"
if %w{ 127.0.0.1 localhost }.include?(fetch(:db_host))
# FIXME: assumes default configuration setting has not been modified
puts "Nothing to do, postgres listens on localhost by default"
else
# FIXME: brittle, relies on exact string match to modify listen addresses
puts "Configuring postgres to listen on all addresses..."
as_sudo_user do
config_file = "/var/lib/pgsql/9.2/data/postgresql.conf"
tmp_file = "/tmp/postgresql.conf"
sudo "sed \"s/#listen_addresses = 'localhost'/listen_addresses = '\\*'/\" #{config_file} > #{tmp_file}"
keep_original config_file
sudo "mv #{tmp_file} #{config_file}"
copy_original_permissions config_file
end
end
end
end
namespace :setup do
desc "Prepares postgres for deployments in general"
task :default do
create_deploy_user
configure_auth
configure_client
end
task :create_deploy_user, :roles => :db, :only => { :no_release => true } do
deploy_user = user
as_sudo_user do
# note: no password set here - see task to prepare psql client connection
sudo "su - postgres -c \"createuser #{deploy_user}\"; true" # return true to allow for fail if user exists
# deploy user needs to be superuser in order to create extensions
sudo "su - postgres -c \"psql -d template1 -c 'ALTER USER #{deploy_user} WITH SUPERUSER;'\""
end
end
# note: this task assumes that the remote address for each of the app servers,
# as seen from the database server, will match the host or IP specified when
# configuring the role or server in capistrano.
task :configure_auth, :roles => :db, :only => { :no_release => true } do
as_sudo_user do
config_file = "/var/lib/pgsql/9.2/data/pg_hba.conf"
tmp_file = "/tmp/pg_hba.conf"
keep_original config_file
roles[:app].servers.map(&:to_s).each do |host|
address = host.match(/^(\d+\.){3}\d+$/) ? "#{host}/32" : host
# remove existing host lines matching this host
# this is little heavy handed, but it's easier than grepping to check
# for existing line due to permissions issues when using capture helper
sudo "sed '/^host .* #{host}/d' #{config_file} > #{tmp_file}"
# and allow md5 auth from this host
line = "host all all #{address} md5"
run "echo \"#{line}\" >> #{tmp_file}"
sudo "mv #{tmp_file} #{config_file}"
copy_original_permissions config_file
end
end
end
task :configure_client, :roles => :db do
deploy_user = user
password = generate_password
# set the password on the db server
as_sudo_user do
sudo "su - postgres -c \"psql -d template1 -c \\\"ALTER USER #{deploy_user} PASSWORD '#{password}';\\\"\"", :only => { :no_release => true }
end
# create a .pgpass file for the client connection
run "echo \"*:*:*:#{user}:#{password}\" > ~/.pgpass", :only => { :primary => true }
run "chmod 600 /home/#{user}/.pgpass", :only => { :primary => true }
end
end
desc "Checks postgres client connection"
task :check, :roles => :db, :only => { :primary => true } do
run "psql -d template1 -h #{db_host} -c \"select 'foo'\""
end
namespace :deploy do
namespace :setup do
desc "Prepares postgres for a specific deploy target"
task :default do
db.yaml.write
create_database
grant_privileges
end
desc "[internal] Creates the database if it doesn't exist"
task :create_database, :roles => :db, :only => { :no_release => true } do
db.yaml.read
if capture("psql -d template1 -c \"SELECT datname FROM pg_database;\"").lines.grep(/#{db_name}/).empty?
run "psql -d template1 -c \"CREATE DATABASE #{db_name} ENCODING 'UTF-8';\""
else
logger.important "Warning: database not created because it already exists"
end
end
desc "[internal] Grants db user privileges on database"
task "grant_privileges", :roles => :db, :only => { :no_release => true } do
db.yaml.read
if capture("psql -d template1 -c \"SELECT usename FROM pg_user;\"").lines.grep(/#{db_user}/).empty?
run "psql -d template1 -c \"CREATE USER #{db_user} PASSWORD '#{db_pass}';\""
else
run "psql -d template1 -c \"ALTER USER #{db_user} PASSWORD '#{db_pass}';\""
end
run "psql -d template1 -c \"GRANT ALL PRIVILEGES ON DATABASE #{db_name} TO #{db_user};\""
run "psql -d #{db_name} -c \"GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO #{db_user};\""
run "psql -d template1 -c \"ALTER DATABASE #{db_name} OWNER TO #{db_user};\""
end
end
end
end
after "release_files:deploy:upload", "release_files:deploy:verify"
namespace :release_files do
namespace :deploy do
desc "Generate and upload release files"
task :upload do
# Version and history files can only be created when deploying via git repo.
# When deploying via copy, they should already exist in the deployable build.
put get_app_version, "#{release_path}/VERSION" unless fetch(:deploy_via) == :copy
put get_history, "#{release_path}/HISTORY" unless fetch(:deploy_via) == :copy
put get_changes, "#{release_path}/CHANGES"
end
desc "Verifies that release files exist on remote server"
task :verify, :roles => :app do
%w{ VERSION HISTORY CHANGES }.each do |file|
raise "#{file} file not found" unless has_file("#{release_path}/#{file}")
end
end
end
end
# set configuration variables before requiring rvm-capistrano
set :rvm_type, :system # system install for multiple rvm users
set :rvm_install_with_sudo, true # needed for system install
# disable setting rvm_require_role - rvm-capistrano sets the default shell
# to the rvm shell for all servers, not just those matching rvm_require_role.
# TODO: update rvm-capistrano if fixed in later version, otherwise report bug
# set :rvm_require_role, :app # only install rvm to app servers
require 'rvm/capistrano'
# run rvm tasks as the sudo user
# FIXME: switch the user for the duration of the task
namespaces[:rvm].task_list(:all).map(&:fully_qualified_name).each do |task_name|
before(task_name) { set :user, sudo_user }
end
# ensure sudo user is added to rvm group
# note: account:setup sets group membership for deploy user
after "rvm:install_rvm" do
sudo "usermod -a -G rvm #{user}", :shell => rvm_install_shell
end
# install required packages before installing rvm
before "rvm:install_rvm", "rvm:install_pkgs"
# require rvm-capistrano-offline if found in gem bundle and add callbacks
if Bundler.locked_gems.dependencies.map(&:name).include?("rvm-capistrano-offline")
require 'rvm/capistrano/offline'
# run rvm-offline tasks as sudo user
namespaces[:rvm_offline].task_list(:all).map(&:fully_qualified_name).each do |task_name|
next if task_name == "rvm_offline:pack"
before(task_name) { set :user, sudo_user }
end
# ensure sudo user is added to rvm group
after "rvm_offline:install" do
sudo "usermod -a -G rvm #{user}", :shell => rvm_install_shell
end
# install packages before installing rvm via rvm-offline
before "rvm_offline:install", "rvm:install_pkgs"
end
namespace :rvm do
# FIXME: should only run on servers matching rvm_require_role
task :install_pkgs do
packages = %w{
gcc gcc-c++ glibc patch readline readline-devel zlib
zlib-devel libyaml-devel libffi-devel openssl-devel
make bzip2 autoconf automake libtool bison curl-devel
libxslt-devel libxml2-devel
}
yum :install, *packages
end
desc "Reinstall RVM ruby on the server"
task :reinstall_ruby do
set :rvm_install_ruby, :reinstall
install_ruby
end
end
<VirtualHost *:443>
ServerName <%= apache_server_name %>
RedirectPermanent / http://<%= apache_server_name %>/
Include ssl/<%= application %>.conf
</VirtualHost>
<VirtualHost *:80>
ServerName <%= apache_server_name %>
DocumentRoot <%= deploy_to %>/current/public
PassengerMinInstances 1
RailsEnv <%= rails_env %>
<Directory <%= deploy_to %>/current/public>
AllowOverride all
Options -MultiViews
</Directory>
# Show maintenance page if it exists
ErrorDocument 503 /system/maintenance.html
RewriteEngine On
RewriteCond %{REQUEST_URI} !\.(css|gif|jpg|png)$
RewriteCond %{DOCUMENT_ROOT}/system/maintenance.html -f
RewriteCond %{SCRIPT_FILENAME} !maintenance.html
RewriteRule ^.*$ - [redirect=503,last]
</VirtualHost>
<VirtualHost *:80>
ServerName <%= apache_server_name %>
RedirectPermanent / https://<%= apache_server_name %>/
</VirtualHost>
<VirtualHost *:443>
ServerName <%= apache_server_name %>
DocumentRoot <%= deploy_to %>/current/public
PassengerMinInstances 1
RailsEnv <%= rails_env %>
<Directory <%= deploy_to %>/current/public>
AllowOverride all
Options -MultiViews
</Directory>
Include ssl/<%= application %>.conf
# Show maintenance page if it exists
ErrorDocument 503 /system/maintenance.html
RewriteEngine On
RewriteCond %{REQUEST_URI} !\.(css|gif|jpg|png)$
RewriteCond %{DOCUMENT_ROOT}/system/maintenance.html -f
RewriteCond %{SCRIPT_FILENAME} !maintenance.html
RewriteRule ^.*$ - [redirect=503,last]
</VirtualHost>
_cset(:yum_cron_check_only) { "yes" }
_cset(:yum_cron_download_only) { "no" }
_cset(:yum_cron_debug_level) { 1 }
_cset(:yum_cron_randomwait) { 15 }
_cset(:yum_cron_yum_parameter) { ""}
_cset(:yum_cron_mailto) do
ui.ask("Enter email address for yum-cron notifications:")
end
namespace :yum_cron do
desc "Installs yum-cron"
task :install do
yum :install, "yum-cron"
end
desc "Configures yum-cron"
task :configure do
as_sudo_user do
# start on boot
sudo "chkconfig yum-cron on"
# modify configuration settings
variables.keys.select{|k| k.to_s.match(/^yum_cron_/) }.each do |key|
param = key.to_s.sub('yum_cron_', '').upcase
value = fetch(key)
update_configuration("/etc/sysconfig/yum-cron", param, value)
end
# start service
service "yum-cron", :start
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment