lib/ec2onrails/recipes.rb in pauldowman-ec2onrails-0.9.10 vs lib/ec2onrails/recipes.rb in pauldowman-ec2onrails-0.9.10.0
- old
+ new
@@ -26,71 +26,63 @@
require 'ec2onrails/version'
require 'ec2onrails/capistrano_utils'
include Ec2onrails::CapistranoUtils
+
+
+Dir[File.join(File.dirname(__FILE__), "recipes/*")].find_all{|x| File.file? x}.each do |recipe|
+ require recipe
+end
+
+
Capistrano::Configuration.instance.load do
unless ec2onrails_config
raise "ec2onrails_config variable not set. (It should be a hash.)"
end
cfg = ec2onrails_config
- #:apache or :nginx
- cfg[:web_proxy_server] ||= :apache
-
set :ec2onrails_version, Ec2onrails::VERSION::STRING
- set :image_id_32_bit, Ec2onrails::VERSION::AMI_ID_32_BIT
- set :image_id_64_bit, Ec2onrails::VERSION::AMI_ID_64_BIT
set :deploy_to, "/mnt/app"
set :use_sudo, false
set :user, "app"
- #in case any changes were made to the configs, like changing the number of mongrels
- before "deploy:cold", "ec2onrails:server:grant_sudo_access"
+ #in case any changes were made to the configs
+ before "deploy:cold", "ec2onrails:setup"
+
after "deploy:symlink", "ec2onrails:server:set_roles", "ec2onrails:server:init_services"
after "deploy:cold", "ec2onrails:db:init_backup", "ec2onrails:db:optimize", "ec2onrails:server:restrict_sudo_access"
+ # TODO I don't think we can do gem source -a every time because I think it adds the same repo multiple times
after "ec2onrails:server:install_gems", "ec2onrails:server:add_gem_sources"
+
+ # There's an ordering problem here. For convenience, we want to run 'rake gems:install' automatically
+ # on every deploy, but in the ec2onrails:setup task I want to do update_code before any other
+ # setup tasks, and at that point I don't want run_rails_rake_gems_install to run. So run_rails_rake_gems_install
+ # can't be triggered by an "after" hook on update_code.
+ # But users might want to have their own tasks triggered after update_code, and those tasks will
+ # fail if they require gems to be installed (or anything else to be set up).
+ #
+ # The best solution is to use an after hook on "deploy:symlink" or "deploy:update" instead of on
+ # "deploy:update_code"
+ on :load do
+ before "deploy:symlink", "ec2onrails:server:run_rails_rake_gems_install"
+ before "deploy:symlink", "ec2onrails:server:install_system_files"
+ end
+
- # override default start/stop/restart tasks
- namespace :deploy do
- desc <<-DESC
- Overrides the default Capistrano deploy:start, uses \
- 'god start app'
- DESC
- task :start, :roles => :app do
- sudo "god start app"
- # sudo "god monitor app"
- end
-
- desc <<-DESC
- Overrides the default Capistrano deploy:stop, uses \
- 'god stop app'
- DESC
- task :stop, :roles => :app do
- # sudo "god unmonitor app"
- sudo "god stop app"
- end
-
- desc <<-DESC
- Overrides the default Capistrano deploy:restart, uses \
- 'god restart app'
- DESC
- task :restart, :roles => :app do
- sudo "god restart app"
- end
- end
-
namespace :ec2onrails do
desc <<-DESC
Show the AMI id's of the current images for this version of \
EC2 on Rails.
DESC
task :ami_ids do
- puts "32-bit server image for EC2 on Rails #{ec2onrails_version}: #{image_id_32_bit}"
- puts "64-bit server image for EC2 on Rails #{ec2onrails_version}: #{image_id_64_bit}"
+ puts "32-bit server image (US location) for EC2 on Rails #{ec2onrails_version}: #{Ec2onrails::VERSION::AMI_ID_32_BIT_US}"
+ puts "64-bit server image (US location) for EC2 on Rails #{ec2onrails_version}: #{Ec2onrails::VERSION::AMI_ID_64_BIT_US}"
+ puts "32-bit server image (EU location) for EC2 on Rails #{ec2onrails_version}: #{Ec2onrails::VERSION::AMI_ID_32_BIT_EU}"
+ puts "64-bit server image (EU location) for EC2 on Rails #{ec2onrails_version}: #{Ec2onrails::VERSION::AMI_ID_64_BIT_EU}"
end
desc <<-DESC
Copies the public key from the server using the external "ssh"
command because Net::SSH, which is used by Capistrano, needs it.
@@ -121,664 +113,34 @@
desc <<-DESC
Prepare a newly-started instance for a cold deploy.
DESC
task :setup do
- server.set_mail_forward_address
- server.set_timezone
- server.install_packages
- server.install_gems
- server.deploy_files
- server.setup_web_proxy
- server.set_roles
- server.enable_ssl if cfg[:enable_ssl]
- server.set_rails_env
- server.restart_services
+ # we now have some things being included inside the app so we deploy
+ # the app's code to the server before we do any other setup
+ server.upload_deploy_keys
deploy.setup
- db.create
- server.harden_server
- db.enable_ebs
- end
-
- desc <<-DESC
- Deploy and restore database from S3
- DESC
- task :restore_db_and_deploy do
- db.recreate
deploy.update_code
- deploy.symlink
- db.restore
- deploy.migrations
- end
-
- namespace :ec2 do
- desc <<-DESC
- DESC
- task :configure_firewall do
- # TODO
- end
- end
-
- namespace :db do
- desc <<-DESC
- [internal] Load configuration info for the database from
- config/database.yml, and start mysql (it must be running
- in order to interact with it).
- DESC
- task :load_config do
- unless hostnames_for_role(:db, :primary => true).empty?
- db_config = YAML::load(ERB.new(File.read("config/database.yml")).result)[rails_env.to_s] || {}
- cfg[:db_name] ||= db_config['database']
- cfg[:db_user] ||= db_config['username'] || db_config['user']
- cfg[:db_password] ||= db_config['password']
- cfg[:db_host] ||= db_config['host']
- cfg[:db_port] ||= db_config['port']
- cfg[:db_socket] ||= db_config['socket']
-
- if (cfg[:db_host].nil? || cfg[:db_host].empty?) && (cfg[:db_socket].nil? || cfg[:db_socket].empty?)
- raise "ERROR: missing database config. Make sure database.yml contains a '#{rails_env}' section with either 'host: hostname' or 'socket: /var/run/mysqld/mysqld.sock'."
- end
-
- [cfg[:db_name], cfg[:db_user], cfg[:db_password]].each do |s|
- if s.nil? || s.empty?
- raise "ERROR: missing database config. Make sure database.yml contains a '#{rails_env}' section with a database name, user, and password."
- elsif s.match(/['"]/)
- raise "ERROR: database config string '#{s}' contains quotes."
- end
- end
- end
- end
- desc <<-DESC
- Create the MySQL database. Assumes there is no MySQL root \
- password. To create a MySQL root password create a task that's run \
- after this task using an after hook.
- DESC
- task :create, :roles => :db do
- on_rollback { drop }
- load_config
- start
-
- # remove the default test database, though sometimes it doesn't exist (perhaps it isn't there anymore?)
- run %{mysql -u root -e "drop database if exists test; flush privileges;"}
-
- # removing anonymous mysql accounts
- run %{mysql -u root -D mysql -e "delete from db where User = ''; flush privileges;"}
- run %{mysql -u root -D mysql -e "delete from user where User = ''; flush privileges;"}
-
- run %{mysql -u root -e "create database if not exists #{cfg[:db_name]};"}
- run %{mysql -u root -e "grant all on #{cfg[:db_name]}.* to '#{cfg[:db_user]}'@'%' identified by '#{cfg[:db_password]}';"}
- run %{mysql -u root -e "grant reload on *.* to '#{cfg[:db_user]}'@'%' identified by '#{cfg[:db_password]}';"}
- run %{mysql -u root -e "grant super on *.* to '#{cfg[:db_user]}'@'%' identified by '#{cfg[:db_password]}';"}
+ ec2onrails.server.allow_sudo do
+ server.set_timezone
+ server.set_mail_forward_address
+ server.install_packages
+ server.install_gems
+ server.run_rails_rake_gems_install
+ server.deploy_files # DEPRECATED, see install_system_files
+ server.install_system_files
+ server.set_roles
+ server.enable_ssl if cfg[:enable_ssl]
+ server.set_rails_env
+ server.restart_services
+ db.create
+ server.harden_server
+ db.enable_ebs
+ db.set_root_password
end
-
- desc <<-DESC
- Move the MySQL database to Amazon's Elastic Block Store (EBS), \
- which is a persistant data store for the cloud.
- OPTIONAL PARAMETERS:
- * SIZE: Pass in num in gigs, like 10, to set the size, otherwise it will \
- default to 10 gigs.
- * VOLUME_ID: The volume_id to use for the mysql database
- NOTE: keep track of the volume ID, as you'll want to keep this for your \
- records and probably add it to the :db role in your deploy.rb file \
- (see the ec2onrails sample deploy.rb file for additional information)
- DESC
- task :enable_ebs, :roles => :db, :only => { :primary => true } do
- # based off of Eric's work:
- # http://developer.amazonwebservices.com/connect/entry.jspa?externalID=1663&categoryID=100
- #
- # EXPLAINATION:
- # There is a lot going on here! At the end, the setup should be:
- # * create EBS volume if run outside of the ec2onrails:setup and
- # VOLUME_ID is not passed in when the cap task is called
- # * EBS volume attached to /dev/sdh
- # * format to xfs if new or do a xfs_check if previously existed
- # * mounted on /var/local and update /etc/fstab
- # * move /mnt/mysql_data -> /var/local/mysql_data
- # * move /mnt/log/mysql -> /var/local/log/mysql
- # * change mysql configs by writing /etc/mysql/conf.d/mysql-ec2-ebs.cnf
- # * keep a copy of the mysql configs with the EBS volume, and if that volume is hooked into
- # another instance, make sure the mysql configs that go with that volume are symlinked to /etc/mysql
- # * update the file locations of the mysql binary logs in /mnt/log/mysql/mysql-bin.index
- # * symlink the moved folders to their old position... makes the move to EBS transparent
- # * Amazon doesn't contain EBS information in the meta-data API (yet). So write
- # /etc/ec2onrails/ebs_info.yml
- # to contain the meta-data information that we need
- #
- # DESIGN CONSIDERATIONS
- # * only moving mysql data to EBS. seems the most obvious, and if we move over other components
- # we will have to share that bandwidth (1 Gbps pipe to SAN). So limiting to what we really need
- # * not moving all mysql logic over (tmp scratch space stays local). Again, this is to limit
- # unnecessary bandwidth usage, PLUS, we are charged per million IO to EBS
- #
- # TODO:
- # * make sure if we have a predefined ebs_vol_id, that we error out with a nice msg IF the zones do not match
- # * can we move more of the mysql cache files back to the local disk and off of EBS, like the innodb table caches?
- # * right now we force this task to only be run on one server; that works for db :primary => true
- # But what is the best way to make this work if it needs to setup multiple servers (like db slaves)?
- # I need to figure out how to do a direct mapping from a server definition to a ebs_vol_id
- # * when we enable slaves and we setup ebs volumes on them, make it transparent to the user.
- # have the slave create a snapshot of the db.master volume, and then use that to mount from
- # * need to do a rollback that if the volume is created but something fails, lets uncreate it?
- # carefull though! If it fails towards the end when information is copied over, it could cause information
- # to be lost!
- #
-
- mysql_dir_root = '/var/local'
- block_mnt = '/dev/sdh'
- servers = find_servers_for_task(current_task)
-
- if servers.empty?
- raise Capistrano::NoMatchingServersError, "`#{task.fully_qualified_name}' is only run for servers matching #{task.options.inspect}, but no servers matched"
- elsif servers.size > 1
- raise Capistrano::Error, "`#{task.fully_qualified_name}' is can only be run on one server, not #{server.size}"
- end
-
- vol_id = ENV['VOLUME_ID'] || servers.first.options[:ebs_vol_id]
-
- #HACK! capistrano doesn't allow arguments to be passed in if we call this task as a method, like 'db.enable_ebs'
- # the places where we do call it like that, we don't want to force a move to ebs, so....
- # if the call frame is > 1 (ie, another task called it), do NOT force the ebs move
- no_force = task_call_frames.size > 1
- prev_created = !(vol_id.nil? || vol_id.empty?)
- #no vol_id was passed in, but perhaps it is already mounted...?
- prev_created = true if !quiet_capture("mount | grep -inr '#{mysql_dir_root}' || echo ''").empty?
-
- unless no_force && (vol_id.nil? || vol_id.empty?)
- zone = quiet_capture("/usr/local/ec2onrails/bin/ec2_meta_data.rb -key 'placement/availability-zone'")
- instance_id = quiet_capture("/usr/local/ec2onrails/bin/ec2_meta_data.rb -key 'instance-id'")
-
- unless prev_created
- puts "creating new ebs volume...."
- size = ENV["SIZE"] || "10"
- cmd = "ec2-create-volume -s #{size} -z #{zone} 2>&1"
- puts "running: #{cmd}"
- output = `#{cmd}`
- puts output
- vol_id = (output =~ /^VOLUME\t(.+?)\t/ && $1)
- puts "NOTE: remember that vol_id"
- sleep(2)
- end
- vol_id.strip! if vol_id
- if quiet_capture("mount | grep -inr '#{block_mnt}' || echo ''").empty?
- cmd = "ec2-attach-volume -d #{block_mnt} -i #{instance_id} #{vol_id} 2>&1"
- puts "running: #{cmd}"
- output = `#{cmd}`
- puts output
- if output =~ /Client.InvalidVolume.ZoneMismatch/i
- raise Exception, "The volume you are trying to attach does not reside in the zone of your instance. Stopping!"
- end
-
-
- sleep(10)
- end
-
- ec2onrails.server.allow_sudo do
- # try to format the volume... if it is already formatted, lets run a check on
- # it to make sure it is ok, and then continue on
- # if errors, the device is busy...something else is going on here and it is already mounted... skip!
- if prev_created
- quiet_capture("sudo umount #{mysql_dir_root}") #unmount if need to
- sudo "xfs_check #{block_mnt}"
- else
- sudo "mkfs.xfs #{block_mnt}"
- end
-
- # if not added to /etc/fstab, lets do so
- sudo "sh -c \"grep -iqn '#{mysql_dir_root}' /etc/fstab || echo '#{block_mnt} #{mysql_dir_root} xfs noatime 0 0' >> /etc/fstab\""
- sudo "mkdir -p #{mysql_dir_root}"
- #if not already mounted, lets mount it
- sudo "sh -c \"mount | grep -iqn '#{mysql_dir_root}' || mount '#{mysql_dir_root}'\""
-
- #ok, now lets move the mysql stuff off of /mnt -> mysql_dir_root
- stop rescue nil #already stopped
- sudo "mkdir -p #{mysql_dir_root}/log"
- #move the data over, but keep a symlink to the new location for backwards compatability
- #and do not do it if /mnt/mysql_data has already been moved
- quiet_capture("sudo sh -c 'test ! -d #{mysql_dir_root}/mysql_data && mv /mnt/mysql_data #{mysql_dir_root}/'")
- sudo "mv /mnt/mysql_data /mnt/mysql_data_old 2>/dev/null || echo"
- sudo "ln -fs #{mysql_dir_root}/mysql_data /mnt/mysql_data"
-
- #but keep the tmpdir on mnt
- sudo "sh -c 'mkdir -p /mnt/tmp/mysql && chown mysql:mysql /mnt/tmp/mysql'"
- #move the logs over, but keep a symlink to the new location for backwards compatability
- #and do not do it if the logs have already been moved
- sudo("sudo sh -c 'test ! -d #{mysql_dir_root}/log/mysql_data && mv /mnt/log/mysql #{mysql_dir_root}/log/'")
- sudo "ln -fs #{mysql_dir_root}/log/mysql /mnt/log/mysql"
- quiet_capture("sudo sh -c \"test -f #{mysql_dir_root}/log/mysql/mysql-bin.index && \
- perl -pi -e 's%/mnt/log/%#{mysql_dir_root}/log/%' #{mysql_dir_root}/log/mysql/mysql-bin.index\"") rescue false
-
- if quiet_capture("test -d /var/local/etc/mysql && echo 'yes'").empty?
- txt = <<-FILE
-[mysqld]
- datadir = #{mysql_dir_root}/mysql_data
- tmpdir = /mnt/tmp/mysql
- log_bin = #{mysql_dir_root}/log/mysql/mysql-bin.log
- log_slow_queries = #{mysql_dir_root}/log/mysql/mysql-slow.log
-FILE
- put txt, '/tmp/mysql-ec2-ebs.cnf'
- sudo 'mv /tmp/mysql-ec2-ebs.cnf /etc/mysql/conf.d/mysql-ec2-ebs.cnf'
-
- #keep a copy
- sudo "rsync -aR /etc/mysql #{mysql_dir_root}/"
- end
- # lets use the mysql configs on the EBS volume
- sudo "mv /etc/mysql /etc/mysql.orig 2>/dev/null"
- sudo "ln -sf #{mysql_dir_root}/etc/mysql /etc/mysql"
-
- #just put a README on the drive so we know what this volume is for!
- txt = <<-FILE
-This volume is setup to be used by Ec2onRails for primary MySql database persistence.
-RAILS_ENV: #{fetch(:rails_env, 'undefined')}
-DOMAIN: #{fetch(:domain, 'undefined')}
-
-Modify this volume at your own risk
-FILE
-
- put txt, "/tmp/VOLUME-README"
- sudo "mv /tmp/VOLUME-README #{mysql_dir_root}/VOLUME-README"
- #update the list of ebs volumes
- #TODO: abstract this away into a helper method!!
- ebs_info = quiet_capture("cat /etc/ec2onrails/ebs_info.yml")
- ebs_info = ebs_info.empty? ? {} : YAML::load(ebs_info)
- ebs_info[mysql_dir_root] = {'block_loc' => block_mnt, 'volume_id' => vol_id}
- put(ebs_info.to_yaml, "/tmp/ebs_info.yml")
- sudo "mv /tmp/ebs_info.yml /etc/ec2onrails/ebs_info.yml"
- #lets start it back up
- start
- end #end of sudo
- end
- end
-
-
- desc <<-DESC
- [internal] Make sure the MySQL server has been started, just in case the db role
- hasn't been set, e.g. when called from ec2onrails:setup.
- (But don't enable monitoring on it.)
- DESC
- task :start, :roles => :db do
- sudo "god start db"
- # sudo "god monitor db"
- end
-
- task :stop, :roles => :db do
- # sudo "god unmonitor db"
- sudo "god stop db"
- end
-
-
- desc <<-DESC
- Drop the MySQL database. Assumes there is no MySQL root \
- password. If there is a MySQL root password, create a task that removes \
- it and run that task before this one using a before hook.
- DESC
- task :drop, :roles => :db do
- load_config
- run %{mysql -u root -e "drop database if exists #{cfg[:db_name]};"}
- end
-
- desc <<-DESC
- db:drop and db:create.
- DESC
- task :recreate, :roles => :db do
- drop
- create
- end
-
- desc <<-DESC
- Set a root password for MySQL, using the variable mysql_root_password \
- if it is set. If this is done db:drop won't work.
- DESC
- task :set_root_password, :roles => :db do
- if cfg[:mysql_root_password]
- run %{mysql -u root -e "UPDATE mysql.user SET Password=PASSWORD('#{cfg[:mysql_root_password]}') WHERE User='root'; FLUSH PRIVILEGES;"}
- end
- end
-
- desc <<-DESC
- Dump the MySQL database to the S3 bucket specified by \
- ec2onrails_config[:archive_to_bucket]. The filename will be \
- "database-archive/<timestamp>/dump.sql.gz".
- DESC
- task :archive, :roles => :db do
- run "/usr/local/ec2onrails/bin/backup_app_db.rb --bucket #{cfg[:archive_to_bucket]} --dir #{cfg[:archive_to_bucket_subdir]}"
- end
-
- desc <<-DESC
- Restore the MySQL database from the S3 bucket specified by \
- ec2onrails_config[:restore_from_bucket]. The archive filename is \
- expected to be the default, "mysqldump.sql.gz".
- DESC
- task :restore, :roles => :db do
- run "/usr/local/ec2onrails/bin/restore_app_db.rb --bucket #{cfg[:restore_from_bucket]} --dir #{cfg[:restore_from_bucket_subdir]}"
- end
-
- desc <<-DESC
- [internal] Initialize the default backup folder on S3 (i.e. do a full
- backup of the newly-created db so the automatic incremental backups
- make sense).
- DESC
- task :init_backup, :roles => :db do
- server.allow_sudo do
- sudo "/usr/local/ec2onrails/bin/backup_app_db.rb --reset"
- end
- end
-
- # do NOT run if the flag does not exist. This is placed by a startup script
- # and it is only run on the first-startup. This means after the db has been
- # optimized, this task will not work again.
- #
- # Of course you can overload it or call the file directly
- task :optimize, :roles => :db do
- if !quiet_capture("test -e /tmp/optimize_db_flag && echo 'file exists'").empty?
- begin
- sudo "/usr/local/ec2onrails/bin/optimize_mysql.rb"
- ensure
- sudo "rm -rf /tmp/optimize_db_flag" #remove so we cannot run again
- end
- else
- puts "skipping as it looks like this task has already been run"
- end
- end
-
end
-
- namespace :server do
- desc <<-DESC
- Tell the servers what roles they are in. This configures them with \
- the appropriate settings for each role, and starts and/or stops the \
- relevant services.
- DESC
- task :set_roles do
- # TODO generate this based on the roles that actually exist so arbitrary new ones can be added
- roles = {
- :web => hostnames_for_role(:web),
- :app => hostnames_for_role(:app),
- :db_primary => hostnames_for_role(:db, :primary => true),
- # doing th ebelow can cause errors elsewhere unless :db is populated.
- # :db => hostnames_for_role(:db),
- :memcache => hostnames_for_role(:memcache)
- }
- roles_yml = YAML::dump(roles)
- put roles_yml, "/tmp/roles.yml"
- server.allow_sudo do
- sudo "cp /tmp/roles.yml /etc/ec2onrails"
- #we want everyone to be able to read to it
- sudo "chmod a+r /etc/ec2onrails/roles.yml"
- sudo "/usr/local/ec2onrails/bin/set_roles.rb"
- end
- end
-
- task :init_services do
- server.allow_sudo do
- sudo "/usr/local/ec2onrails/bin/init_services.rb"
- end
- end
-
- task :setup_web_proxy, :roles => :web do
- sudo "/usr/local/ec2onrails/bin/setup_web_proxy.rb --mode #{cfg[:web_proxy_server].to_s}"
- end
- desc <<-DESC
- Change the default value of RAILS_ENV on the server. Technically
- this changes the server's mongrel config to use a different value
- for "environment". The value is specified in :rails_env.
- Be sure to do deploy:restart after this.
- DESC
- task :set_rails_env do
- rails_env = fetch(:rails_env, "production")
- sudo "/usr/local/ec2onrails/bin/set_rails_env #{rails_env}"
- end
-
- desc <<-DESC
- Upgrade to the newest versions of all Ubuntu packages.
- DESC
- task :upgrade_packages do
- sudo "aptitude -q update"
- sudo "sh -c 'export DEBIAN_FRONTEND=noninteractive; aptitude -q -y safe-upgrade'"
- end
-
- desc <<-DESC
- Upgrade to the newest versions of all rubygems.
- DESC
- task :upgrade_gems do
- sudo "gem update --system --no-rdoc --no-ri"
- sudo "gem update --no-rdoc --no-ri" do |ch, str, data|
- ch[:data] ||= ""
- ch[:data] << data
- if data =~ />\s*$/
- puts data
- choice = Capistrano::CLI.ui.ask("The gem command is asking for a number:")
- ch.send_data("#{choice}\n")
- else
- puts data
- end
- end
-
- end
-
- desc <<-DESC
- Install extra Ubuntu packages. Set ec2onrails_config[:packages], it \
- should be an array of strings.
- NOTE: the package installation will be non-interactive, if the packages \
- require configuration either log in as 'root' and run \
- 'dpkg-reconfigure packagename' or replace the package's config files \
- using the 'ec2onrails:server:deploy_files' task.
- DESC
- task :install_packages do
- sudo "aptitude -q update"
- if cfg[:packages] && cfg[:packages].any?
- sudo "sh -c 'export DEBIAN_FRONTEND=noninteractive; aptitude -q -y install #{cfg[:packages].join(' ')}'"
- end
- end
-
- desc <<-DESC
- Provide extra security measures. Set ec2onrails_config[:harden_server] = true \
- to allow the hardening of the server.
- These security measures are those which can make initial setup and playing around
- with Ec2onRails tricky. For example, you can be logged out of your server forever
- DESC
- task :harden_server do
- #NOTES: for those security features that will get in the way of ease-of-use
- # hook them in here
- if cfg[:harden_server]
- #lets install some extra packages:
- # denyhosts: sshd security tool. config file is already installed...
- #
- security_pkgs = %w{denyhosts}
- old_pkgs = cfg[:packages]
- begin
- cfg[:packages] = security_pkgs
- install_packages
- ensure
- cfg[:packages] = old_pkgs
- end
- end
- end
-
- desc <<-DESC
- Install extra rubygems. Set ec2onrails_config[:rubygems], it should \
- be with an array of strings.
- DESC
- task :install_gems do
- if cfg[:rubygems]
- cfg[:rubygems].each do |gem|
- sudo "gem install #{gem} --no-rdoc --no-ri" do |ch, str, data|
- ch[:data] ||= ""
- ch[:data] << data
- if data =~ />\s*$/
- puts data
- choice = Capistrano::CLI.ui.ask("The gem command is asking for a number:")
- ch.send_data("#{choice}\n")
- else
- puts data
- end
- end
- end
- end
- end
-
- desc <<-DESC
- Add extra gem sources to rubygems (to able to fetch gems from for example gems.github.com).
- Set ec2onrails_config[:rubygems_sources], it should be with an array of strings.
- DESC
- task :add_gem_sources do
- if cfg[:rubygems_sources]
- cfg[:rubygems_sources].each do |gem_source|
- sudo "gem sources -a #{gem_source}"
- end
- end
- end
-
- desc <<-DESC
- A convenience task to upgrade existing packages and gems and install \
- specified new ones.
- DESC
- task :upgrade_and_install_all do
- upgrade_packages
- upgrade_gems
- install_packages
- install_gems
- end
-
- desc <<-DESC
- Set the timezone using the value of the variable named timezone. \
- Valid options for timezone can be determined by the contents of \
- /usr/share/zoneinfo, which can be seen here: \
- http://packages.ubuntu.com/cgi-bin/search_contents.pl?searchmode=filelist&word=tzdata&version=gutsy&arch=all&page=1&number=all \
- Remove 'usr/share/zoneinfo/' from the filename, and use the last \
- directory and file as the value. For example 'Africa/Abidjan' or \
- 'posix/GMT' or 'Canada/Eastern'.
- DESC
- task :set_timezone do
- if cfg[:timezone]
- sudo "bash -c 'echo #{cfg[:timezone]} > /etc/timezone'"
- sudo "cp /usr/share/zoneinfo/#{cfg[:timezone]} /etc/localtime"
- end
- end
-
- desc <<-DESC
- Deploy a set of config files to the server, the files will be owned by \
- root. This doesn't delete any files from the server. This is intended
- mainly for customized config files for new packages installed via the \
- ec2onrails:server:install_packages task. Subdirectories and files \
- inside here will be placed within the same directory structure \
- relative to the root of the server's filesystem.
- DESC
- task :deploy_files do
- if cfg[:server_config_files_root]
- begin
- filename = "config_files.tar"
- local_file = "#{Dir.tmpdir}/#{filename}"
- remote_file = "/tmp/#{filename}"
- FileUtils.cd(cfg[:server_config_files_root]) do
- File.open(local_file, 'wb') { |tar| Minitar.pack(".", tar) }
- end
- put File.read(local_file), remote_file
- sudo "tar xvf #{remote_file} -o -C /"
- ensure
- rm_rf local_file
- sudo "rm -f #{remote_file}"
- end
- end
- end
-
- desc <<-DESC
- Restart a set of services. Set ec2onrails_config[:services_to_restart] \
- to an array of strings. It's assumed that each service has a script \
- in /etc/init.d
- DESC
- task :restart_services do
- if cfg[:services_to_restart] && cfg[:services_to_restart].any?
- cfg[:services_to_restart].each do |service|
- run_init_script(service, "restart")
- end
- end
- end
-
- desc <<-DESC
- Set the email address that mail to the app user forwards to.
- DESC
- task :set_mail_forward_address do
- run "echo '#{cfg[:mail_forward_address]}' >> /home/app/.forward" if cfg[:mail_forward_address]
- # put cfg[:admin_mail_forward_address], "/home/admin/.forward" if cfg[:admin_mail_forward_address]
- end
-
- desc <<-DESC
- Enable ssl for the web server. The SSL cert file should be in
- /etc/ssl/certs/default.pem and the SSL key file should be in
- /etc/ssl/private/default.key (use the deploy_files task).
- DESC
- task :enable_ssl, :roles => :web do
- #TODO: enable for nginx
- sudo "a2enmod ssl"
- sudo "a2ensite default-ssl"
- run_init_script("web_proxy", "restart")
- end
-
- desc <<-DESC
- Restrict the main user's sudo access.
- Defaults the user to only be able to \
- sudo to god
- DESC
- task :restrict_sudo_access do
- old_user = fetch(:user)
- begin
- set :user, 'root'
- sessions.clear #clear out sessions cache..... this way the ssh connections are reinitialized
- sudo "cp -f /etc/sudoers.restricted_access /etc/sudoers"
- # run "ln -sf /etc/sudoers.restricted_access /etc/sudoers"
- ensure
- set :user, old_user
- sessions.clear
- end
- end
-
- desc <<-DESC
- Grant *FULL* sudo access to the main user.
- DESC
- task :grant_sudo_access do
- allow_sudo
- end
-
- @within_sudo = 0
- def allow_sudo
- begin
- @within_sudo += 1
- old_user = fetch(:user)
- if @within_sudo > 1
- yield if block_given?
- true
- elsif capture("ls -l /etc/sudoers /etc/sudoers.full_access | awk '{print $5}'").split.uniq.size == 1
- yield if block_given?
- false
- else
- begin
- # need to cheet and temporarily set the user to ROOT so we
- # can (re)grant full sudo access.
- # we can do this because the root and app user have the same
- # ssh login preferences....
- #
- # TODO:
- # do not escalate priv. to root...use another user like 'admin' that has full sudo access
- set :user, 'root'
- sessions.clear #clear out sessions cache..... this way the ssh connections are reinitialized
- run "cp -f /etc/sudoers.full_access /etc/sudoers"
- set :user, old_user
- sessions.clear
- yield if block_given?
- ensure
- server.restrict_sudo_access if block_given?
- set :user, old_user
- sessions.clear
- true
- end
- end
- ensure
- @within_sudo -= 1
- end
- end
- end
-
end
end
+