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 +