ec2onrails-xtreme-head 0.0.6 → 0.0.7

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,44 @@
1
+ # This rakefile is for building the EC2 on Rails gem.
2
+ # To build a server AMI, see server/rakefile.rb
3
+
4
+ begin
5
+ require 'jeweler'
6
+ require 'rake'
7
+
8
+ Jeweler::Tasks.new do |gemspec|
9
+ gemspec.name = "ec2onrails-xtreme-head"
10
+ gemspec.summary = "Client-side libraries (Capistrano tasks) for managing and deploying to EC2 on Rails servers."
11
+ gemspec.description = <<-DESC
12
+ Deploy a Ruby on Rails app on EC2 in five minutes.
13
+ EC2 on Rails is an Ubuntu Linux server image for Amazon EC2 that's ready to run a standard
14
+ Ruby on Rails application with little or no customization.
15
+ It's a Ruby on Rails virtual appliance.
16
+ This gem contains Capistrano tasks to manage and deploy to an EC2 on Rails server instance.
17
+ DESC
18
+ if gemspec.name == "ec2onrails-xtreme-head"
19
+ experimental_warning = " *** This is the experimental, pre-release version.*** For the regular version install the gem 'ec2onrails-xtreme'"
20
+ gemspec.summary += experimental_warning
21
+ gemspec.description += experimental_warning
22
+ end
23
+ gemspec.homepage = "http://ec2onrails.rubyforge.org"
24
+
25
+ gemspec.authors = ['Xtreme Labs', 'Dwayne Forde']
26
+ gemspec.email = "aws@xtremelabs.com"
27
+
28
+ gemspec.files = FileList['lib/**/**/*', 'Rakefile', 'Version'].to_a
29
+
30
+ gemspec.add_dependency('capistrano', '>= 2.5.19')
31
+ gemspec.add_dependency('archive-tar-minitar', '>= 0.5.2')
32
+ gemspec.add_dependency('optiflag', '>= 0.7')
33
+
34
+ gemspec.rubyforge_project = gemspec.name
35
+ Jeweler::RubyforgeTasks.new do |rubyforge|
36
+ rubyforge.doc_task = "rdoc"
37
+ end
38
+ end
39
+
40
+ Jeweler::GemcutterTasks.new
41
+
42
+ rescue LoadError
43
+ puts "Jeweler not available. Install it with: sudo gem install jeweler"
44
+ end
data/Version ADDED
@@ -0,0 +1 @@
1
+ 0.0.7
@@ -0,0 +1,20 @@
1
+ # This file is part of EC2 on Rails.
2
+ # http://rubyforge.org/projects/ec2onrails/
3
+ #
4
+ # Copyright 2007 Paul Dowman, http://pauldowman.com/
5
+ #
6
+ # EC2 on Rails is free software; you can redistribute it and/or modify
7
+ # it under the terms of the GNU General Public License as published by
8
+ # the Free Software Foundation; either version 2 of the License, or
9
+ # (at your option) any later version.
10
+ #
11
+ # EC2 on Rails is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU General Public License
17
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
18
+
19
+
20
+ $:.unshift File.dirname(__FILE__)
@@ -0,0 +1,50 @@
1
+ module Ec2onrails
2
+ module CapistranoUtils
3
+ def run_local(command)
4
+ result = system command
5
+ raise("error: #{$?}") unless result
6
+ end
7
+
8
+ def run_init_script(script, arg)
9
+ # TODO only restart a service if it's already started.
10
+ # Aside from being smarter and more efficient, This will make sure we
11
+ # aren't starting a service that shouldn't be started for the current
12
+ # roles (e.g. don't start nginx when we're not in the web role)
13
+ # How? Maybe need another param with the process name?
14
+ sudo "/etc/init.d/#{script} #{arg}"
15
+ end
16
+
17
+ # return hostnames for the role named role_sym that has the specified options
18
+ def hostnames_for_role(role_sym, options = {})
19
+ role = roles[role_sym]
20
+ unless role
21
+ return []
22
+ end
23
+ # make sure we match the server with all the passed in options, BUT the server can
24
+ # have additional options defined. e.g.: :primary => true and :ebs_vol_id => 'vol-1234abcd'
25
+ # but we want to select the server where :primary => true
26
+ role.select{|s|
27
+ match = true
28
+ options.each_pair{|k,v| match = false if s.options[k] != v}
29
+ }.collect{|s| s.host}
30
+ end
31
+
32
+ # Like the capture method, but does not print out error stream and swallows
33
+ # an exception if the process's exit code != 0
34
+ # NOTE: this only executes on the first server in the list. Don't use this
35
+ # to execute a task that has a side-effect (i.e. something that needs to be
36
+ # run on all servers).
37
+ def quiet_capture(command, options={})
38
+ output = ""
39
+ invoke_command(command, options.merge(:once => true)) do |ch, stream, data|
40
+ case stream
41
+ when :out then output << data
42
+ # when :err then warn "[err :: #{ch[:server]}] #{data}"
43
+ end
44
+ end
45
+ ensure
46
+ return (output || '').strip
47
+ end
48
+
49
+ end
50
+ end
@@ -0,0 +1,135 @@
1
+ # This file is part of EC2 on Rails.
2
+ # http://rubyforge.org/projects/ec2onrails/
3
+ #
4
+ # Copyright 2007 Paul Dowman, http://pauldowman.com/
5
+ #
6
+ # EC2 on Rails is free software; you can redistribute it and/or modify
7
+ # it under the terms of the GNU General Public License as published by
8
+ # the Free Software Foundation; either version 2 of the License, or
9
+ # (at your option) any later version.
10
+ #
11
+ # EC2 on Rails is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU General Public License
17
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
18
+
19
+ require 'fileutils'
20
+ include FileUtils
21
+ require 'tmpdir'
22
+ require 'pp'
23
+ require 'zlib'
24
+ require 'archive/tar/minitar'
25
+ include Archive::Tar
26
+
27
+ require 'ec2onrails/version_helper'
28
+ require 'ec2onrails/capistrano_utils'
29
+ include Ec2onrails::CapistranoUtils
30
+
31
+
32
+
33
+ Dir[File.join(File.dirname(__FILE__), "recipes/*")].find_all{|x| File.file? x}.each do |recipe|
34
+ require recipe
35
+ end
36
+
37
+
38
+ Capistrano::Configuration.instance.load do
39
+
40
+ unless ec2onrails_config
41
+ raise "ec2onrails_config variable not set. (It should be a hash.)"
42
+ end
43
+
44
+ cfg = ec2onrails_config
45
+
46
+ set :ec2onrails_version, Ec2onrails::VersionHelper.string
47
+ set :deploy_to, "/mnt/app"
48
+ set :use_sudo, true
49
+ set :user, "app"
50
+
51
+ # in case anyone is still using deploy:cold
52
+ before "deploy:cold", "ec2onrails:setup"
53
+
54
+ # after "deploy:symlink", "ec2onrails:server:set_roles", "ec2onrails:server:init_services"
55
+ # after "deploy:symlink", "ec2onrails:server:purge_proxy_cache"
56
+
57
+ on :load do
58
+ before "deploy:symlink", "ec2onrails:server:run_rails_rake_gems_install"
59
+ before "deploy:symlink", "ec2onrails:server:install_system_files"
60
+ end
61
+
62
+
63
+ namespace :ec2onrails do
64
+ desc <<-DESC
65
+ Show the AMI id's of the current images for this version of \
66
+ EC2 on Rails.
67
+ DESC
68
+ task :ami_ids do
69
+ puts "32-bit server image (US location) for EC2 on Rails #{ec2onrails_version}: #{Ec2onrails::VersionHelper.ami_ids["us"]["32bit"]}"
70
+ puts "64-bit server image (US location) for EC2 on Rails #{ec2onrails_version}: #{Ec2onrails::VersionHelper.ami_ids["us"]["64bit"]}"
71
+ puts "32-bit server image (EU location) for EC2 on Rails #{ec2onrails_version}: #{Ec2onrails::VersionHelper.ami_ids["eu"]["32bit"]}"
72
+ puts "64-bit server image (EU location) for EC2 on Rails #{ec2onrails_version}: #{Ec2onrails::VersionHelper.ami_ids["eu"]["64bit"]}"
73
+ end
74
+
75
+ desc <<-DESC
76
+ Copies the public key from the server using the external "ssh"
77
+ command because Net::SSH, which is used by Capistrano, needs it.
78
+ This will only work if you have an ssh command in the path.
79
+ If Capistrano can successfully connect to your EC2 instance you
80
+ don't need to do this. It will copy from one of the servers
81
+ at random, this can be overridden by specifying the HOST
82
+ environment variable
83
+ DESC
84
+ task :get_public_key_from_server do
85
+ host = find_servers_for_task(current_task).first.host
86
+ privkey = ssh_options[:keys][0]
87
+ pubkey = "#{privkey}.pub"
88
+ msg = <<-MSG
89
+ Your first key in ssh_options[:keys] is #{privkey}, presumably that's
90
+ your EC2 private key. The public key will be copied from the server
91
+ named '#{host}' and saved locally as #{pubkey}. Continue? [y/n]
92
+ MSG
93
+ choice = nil
94
+ while choice != "y" && choice != "n"
95
+ choice = Capistrano::CLI.ui.ask(msg).downcase
96
+ msg = "Please enter 'y' or 'n'."
97
+ end
98
+ if choice == "y"
99
+ run_local "scp -i '#{privkey}' app@#{host}:.ssh/authorized_keys #{pubkey}"
100
+ end
101
+ end
102
+
103
+ desc <<-DESC
104
+ Prepare a newly-started instance for a cold deploy.
105
+ DESC
106
+ task :setup do
107
+ # we now have some things being included inside the app so we deploy
108
+ # the app's code to the server before we do any other setup
109
+ server.upload_deploy_keys
110
+ deploy.setup
111
+ deploy.update_code
112
+
113
+ ec2onrails.server.allow_sudo do
114
+ server.set_timezone
115
+ server.set_mail_forward_address
116
+ server.install_packages
117
+ server.install_gems
118
+ server.run_rails_rake_gems_install
119
+ server.deploy_files # DEPRECATED, see install_system_files
120
+ server.install_system_files
121
+ server.set_roles
122
+ server.enable_ssl if cfg[:enable_ssl]
123
+ server.set_rails_env
124
+ server.restart_services
125
+ db.create
126
+ server.harden_server
127
+ db.enable_ebs
128
+ db.set_root_password
129
+ end
130
+ end
131
+
132
+ end
133
+ end
134
+
135
+
@@ -0,0 +1,369 @@
1
+ Capistrano::Configuration.instance.load do
2
+ cfg = ec2onrails_config
3
+
4
+ namespace :ec2onrails do
5
+ desc <<-DESC
6
+ Deploy and restore database from S3
7
+ DESC
8
+ task :restore_db_and_deploy do
9
+ db.recreate
10
+ deploy.update_code
11
+ deploy.symlink
12
+ db.restore
13
+ deploy.migrations
14
+ end
15
+
16
+ namespace :db do
17
+ desc <<-DESC
18
+ [internal] Load configuration info for the database from
19
+ config/database.yml, and start mysql (it must be running
20
+ in order to interact with it).
21
+ DESC
22
+ task :load_config do
23
+ unless hostnames_for_role(:db, :primary => true).empty?
24
+ db_config = YAML::load(ERB.new(File.read("config/database.yml")).result)[rails_env.to_s] || {}
25
+ cfg[:db_name] ||= db_config['database']
26
+ cfg[:db_user] ||= db_config['username'] || db_config['user']
27
+ cfg[:db_password] ||= db_config['password']
28
+ cfg[:db_host] ||= db_config['host']
29
+ cfg[:db_port] ||= db_config['port']
30
+ cfg[:db_socket] ||= db_config['socket']
31
+
32
+ if (cfg[:db_host].nil? || cfg[:db_host].empty?) && (cfg[:db_socket].nil? || cfg[:db_socket].empty?)
33
+ 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'."
34
+ end
35
+
36
+ [cfg[:db_name], cfg[:db_user], cfg[:db_password]].each do |s|
37
+ if s.nil? || s.empty?
38
+ raise "ERROR: missing database config. Make sure database.yml contains a '#{rails_env}' section with a database name, user, and password."
39
+ elsif s.match(/['"]/)
40
+ raise "ERROR: database config string '#{s}' contains quotes."
41
+ end
42
+ end
43
+ end
44
+ end
45
+
46
+ desc <<-DESC
47
+ Create the MySQL database. Assumes there is no MySQL root \
48
+ password. To create a MySQL root password create a task that's run \
49
+ after this task using an after hook.
50
+ DESC
51
+ task :create, :roles => :db do
52
+ on_rollback { drop }
53
+ load_config
54
+ start
55
+ puts " * Pausing to give MySQL some time to start up..."
56
+ sleep 30
57
+
58
+ # TODO run init_backup after creating the db. This might be slow, so we need to
59
+ # check if the db exists, and exit without doing any of this if it exists.
60
+
61
+ run %{mysql -u root -e "drop database if exists test; flush privileges;"}
62
+ # removing anonymous mysql accounts
63
+ run %{mysql -u root -D mysql -e "delete from db where User = ''; flush privileges;"}
64
+ run %{mysql -u root -D mysql -e "delete from user where User = ''; flush privileges;"}
65
+
66
+ # qoting of database names allows special characters eg (the-database-name)
67
+ # the quotes need to be double escaped. Once for capistrano and once for the host shell
68
+ run %{mysql -u root -e "create database if not exists \\`#{cfg[:db_name]}\\`;"}
69
+ run %{mysql -u root -e "grant all on \\`#{cfg[:db_name]}\\`.* to '#{cfg[:db_user]}'@'%' identified by '#{cfg[:db_password]}';"}
70
+ run %{mysql -u root -e "grant reload on *.* to '#{cfg[:db_user]}'@'%' identified by '#{cfg[:db_password]}';"}
71
+ run %{mysql -u root -e "grant super on *.* to '#{cfg[:db_user]}'@'%' identified by '#{cfg[:db_password]}';"}
72
+ end
73
+
74
+ desc <<-DESC
75
+ Move the MySQL database to Amazon's Elastic Block Store (EBS), \
76
+ which is a persistant data store for the cloud.
77
+ OPTIONAL PARAMETERS:
78
+ * SIZE: Pass in a number representing the GB's to hold, like 10. \
79
+ It will default to 10 gigs.
80
+ * VOLUME_ID: The volume_id to use for the mysql database
81
+ NOTE: keep track of the volume ID, as you'll want to keep this for your \
82
+ records and probably add it to the :db role in your deploy.rb file \
83
+ (see the ec2onrails sample deploy.rb file for additional information)
84
+ DESC
85
+ task :enable_ebs, :roles => :db, :only => { :primary => true } do
86
+ # based off of Eric's work:
87
+ # http://developer.amazonwebservices.com/connect/entry.jspa?externalID=1663&categoryID=100
88
+ #
89
+ # EXPLAINATION:
90
+ # There is a lot going on here! At the end, the setup should be:
91
+ # * create EBS volume if run outside of the ec2onrails:setup and
92
+ # VOLUME_ID is not passed in when the cap task is called
93
+ # * EBS volume attached to /dev/sdh
94
+ # * format to xfs if new or do a xfs_check if previously existed
95
+ # * mounted on /var/local and update /etc/fstab
96
+ # * move /mnt/mysql_data -> /var/local/mysql_data
97
+ # * move /mnt/log/mysql -> /var/local/log/mysql
98
+ # * change mysql configs by writing /etc/mysql/conf.d/mysql-ec2-ebs.cnf
99
+ # * keep a copy of the mysql configs with the EBS volume, and if that volume is hooked into
100
+ # another instance, make sure the mysql configs that go with that volume are symlinked to /etc/mysql
101
+ # * update the file locations of the mysql binary logs in /mnt/log/mysql/mysql-bin.index
102
+ # * symlink the moved folders to their old position... makes the move to EBS transparent
103
+ # * Amazon doesn't contain EBS information in the meta-data API (yet). So write
104
+ # /etc/ec2onrails/ebs_info.yml
105
+ # to contain the meta-data information that we need
106
+ #
107
+ # DESIGN CONSIDERATIONS
108
+ # * only moving mysql data to EBS. seems the most obvious, and if we move over other components
109
+ # we will have to share that bandwidth (1 Gbps pipe to SAN). So limiting to what we really need
110
+ # * not moving all mysql logic over (tmp scratch space stays local). Again, this is to limit
111
+ # unnecessary bandwidth usage, PLUS, we are charged per million IO to EBS
112
+ #
113
+ # TODO:
114
+ # * make sure if we have a predefined ebs_vol_id, that we error out with a nice msg IF the zones do not match
115
+ # * can we move more of the mysql cache files back to the local disk and off of EBS, like the innodb table caches?
116
+ # * right now we force this task to only be run on one server; that works for db :primary => true
117
+ # But what is the best way to make this work if it needs to setup multiple servers (like db slaves)?
118
+ # I need to figure out how to do a direct mapping from a server definition to a ebs_vol_id
119
+ # * when we enable slaves and we setup ebs volumes on them, make it transparent to the user.
120
+ # have the slave create a snapshot of the db.master volume, and then use that to mount from
121
+ # * need to do a rollback that if the volume is created but something fails, lets uncreate it?
122
+ # carefull though! If it fails towards the end when information is copied over, it could cause information
123
+ # to be lost!
124
+ #
125
+
126
+ mysql_dir_root = '/var/local'
127
+ block_mnt = '/dev/sdh'
128
+ servers = find_servers_for_task(current_task)
129
+
130
+ if servers.empty?
131
+ raise Capistrano::NoMatchingServersError, "`#{task.fully_qualified_name}' is only run for servers matching #{task.options.inspect}, but no servers matched"
132
+ elsif servers.size > 1
133
+ raise Capistrano::Error, "`#{task.fully_qualified_name}' is can only be run on one server, not #{server.size}"
134
+ end
135
+
136
+ vol_id = ENV['VOLUME_ID'] || servers.first.options[:ebs_vol_id]
137
+
138
+ #HACK! capistrano doesn't allow arguments to be passed in if we call this task as a method, like 'db.enable_ebs'
139
+ # the places where we do call it like that, we don't want to force a move to ebs, so....
140
+ # if the call frame is > 1 (ie, another task called it), do NOT force the ebs move
141
+ no_force = task_call_frames.size > 1
142
+ prev_created = !(vol_id.nil? || vol_id.empty?)
143
+ #no vol_id was passed in, but perhaps it is already mounted...?
144
+ prev_created = true if !quiet_capture("mount | grep -inr '#{mysql_dir_root}' || echo ''").empty?
145
+
146
+ unless no_force && (vol_id.nil? || vol_id.empty?)
147
+ zone = quiet_capture("/usr/local/ec2onrails/bin/ec2_meta_data -key 'placement/availability-zone'")
148
+ instance_id = quiet_capture("/usr/local/ec2onrails/bin/ec2_meta_data -key 'instance-id'")
149
+
150
+ unless prev_created
151
+ puts "creating new ebs volume...."
152
+ size = ENV["SIZE"] || "10"
153
+ cmd = "ec2-create-volume -s #{size} -z #{zone} 2>&1"
154
+ puts "running: #{cmd}"
155
+ output = `#{cmd}`
156
+ puts output
157
+ vol_id = (output =~ /^VOLUME\t(.+?)\t/ && $1)
158
+ puts "NOTE: remember that vol_id"
159
+ sleep(2)
160
+ end
161
+ vol_id.strip! if vol_id
162
+ if quiet_capture("mount | grep -inr '#{block_mnt}' || echo ''").empty?
163
+ cmd = "ec2-attach-volume -d #{block_mnt} -i #{instance_id} #{vol_id} 2>&1"
164
+ puts "running: #{cmd}"
165
+ output = `#{cmd}`
166
+ puts output
167
+ if output =~ /Client.InvalidVolume.ZoneMismatch/i
168
+ raise Exception, "The volume you are trying to attach does not reside in the zone of your instance. Stopping!"
169
+ end
170
+ while !system( "ec2-describe-volumes | grep #{vol_id} | grep attached" )
171
+ puts "Waiting for #{vol_id} to be attached..."
172
+ sleep 1
173
+ end
174
+ end
175
+
176
+ ec2onrails.server.allow_sudo do
177
+ # try to format the volume... if it is already formatted, lets run a check on
178
+ # it to make sure it is ok, and then continue on
179
+ # if errors, the device is busy...something else is going on here and it is already mounted... skip!
180
+ if prev_created
181
+ # Stop the db (mysql server) for cases where this is being run after the original run
182
+ # If EBS partiion is already mounted and being used by mysql, it will fail when umount is run
183
+ god_status = quiet_capture("sudo god status")
184
+ god_status = god_status.empty? ? {} : YAML::load(god_status)
185
+ start_stop_db = false
186
+ start_stop_db = god_status['db_primary']['mysql'] == 'up'
187
+ if start_stop_db
188
+ stop
189
+ puts "Waiting for mysql to stop"
190
+ sleep(10)
191
+ end
192
+ quiet_capture("sudo umount #{mysql_dir_root}") #unmount if need to
193
+ puts "Checking if the filesystem needs to be created (if you created #{vol_id} yourself)"
194
+ existing = quiet_capture( "mkfs.xfs /dev/sdh", :via => 'sudo' ).match( /existing filesystem/ )
195
+ sudo "xfs_check #{block_mnt}"
196
+ # Restart the db if it
197
+ start if start_stop_db && existing
198
+ else
199
+ sudo "mkfs.xfs #{block_mnt}"
200
+ end
201
+
202
+ # if not added to /etc/fstab, lets do so
203
+ sudo "sh -c \"grep -iqn '#{mysql_dir_root}' /etc/fstab || echo '#{block_mnt} #{mysql_dir_root} xfs noatime 0 0' >> /etc/fstab\""
204
+ sudo "mkdir -p #{mysql_dir_root}"
205
+ #if not already mounted, lets mount it
206
+ sudo "sh -c \"mount | grep -iqn '#{mysql_dir_root}' || mount '#{mysql_dir_root}'\""
207
+
208
+ #ok, now lets move the mysql stuff off of /mnt -> mysql_dir_root
209
+ stop rescue nil #already stopped
210
+ sudo "mkdir -p #{mysql_dir_root}/log"
211
+ #move the data over, but keep a symlink to the new location for backwards compatibility
212
+ #and do not do it if /mnt/mysql_data has already been moved
213
+ quiet_capture("sudo sh -c 'test ! -d #{mysql_dir_root}/mysql_data && mv /mnt/mysql_data #{mysql_dir_root}/'")
214
+ sudo "mv /mnt/mysql_data /mnt/mysql_data_old 2>/dev/null || echo"
215
+ sudo "ln -fs #{mysql_dir_root}/mysql_data /mnt/mysql_data"
216
+
217
+ #but keep the tmpdir on mnt
218
+ sudo "sh -c 'mkdir -p /mnt/tmp/mysql && chown mysql:mysql /mnt/tmp/mysql'"
219
+ #move the logs over, but keep a symlink to the new location for backwards compatibility
220
+ #and do not do it if the logs have already been moved
221
+ quiet_capture("sudo sh -c 'test ! -d #{mysql_dir_root}/log/mysql_data && mv /mnt/log/mysql #{mysql_dir_root}/log/'")
222
+ sudo "ln -fs #{mysql_dir_root}/log/mysql /mnt/log/mysql"
223
+ quiet_capture("sudo sh -c \"test -f #{mysql_dir_root}/log/mysql/mysql-bin.index && \
224
+ perl -pi -e 's%/mnt/log/%#{mysql_dir_root}/log/%' #{mysql_dir_root}/log/mysql/mysql-bin.index\"") rescue false
225
+
226
+ if quiet_capture("test -d /var/local/etc/mysql && echo 'yes'").empty?
227
+ txt = <<-FILE
228
+ [mysqld]
229
+ datadir = #{mysql_dir_root}/mysql_data
230
+ tmpdir = /mnt/tmp/mysql
231
+ log_bin = #{mysql_dir_root}/log/mysql/mysql-bin.log
232
+ log_slow_queries = #{mysql_dir_root}/log/mysql/mysql-slow.log
233
+ FILE
234
+ put txt, '/tmp/mysql-ec2-ebs.cnf'
235
+ sudo 'mv /tmp/mysql-ec2-ebs.cnf /etc/mysql/conf.d/mysql-ec2-ebs.cnf'
236
+
237
+ #keep a copy
238
+ sudo "rsync -aR /etc/mysql #{mysql_dir_root}/"
239
+ end
240
+ # lets use the mysql configs on the EBS volume
241
+ sudo "mv /etc/mysql /etc/mysql.orig 2>/dev/null"
242
+ sudo "ln -sf #{mysql_dir_root}/etc/mysql /etc/mysql"
243
+
244
+ #just put a README on the drive so we know what this volume is for!
245
+ txt = <<-FILE
246
+ This volume is setup to be used by Ec2onRails in conjunction with Amazon's EBS, for primary MySql database persistence.
247
+ RAILS_ENV: #{fetch(:rails_env, 'undefined')}
248
+ DOMAIN: #{fetch(:domain, 'undefined')}
249
+
250
+ Modify this volume at your own risk
251
+ FILE
252
+
253
+ put txt, "/tmp/VOLUME-README"
254
+ sudo "mv /tmp/VOLUME-README #{mysql_dir_root}/VOLUME-README"
255
+ sudo "touch /etc/ec2onrails/ebs_info.yml"
256
+ ebs_info = quiet_capture("cat /etc/ec2onrails/ebs_info.yml")
257
+
258
+ ebs_info = ebs_info.empty? ? {} : YAML::load(ebs_info)
259
+ ebs_info[mysql_dir_root] = {'block_loc' => block_mnt, 'volume_id' => vol_id}
260
+ put(ebs_info.to_yaml, "/tmp/ebs_info.yml")
261
+ sudo "mv /tmp/ebs_info.yml /etc/ec2onrails/ebs_info.yml"
262
+ #lets start it back up
263
+ start
264
+ end #end of sudo
265
+ end
266
+ end
267
+
268
+
269
+ desc <<-DESC
270
+ [internal] Make sure the MySQL server has been started, just in case the db role
271
+ hasn't been set, e.g. when called from ec2onrails:setup.
272
+ (But don't enable monitoring on it.)
273
+ DESC
274
+ task :start, :roles => :db do
275
+ sudo "god start db_primary"
276
+ end
277
+
278
+ task :stop, :roles => :db do
279
+ sudo "god stop db_primary"
280
+ end
281
+
282
+
283
+ desc <<-DESC
284
+ Drop the MySQL database. Assumes there is no MySQL root \
285
+ password. If there is a MySQL root password, create a task that removes \
286
+ it and run that task before this one using a before hook.
287
+ DESC
288
+ task :drop, :roles => :db do
289
+ load_config
290
+ run %{mysql -u root -e "drop database if exists \\`#{cfg[:db_name]}\\`;"}
291
+ end
292
+
293
+ desc <<-DESC
294
+ db:drop and db:create.
295
+ DESC
296
+ task :recreate, :roles => :db do
297
+ drop
298
+ create
299
+ end
300
+
301
+ desc <<-DESC
302
+ Set a root password for MySQL, using the variable mysql_root_password \
303
+ if it is set. If this is done db:drop won't work.
304
+ DESC
305
+ task :set_root_password, :roles => :db do
306
+ if cfg[:mysql_root_password]
307
+ begin
308
+ run %{mysql -u root -e "UPDATE mysql.user SET Password=PASSWORD('#{cfg[:mysql_root_password]}') WHERE User='root'; FLUSH PRIVILEGES;"}
309
+ rescue Exception => e
310
+ #most likely because the password was already set
311
+ #in that case this is fine to swallow the error because the task is 'set' db password, not reset it.... we would have to know
312
+ #what the old root password was
313
+ end
314
+ end
315
+ end
316
+
317
+ desc <<-DESC
318
+ Dump the MySQL database to ebs (if enabled) or the S3 bucket specified by \
319
+ ec2onrails_config[:archive_to_bucket]. The filename will be \
320
+ "database-archive/<timestamp>/dump.sql.gz".
321
+ DESC
322
+ task :archive, :roles => :db do
323
+ run "/usr/local/ec2onrails/bin/backup_app_db --bucket #{cfg[:archive_to_bucket]} --dir #{cfg[:archive_to_bucket_subdir]}"
324
+ end
325
+
326
+ desc <<-DESC
327
+ Restore the MySQL database from the S3 bucket specified by \
328
+ ec2onrails_config[:restore_from_bucket]. The archive filename is \
329
+ expected to be the default, "mysqldump.sql.gz".
330
+ DESC
331
+ task :restore, :roles => :db do
332
+ run "/usr/local/ec2onrails/bin/restore_app_db --bucket #{cfg[:restore_from_bucket]} --dir #{cfg[:restore_from_bucket_subdir]}"
333
+ end
334
+
335
+ desc <<-DESC
336
+ [internal] Initialize the default backup folder on S3 (i.e. do a full
337
+ backup of the newly-created db so the automatic incremental backups
338
+ make sense). NOTE: Only of use if you do not have ebs enabled
339
+ DESC
340
+ task :init_backup, :roles => :db do
341
+ server.allow_sudo do
342
+ sudo "/usr/local/ec2onrails/bin/backup_app_db --reset"
343
+ end
344
+ end
345
+
346
+ # do NOT run if the flag does not exist. This is placed by a startup script
347
+ # and it is only run on the first-startup. This means after the db has been
348
+ # optimized, this task will not work again.
349
+ #
350
+ # Of course you can overload it or call the file directly
351
+ task :optimize, :roles => :db do
352
+ if !quiet_capture("test -e /tmp/optimize_db_flag && echo 'file exists'").empty?
353
+ ec2onrails.server.allow_sudo do
354
+ begin
355
+ sudo "/usr/local/ec2onrails/bin/optimize_mysql"
356
+ ensure
357
+ sudo "rm -rf /tmp/optimize_db_flag" #remove so we cannot run again
358
+ end
359
+ end
360
+ else
361
+ puts "skipping as it looks like this task has already been run"
362
+ end
363
+ end
364
+
365
+ end
366
+
367
+ end
368
+ end
369
+
@@ -0,0 +1,58 @@
1
+ Capistrano::Configuration.instance(:must_exist).load do
2
+
3
+ # Override default start/stop/restart tasks for Passenger
4
+ namespace :deploy do
5
+ desc <<-DESC
6
+ Overrides the default Capistrano deploy:start.
7
+ DESC
8
+ task :start, :roles => :web do
9
+ run "touch #{current_release}/tmp/restart.txt"
10
+ end
11
+
12
+ desc <<-DESC
13
+ Overrides the default Capistrano deploy:stop.
14
+ DESC
15
+ task :stop, :roles => :web do
16
+ # Do nothing,
17
+ end
18
+
19
+ desc <<-DESC
20
+ Overrides the default Capistrano deploy:restart.
21
+ DESC
22
+ task :restart, :roles => :web do
23
+ run "touch #{current_release}/tmp/restart.txt"
24
+ end
25
+
26
+ desc <<-DESC
27
+ Run the migrate rake task. By default, it runs this in most recently \
28
+ deployed version of the app. However, you can specify a different release \
29
+ via the migrate_target variable, which must be one of :latest (for the \
30
+ default behavior), or :current (for the release indicated by the `current' \
31
+ symlink). latest release to be deployed with the update_code task). Strings will work \
32
+ for those values instead of symbols, too. You can also specify additional \
33
+ environment variables to pass to rake via the migrate_env variable. Finally, \
34
+ you can specify the full path to the rake executable by setting the rake \
35
+ variable. The defaults are:
36
+
37
+ set :rake, \"rake\"
38
+ set :rails_env, \"production\"
39
+ set :migrate_env, \"\"
40
+ set :migrate_target, :latest"
41
+ DESC
42
+ task :migrate, :roles => :db, :only => { :primary => true } do
43
+ rake = fetch(:rake, "rake")
44
+ rails_env = fetch(:rails_env, "production")
45
+ migrate_env = fetch(:migrate_env, "")
46
+ migrate_target = fetch(:migrate_target, :latest)
47
+
48
+ directory = case migrate_target.to_sym
49
+ when :current then current_path
50
+ when :latest then current_release
51
+ else raise ArgumentError, "unknown migration target #{migrate_target.inspect}"
52
+ end
53
+
54
+ run "cd #{directory}; rvmsudo bundle exec #{rake} RAILS_ENV=#{rails_env} #{migrate_env} db:migrate"
55
+ end
56
+
57
+ end
58
+ end
@@ -0,0 +1,505 @@
1
+ Capistrano::Configuration.instance(:must_exist).load do
2
+ cfg = ec2onrails_config
3
+
4
+ namespace :ec2onrails do
5
+ namespace :server do
6
+ desc <<-DESC
7
+ Tell the servers what roles they are in. This configures them with \
8
+ the appropriate settings for each role, and starts and/or stops the \
9
+ relevant services.
10
+ DESC
11
+ task :set_roles do
12
+ # Create a list of server roles based on the capistrano roles.
13
+ # We treat :db specially because it maps to a different rolename on the server
14
+ # if :primary => true
15
+ server_roles = {}
16
+ roles.keys.delete_if{|r|r == :db}.each do |rolename|
17
+ server_roles[rolename] = hostnames_for_role(rolename)
18
+ end
19
+ server_roles[:db_primary] = hostnames_for_role(:db, :primary => true)
20
+
21
+ roles_yml = YAML::dump(server_roles)
22
+ put roles_yml, "/tmp/roles.yml"
23
+ allow_sudo do
24
+ sudo "cp /tmp/roles.yml /etc/ec2onrails"
25
+ #we want everyone to be able to read to it
26
+ sudo "chmod a+r /etc/ec2onrails/roles.yml"
27
+ sudo "/usr/local/ec2onrails/bin/set_roles"
28
+ end
29
+ end
30
+
31
+ task :init_services do
32
+ allow_sudo do
33
+ #lets pick up the new configuration files
34
+ sudo "/usr/local/ec2onrails/bin/init_services"
35
+ end
36
+ end
37
+
38
+ desc <<-DESC
39
+ Change the default value of RAILS_ENV on the server.
40
+ The value is specified in :rails_env.
41
+ Be sure to do deploy:restart after this.
42
+ DESC
43
+ task :set_rails_env do
44
+ allow_sudo do
45
+ rails_env = fetch(:rails_env, "production")
46
+ sudo "/usr/local/ec2onrails/bin/set_rails_env #{rails_env}"
47
+ end
48
+ end
49
+
50
+ desc <<-DESC
51
+ Upgrade to the newest versions of all Ubuntu packages.
52
+ DESC
53
+ task :upgrade_packages do
54
+ allow_sudo do
55
+ sudo "aptitude -q update"
56
+ sudo "sh -c 'export DEBIAN_FRONTEND=noninteractive; aptitude -q -y safe-upgrade'"
57
+ end
58
+ end
59
+
60
+ desc <<-DESC
61
+ Upgrade to the newest versions of all rubygems.
62
+ DESC
63
+ task :upgrade_gems do
64
+ allow_sudo do
65
+ sudo "gem update --system --no-rdoc --no-ri"
66
+ sudo "gem update --no-rdoc --no-ri"
67
+ end
68
+ end
69
+
70
+ desc <<-DESC
71
+ Install extra Ubuntu packages. Set ec2onrails_config[:packages], it \
72
+ should be an array of strings.
73
+ NOTE: the package installation will be non-interactive, if the packages \
74
+ require configuration either set ec2onrails_config[:interactive_packages] \
75
+ like you would for ec2onrails_config[:packages] (we'll flood the server \
76
+ with 'Y' inputs), or log in as 'root' and run \
77
+ 'dpkg-reconfigure packagename' or replace the package's config files \
78
+ using the 'ec2onrails:server:deploy_files' task.
79
+ DESC
80
+ task :install_packages do
81
+ allow_sudo do
82
+ sudo "aptitude -q update"
83
+ if cfg[:packages] && cfg[:packages].any?
84
+ sudo "sh -c 'export DEBIAN_FRONTEND=noninteractive; aptitude -q -y install #{cfg[:packages].join(' ')}'"
85
+ end
86
+ if cfg[:interactive_packages] && cfg[:interactive_packages].any?
87
+ # sudo "aptitude install #{cfg[:interactive_packages].join(' ')}", {:env => {'DEBIAN_FRONTEND' => 'readline'} }
88
+ #trying to pick WHEN to send a Y is a bit tricky...it totally depends on the
89
+ #interactive package you want to install. FLOODING it with 'Y'... but not sure how
90
+ #'correct' or robust this is
91
+ cmd = "sudo sh -c 'export DEBIAN_FRONTEND=readline; aptitude -y -q install #{cfg[:interactive_packages].join(' ')}'"
92
+ run(cmd) do |channel, stream, data|
93
+ channel.send_data "Y\n"
94
+ end
95
+ end
96
+ end
97
+ end
98
+
99
+ task :configure_firewall do
100
+ # TODO
101
+ end
102
+
103
+
104
+ desc <<-DESC
105
+ Provide extra security measures. Set ec2onrails_config[:harden_server] = true \
106
+ to allow the hardening of the server.
107
+ These security measures are those which can make initial setup and playing around
108
+ with Ec2onRails tricky. For example, you can be logged out of your server forever
109
+ DESC
110
+ task :harden_server do
111
+ #NOTES: for those security features that will get in the way of ease-of-use
112
+ # hook them in here
113
+ # Like encrypting the mnt directory
114
+ # http://groups.google.com/group/ec2ubuntu/web/encrypting-mnt-using-cryptsetup-on-ubuntu-7-10-gutsy-on-amazon-ec2
115
+ if cfg[:harden_server]
116
+ #lets install some extra packages:
117
+ # denyhosts: sshd security tool. config file is already installed...
118
+ #
119
+ security_pkgs = %w{denyhosts}
120
+ allow_sudo do
121
+ sudo "sh -c 'export DEBIAN_FRONTEND=noninteractive; aptitude -q -y install #{security_pkgs.join(' ')}'"
122
+ #lets setup dkim
123
+ setup_email_signing
124
+ end
125
+ end
126
+ end
127
+
128
+ #based on the recipe here (but which is missing a few key steps!)
129
+ #http://www.howtoforge.com/quick-and-easy-setup-for-domainkeys-using-ubuntu-postfix-and-dkim-filter
130
+ desc <<-DESC
131
+ enables dkim signing of outgoing msgs. This helps with fightint spam.
132
+ You'll have to update your dns records to take advantage of this, but we'll
133
+ help you out with that
134
+ NOTE: set ec2onrails_config[:service_domain] = 'yourdomain.com' before running this task
135
+ DESC
136
+ task :setup_email_signing do
137
+ if cfg[:service_domain].nil? || cfg[:service_domain].empty?
138
+ raise "ERROR: missing the :service_domain key. Please set that in your deploy script if you would like to use this task."
139
+ end
140
+
141
+ domain = cfg[:service_domain]
142
+ postmaster_email = "postmaster@#{domain}"
143
+
144
+ #make the selector something that will help us roll over and expire the old key next year
145
+ selector = "mail#{Time.now.year.to_s[-2..-1]}" #ie, mail09
146
+
147
+ allow_sudo do
148
+ sudo "sh -c 'export DEBIAN_FRONTEND=noninteractive; aptitude -q -y install postfix dkim-filter'"
149
+ #do NOT change the size of the key; making it longer can cause problems with some of the dkim implementations
150
+
151
+ keys_exist = File.exist?("config/mail/dkim/dkim_#{selector}.private.key") && File.exist?("config/mail/dkim/dkim_#{selector}.public.key")
152
+
153
+ unless keys_exist
154
+ #lets make them!
155
+ cmds = <<-CMDS
156
+ mkdir -p config/mail/dkim;
157
+ cd config/mail/dkim;
158
+ openssl genrsa -out dkim_#{selector}.private.key 1024;
159
+ openssl rsa -in dkim_#{selector}.private.key -out dkim_#{selector}.public.key -pubout -outform PEM
160
+ CMDS
161
+ system cmds
162
+ end
163
+
164
+ pub_key = File.read("config/mail/dkim/dkim_#{selector}.public.key")
165
+ pub_key = pub_key.split("\n")[1..-2].join('')
166
+
167
+ #lets get the private and public keys up to the server
168
+ put File.read("config/mail/dkim/dkim_#{selector}.private.key"), "/tmp/dkim_#{selector}.private.key"
169
+ put File.read("config/mail/dkim/dkim_#{selector}.public.key"), "/tmp/dkim_#{selector}.public.key"
170
+ sudo "mkdir -p /var/dkim-filter"
171
+ sudo "mv /tmp/dkim_#{selector}.p*.key /var/dkim-filter/."
172
+
173
+ #saw a note that Canonicalization relaxed was helpful for rails applications...
174
+ #haven't tested that yet
175
+ dkim_filter_conf = <<-SCRIPT
176
+ # Log to syslog
177
+ Syslog yes
178
+
179
+ # Sign for example.com with key in /etc/mail/dkim.key using
180
+ Domain #{domain}
181
+ KeyFile /var/dkim-filter/dkim_#{selector}.private.key
182
+ Selector #{selector}
183
+
184
+ # Common settings. See dkim-filter.conf(5) for more information.
185
+ AutoRestart no
186
+ Background yes
187
+ SubDomains no
188
+ Canonicalization relaxed
189
+ SCRIPT
190
+
191
+ put dkim_filter_conf, "/tmp/dkim-filter.conf.tmp"
192
+ sudo "mv /etc/dkim-filter.conf /etc/dkim-filter.conf.orig"
193
+ sudo "mv /tmp/dkim-filter.conf.tmp /etc/dkim-filter.conf"
194
+ cmds = <<-CMDS
195
+ sudo postconf -e 'myhostname = #{domain}';
196
+ sudo postconf -e 'mydomain = #{domain}';
197
+ sudo postconf -e 'myorigin = $mydomain';
198
+ sudo postconf -e 'mynetworks_style=subnet';
199
+ sudo postconf -e 'biff = no';
200
+ sudo postconf -e 'alias_maps = hash:/etc/aliases';
201
+ sudo postconf -e 'alias_database = hash:/etc/aliases';
202
+ sudo postconf -e 'mydestination = localdomain, localhost, localhost.localdomain, localhost';
203
+ sudo postconf -e 'relay_domains=$mydestination';
204
+ sudo postconf -e 'mynetworks = 127.0.0.0/8';
205
+ sudo postconf -e 'smtpd_milters = inet:localhost:8891';
206
+ sudo postconf -e 'non_smtpd_milters = inet:localhost:8891';
207
+ sudo postconf -e 'milter_protocol = 2';
208
+ sudo postconf -e 'milter_default_action = accept'
209
+ CMDS
210
+ sudo cmds
211
+
212
+ #lets lock it down
213
+ sudo "chown -R dkim-filter:dkim-filter /var/dkim-filter"
214
+ sudo "chmod 600 /var/dkim-filter/*"
215
+
216
+ puts "*" * 80
217
+ puts "NOTE: you need to do a few things"
218
+ puts " * created public and private DKIM keys to config/mail/dkim_#{selector}.*.key" unless keys_exist
219
+ puts "\n"
220
+ msg = <<-MSG
221
+ * Enter these *TWO* records into your DNS record:
222
+ #{selector}._domainkey.#{domain} IN TXT 'k=rsa; t=y; p=#{pub_key}'
223
+ _domainkey.#{domain} IN TXT 't=y; o=~; r=#{postmaster_email}'
224
+
225
+ I would recommend signing into your ec2 instance and running some test emails. Gmail is very fast in updating their records, but yahoo (as of this writing) is slow and inconsistent. But you can run a command like this to various email address to see how it works:
226
+
227
+ echo 'something searchable so you can find it in your spam filter! did dkim work?' | mail -s "my dkim email; lets see how it went" adam@someservice.com
228
+
229
+
230
+ NOTE: in the near future, when things are looking good, if you take away the 't=y; ' from the above two records, it tells the email services that you are no longer testing the service and to treat your signings with tough love.
231
+
232
+
233
+ MSG
234
+ puts msg
235
+
236
+ #sometimes the dkim-filter restart fails; it seems to be a race condition with some of the postfix changes going in...
237
+ #but a sleep here seems to do the trick.
238
+ sleep(10)
239
+ output = quiet_capture "sudo /etc/init.d/dkim-filter restart"
240
+ if output =~ /smfi_opensocket\(\) failed/
241
+ #ah, if we didn't sleep enough above, lets try it one more time; but this time it will fail if we still get
242
+ #the smfi_opensocket error
243
+ sleep(5)
244
+ sudo "/etc/init.d/dkim-filter restart 2>&1"
245
+ end
246
+ sleep(2)
247
+ sudo "/etc/init.d/postfix restart 2>&1"
248
+ end
249
+
250
+ end
251
+
252
+
253
+ desc <<-DESC
254
+ Install extra rubygems. Set ec2onrails_config[:rubygems], it should \
255
+ be with an array of strings.
256
+ DESC
257
+ task :install_gems do
258
+ if cfg[:rubygems]
259
+ allow_sudo do
260
+ cfg[:rubygems].each do |g|
261
+ sudo "gem install #{g} --no-rdoc --no-ri"
262
+ end
263
+ end
264
+ end
265
+ end
266
+
267
+ task :run_rails_rake_gems_install do
268
+ #if running under Rails 2.1, lets trigger 'rake gems:install', but in such a way
269
+ #so it fails gracefully if running rails < 2.1
270
+ # ALSO, this might be the first time rake is run, and running it as sudo means that
271
+ # if any plugins are loaded and create directories... like what image_science does for
272
+ # ruby_inline, then the dirs will be created as root. so trigger the rails loading
273
+ # very quickly before the sudo is called
274
+ # run "cd #{release_path} && rake RAILS_ENV=#{rails_env} -T 1>/dev/null && sudo rake RAILS_ENV=#{rails_env} gems:install"
275
+ allow_sudo do
276
+ output = run "cd #{release_path} && rake RAILS_ENV=#{rails_env} db:version > /dev/null 2>&1 || rvmsudo bundle install --path vendor/bundle"
277
+ puts output
278
+ end
279
+ end
280
+
281
+ desc <<-DESC
282
+ A convenience task to upgrade existing packages and gems and install \
283
+ specified new ones.
284
+ DESC
285
+ task :upgrade_and_install_all do
286
+ upgrade_packages
287
+ upgrade_gems
288
+ install_packages
289
+ install_gems
290
+ end
291
+
292
+ desc <<-DESC
293
+ Set the timezone using the value of the variable named timezone. \
294
+ Valid options for timezone can be determined by the contents of \
295
+ /usr/share/zoneinfo, which can be seen here: \
296
+ http://packages.ubuntu.com/cgi-bin/search_contents.pl?searchmode=filelist&word=tzdata&version=gutsy&arch=all&page=1&number=all \
297
+ Remove 'usr/share/zoneinfo/' from the filename, and use the last \
298
+ directory and file as the value. For example 'Africa/Abidjan' or \
299
+ 'posix/GMT' or 'Canada/Eastern'.
300
+ DESC
301
+ task :set_timezone do
302
+ if cfg[:timezone]
303
+ allow_sudo do
304
+ sudo "bash -c 'echo #{cfg[:timezone]} > /etc/timezone'"
305
+ sudo "cp /usr/share/zoneinfo/#{cfg[:timezone]} /etc/localtime"
306
+ end
307
+ end
308
+ end
309
+
310
+ desc <<-DESC
311
+ DEPRECATED. See install_system_files.
312
+ DESC
313
+ task :deploy_files do
314
+ if cfg[:server_config_files_root]
315
+ puts "***** DEPRECATION WARNING: you're using the deploy_files task which has been deprecated" # TODO pointer to documentation
316
+ begin
317
+ filename = "config_files.tar"
318
+ local_file = "#{Dir.tmpdir}/#{filename}"
319
+ remote_file = "/tmp/#{filename}"
320
+ FileUtils.cd(cfg[:server_config_files_root]) do
321
+ File.open(local_file, 'wb') { |tar| Minitar.pack(".", tar) }
322
+ end
323
+ put File.read(local_file), remote_file
324
+ allow_sudo do
325
+ sudo "tar xvf #{remote_file} -o -C /"
326
+ end
327
+ ensure
328
+ rm_rf local_file
329
+ run "rm -f #{remote_file}"
330
+ end
331
+ end
332
+ end
333
+
334
+ desc <<-DESC
335
+ Installs files into the system anywhere outside of the Rails app.
336
+ The directory RAILS_ROOT/config/ec2onrails/system_files can contain
337
+ files that will be installed into the server relative to "/", and it
338
+ can contain a manifest file with metadata to change the file owner
339
+ and permissions, and it allows the files to be cleanly uninstalled
340
+ from the system.
341
+ TODO pointer to full documentation
342
+ DESC
343
+ task :install_system_files do
344
+ allow_sudo do
345
+ sudo "/usr/local/ec2onrails/bin/install_system_files #{release_path}"
346
+ end
347
+ end
348
+
349
+ desc <<-DESC
350
+ Restart a set of services. Set ec2onrails_config[:services_to_restart]
351
+ to an array of strings. It's assumed that each service has a script
352
+ in /etc/init.d
353
+ DESC
354
+ task :restart_services do
355
+ if cfg[:services_to_restart] && cfg[:services_to_restart].any?
356
+ allow_sudo do
357
+ cfg[:services_to_restart].each do |service|
358
+ run_init_script(service, "restart")
359
+ end
360
+ end
361
+ end
362
+ end
363
+
364
+ desc <<-DESC
365
+ Set the email address that mail to the app user forwards to.
366
+ DESC
367
+ task :set_mail_forward_address do
368
+ if cfg[:mail_forward_address]
369
+ allow_sudo do
370
+ sudo "sh -c 'echo #{cfg[:mail_forward_address]} > /root/.forward'"
371
+ end
372
+ end
373
+ end
374
+
375
+ desc <<-DESC
376
+ Enable ssl for the web server. You'll want to replace the default SSL
377
+ certificate and key files, the certificate file is at
378
+ /etc/ec2onrails/ssl/cert/ec2onrails-default.crt
379
+ and a the key file is at
380
+ /etc/ec2onrails/ssl/private/ec2onrails-default.key
381
+ (use the deploy_files task).
382
+ The key file should NOT have a passphrase.
383
+ DESC
384
+ task :enable_ssl, :roles => :web do
385
+ # TODO: enable for nginx
386
+ # run_init_script("nginx", "restart")
387
+ end
388
+
389
+ desc <<-DESC
390
+ Upload the app user's SSH deploy keys from
391
+ config/ec2onrails/deploy_keys to /home/app/.ssh
392
+ The deploy_keys dir should contain the SSH config files that
393
+ are needed to deploy your app's source code from your SCM repository
394
+ (if you're deploying from an SCM repo). This means an SSH private key
395
+ (named id_dsa), the public key (named id_dsa.pub) and possibly a
396
+ known_hosts file.
397
+ They can't deployed via install_system_files because the
398
+ app user's SSH credentials might be needed to deploy the
399
+ app itself if it's coming directly from an SCM repository.
400
+ DESC
401
+ task :upload_deploy_keys do
402
+ deploy_keys_dir = "config/ec2onrails/deploy_keys"
403
+ remote_dir = "/home/app/.ssh"
404
+
405
+ if File.exist? deploy_keys_dir
406
+ run "mkdir -p #{remote_dir}"
407
+ Dir.chdir deploy_keys_dir do
408
+ Dir.glob("*").each do |f|
409
+ remote_file = "#{remote_dir}/#{f}"
410
+ put File.read(f), "#{remote_file}"
411
+ end
412
+ end
413
+ run "chmod -R go-rwx /home/app/.ssh"
414
+ end
415
+ end
416
+
417
+ desc <<-DESC
418
+ Clear the varnish proxy cache (if caching is enabled, which it isn't
419
+ by default). Purges all documents from the cache.
420
+ DESC
421
+ task :purge_proxy_cache, :roles => :proxy do
422
+ run "varnishadm -T localhost:6082 'url.purge .*'"
423
+ end
424
+
425
+ desc <<-DESC
426
+ Restrict the app user's sudo access.
427
+ Defaults the user to only be able to \
428
+ sudo to god
429
+ DESC
430
+ task :restrict_sudo_access do
431
+ old_user = fetch(:user)
432
+ is_rootequiv = capture("groups").split.include?("admin") # check groups before changing user
433
+ begin
434
+ set :user, 'ubuntu'
435
+ sessions.clear #clear out sessions cache..... this way the ssh connections are reinitialized
436
+
437
+ # Remove the app user from the "rootequiv" group, this removes full sudo ability
438
+ if is_rootequiv
439
+ sudo "deluser app admin"
440
+ else
441
+ puts "User 'app' is not a member of group 'rootequiv' (old_user = #{old_user})."
442
+ end
443
+ ensure
444
+ set :user, old_user
445
+ sessions.clear
446
+ end
447
+ end
448
+
449
+ desc <<-DESC
450
+ Grant *FULL* sudo access to the app user.
451
+ This is NOT RECOMMENDED, it will make the 'app' user the
452
+ equivalent of 'root' until the 'restrict_sudo_access' task is run.
453
+ Alternatively, a task that requires sudo ability can call the
454
+ allow_sudo method with a block, this will give the app user sudo
455
+ ability only while the block is being run.
456
+ DESC
457
+ task :grant_sudo_access do
458
+ allow_sudo
459
+ end
460
+
461
+ @within_sudo = 0
462
+ def allow_sudo
463
+ begin
464
+ @within_sudo += 1
465
+ old_user = fetch(:user)
466
+ if @within_sudo > 1
467
+ yield if block_given?
468
+ true
469
+ # The following can break if using multiple instances and a deploy was killed partway.
470
+ # Disabled for now.
471
+ # elsif capture("groups").split.include?("rootequiv")
472
+ # yield if block_given?
473
+ # false
474
+ else
475
+ begin
476
+ # need to cheat and temporarily set the user to ROOT so we
477
+ # can temporarily add the app user to the rootequiv group.
478
+ # we can do this because the root and app user have the same
479
+ # ssh login preferences....
480
+ set :user, 'ubuntu'
481
+ sessions.clear #clear out sessions cache..... this way the ssh connections are reinitialized
482
+
483
+ # Temporarily add the app user to the "rootequiv" group, this will give full sudo ability
484
+ sudo "adduser app admin"
485
+
486
+ set :user, old_user
487
+ sessions.clear
488
+ yield if block_given?
489
+ ensure
490
+ server.restrict_sudo_access if block_given?
491
+ set :user, old_user
492
+ sessions.clear
493
+ true
494
+ end
495
+ end
496
+ ensure
497
+ @within_sudo -= 1
498
+ end
499
+ end
500
+
501
+ end
502
+
503
+ end
504
+
505
+ end
@@ -0,0 +1,33 @@
1
+ # This file is part of EC2 on Rails.
2
+ # http://rubyforge.org/projects/ec2onrails/
3
+ #
4
+ # Copyright 2007 Paul Dowman, http://pauldowman.com/
5
+ #
6
+ # EC2 on Rails is free software; you can redistribute it and/or modify
7
+ # it under the terms of the GNU General Public License as published by
8
+ # the Free Software Foundation; either version 2 of the License, or
9
+ # (at your option) any later version.
10
+ #
11
+ # EC2 on Rails is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU General Public License
17
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
18
+
19
+ require "yaml"
20
+
21
+ module Ec2onrails
22
+ module VersionHelper
23
+ ROOT_DIR = File.dirname(__FILE__) + "/../.."
24
+
25
+ def self.string
26
+ File.read(ROOT_DIR + "/VERSION").strip
27
+ end
28
+
29
+ def self.ami_ids
30
+ YAML::load_file(ROOT_DIR + "/ami_ids.yml")
31
+ end
32
+ end
33
+ end
metadata CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
5
5
  segments:
6
6
  - 0
7
7
  - 0
8
- - 6
9
- version: 0.0.6
8
+ - 7
9
+ version: 0.0.7
10
10
  platform: ruby
11
11
  authors:
12
12
  - Xtreme Labs
@@ -70,8 +70,16 @@ extensions: []
70
70
 
71
71
  extra_rdoc_files: []
72
72
 
73
- files: []
74
-
73
+ files:
74
+ - Rakefile
75
+ - Version
76
+ - lib/ec2onrails.rb
77
+ - lib/ec2onrails/capistrano_utils.rb
78
+ - lib/ec2onrails/recipes.rb
79
+ - lib/ec2onrails/recipes/db.rb
80
+ - lib/ec2onrails/recipes/deploy.rb
81
+ - lib/ec2onrails/recipes/server.rb
82
+ - lib/ec2onrails/version_helper.rb
75
83
  has_rdoc: true
76
84
  homepage: http://ec2onrails.rubyforge.org
77
85
  licenses: []