Permalink
Fetching contributors…
Cannot retrieve contributors at this time
279 lines (241 sloc) 8.83 KB
# This file is part of EC2 on Rails.
# http://rubyforge.org/projects/ec2onrails/
#
# Copyright 2007 Paul Dowman, http://pauldowman.com/
#
# EC2 on Rails is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# EC2 on Rails is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# This script is meant to be run by rakefile-wrapper, which is run by
# Eric Hammond's Ubuntu build script: http://alestic.com/
# See the README file for details
require "rake/clean"
require 'yaml'
require 'erb'
require "#{File.dirname(__FILE__)}/../gem/lib/ec2onrails/version_helper"
# package notes:
# * gcc: libraries needed to compile c/c++ files from source
# * libmysqlclient-dev : provide mysqlclient-dev libs, needed for DataObject gems
# * nano/vim/less: simle file editors and viewer
# * git-core: because we are all using git now, aren't we?
# * xfsprogs: help with freezing and resizing of persistent volumes
#
@packages = %w(
adduser
bison
ca-certificates
cron
curl
flex
gcc
git-core
irb
less
libmysqlclient-dev
libmysql-ruby
libpcre3-dev
libssl-dev
libxml2-dev
libxslt1-dev
libyaml-ruby
libzlib-ruby
logrotate
make
mailx
memcached
mysql-client
mysql-server
nano
openssh-server
postfix
rdoc
ri
rsync
ruby-full
subversion
sysstat
unzip
varnish
vim
wget
xfsprogs
)
# NOTE: the amazon-ec2 gem is now at github, maintained by
# grempe-amazon-ec2. Will move back to regular amazon-ec2
# gem if/when he cuts a new release with volume and snapshot
# support included
@rubygems = [
"grempe-amazon-ec2",
"god",
"RubyInline",
"memcache-client",
"optiflag",
"passenger",
"rails",
"rails -v '~> 2.3.4'",
"rails -v '~> 2.2.3'",
"rails -v '~> 2.1.2'",
"rails -v '~> 2.0.5'",
"rails -v '~> 1.2.6'",
"rake",
"right_aws"
]
@build_root = "/mnt/build"
@fs_dir = "#{@build_root}/ubuntu"
@version = Ec2onrails::VersionHelper.string
task :default => :configure
desc "Removes all build files"
task :clean_all => :require_root do |t|
puts "Unmounting proc and dev from #{@build_root}..."
run "umount #{@build_root}/ubuntu/proc", true
run "umount #{@build_root}/ubuntu/dev", true
puts "Removing #{@build_root}..."
rm_rf @build_root
end
task :require_root do |t|
if `whoami`.strip != 'root'
raise "Sorry, this buildfile must be run as root."
end
end
desc "Use aptitude to install required packages inside the image's filesystem"
task :install_packages => :require_root do |t|
unless_completed(t) do
ENV['DEBIAN_FRONTEND'] = 'noninteractive'
ENV['LANG'] = ''
run_chroot "apt-get autoremove -y"
run_chroot "aptitude update"
run_chroot "aptitude install -y #{@packages.join(' ')}"
run_chroot "aptitude clean"
end
end
desc "Install required ruby gems inside the image's filesystem"
task :install_gems => [:require_root, :install_packages] do |t|
unless_completed(t) do
version = "1.3.5"
dir = "60718"
filename = "rubygems-#{version}.tgz"
url = "http://rubyforge.org/frs/download.php/#{dir}/#{filename}"
run_chroot "sh -c 'cd /tmp && wget -q #{url} && tar zxf #{filename}'"
run_chroot "sh -c 'cd /tmp/rubygems-#{version} && ruby setup.rb'"
run_chroot "ln -sf /usr/bin/gem1.8 /usr/bin/gem"
#NOTE: this will update to the most recent rubygems version even if we haven't updated the url here
run_chroot "gem update --system --no-rdoc --no-ri"
run_chroot "gem update --no-rdoc --no-ri"
run_chroot "gem sources -a http://gems.github.com"
run_chroot "gem install gemcutter --no-rdoc --no-ri"
run_chroot "gem tumble" # set gemcutter as default gem server
@rubygems.each do |g|
run_chroot "gem install #{g} --no-rdoc --no-ri"
end
end
end
desc "Install nginx from source"
task :install_nginx => [:require_root, :install_packages, :install_gems] do |t|
unless_completed(t) do
# To work around a Passenger bug, we need to edit the Rakefile.
# Unfortunately this is a bit brittle and will break when Passenger is updated
# The bug: http://code.google.com/p/phusion-passenger/issues/detail?id=316
# The solution is to add -mno-tls-direct-seg-refs to the compiler options:
# http://blog.pacharest.com/2009/08/a-bit-technical-nginx-passenger-4gb-seg-fixup/
replace_line("#{@fs_dir}/usr/lib/ruby/gems/1.8/gems/passenger-2.2.5/Rakefile", %q(EXTRA_CXXFLAGS = "-Wall -mno-tls-direct-seg-refs #{OPTIMIZATION_FLAGS}"), 50)
nginx_version = "nginx-0.7.60"
nginx_tar = "#{nginx_version}.tar.gz"
nginx_img = "http://sysoev.ru/nginx/#{nginx_tar}"
fair_bal_img = "http://github.com/gnosek/nginx-upstream-fair/tarball/master"
src_dir = "/tmp/src/nginx"
# Make sure the dir is created but empty...lets start afresh
run_chroot "mkdir -p -m 755 #{src_dir}/ && rm -rf #{src_dir}/*"
run_chroot "sh -c 'cd #{src_dir} && wget -q #{nginx_img} && tar -xzf #{nginx_tar}'"
run_chroot "sh -c 'cd #{src_dir}/#{nginx_version} && \
./configure \
--sbin-path=/usr/sbin \
--conf-path=/etc/nginx/nginx.conf \
--pid-path=/var/run/nginx.pid \
--error-log-path=/mnt/log/nginx/default_error.log \
--with-http_ssl_module \
--with-http_stub_status_module \
--add-module=`/usr/bin/passenger-config --root`/ext/nginx && \
make && \
make install'"
end
end
desc "Install Ubuntu packages, download and compile other software, and install gems"
task :install_software => [:require_root, :install_gems, :install_packages, :install_nginx]
desc "Configure the image"
task :configure => [:require_root, :install_software] do |t|
unless_completed(t) do
sh("cp -r files/* #{@fs_dir}")
replace("#{@fs_dir}/etc/motd.tail", /!!VERSION!!/, "Version #{@version}")
run_chroot "/usr/sbin/adduser --system --group --disabled-login --no-create-home nginx"
run_chroot "/usr/sbin/adduser --gecos ',,,' --disabled-password app"
run_chroot "/usr/sbin/addgroup rootequiv"
run_chroot "cp /root/.gemrc /home/app" # so the app user also has access to gems.github.com
run_chroot "chown app:app /home/app/.gemrc"
run "echo '. /usr/local/ec2onrails/config' >> #{@fs_dir}/root/.bashrc"
run "echo '. /usr/local/ec2onrails/config' >> #{@fs_dir}/home/app/.bashrc"
%w(mysql auth.log daemon.log kern.log mail.err mail.info mail.log mail.warn syslog user.log).each do |f|
rm_rf "#{@fs_dir}/var/log/#{f}"
run_chroot "ln -sf /mnt/log/#{f} /var/log/#{f}"
end
# Create symlinks to run scripts on startup
run_chroot "update-rc.d ec2-first-startup start 91 S ."
run_chroot "update-rc.d ec2-every-startup start 92 S ."
# Disable the services that will be managed by god, depending on the roles
%w(nginx mysql memcached varnish varnishncsa).each do |service|
run_chroot "update-rc.d -f #{service} remove"
run_chroot "update-rc.d #{service} stop 20 2 3 4 5 ."
end
# God is started by upstart so that it will be restarted automatically if it dies,
# see /etc/event.d/god
# Create the mail aliases db
run_chroot "postalias /etc/aliases"
run_chroot "chmod 0440 /etc/sudoers"
end
end
desc "This task is for deploying the contents of /files to a running server image to test config file changes without rebuilding."
task :deploy_files do |t|
raise "need 'key' and 'host' env vars defined" unless ENV['key'] && ENV['host']
run "rsync -rlvzcCp --rsh='ssh -l root -i #{ENV['key']}' files/ #{ENV['host']}:/"
end
##################
# Execute a given block and touch a stampfile. The block won't be run if the stampfile exists.
def unless_completed(task, &proc)
stampfile = "#{@build_root}/#{task.name}.completed"
unless File.exists?(stampfile)
yield
touch stampfile
end
end
def run_chroot(command, ignore_error = false)
run "chroot '#{@fs_dir}' #{command}", ignore_error
end
def run(command, ignore_error = false)
puts "*** #{command}"
result = system command
raise("error: #{$?}") unless result || ignore_error
end
def replace_line(file, newline, linenum)
contents = File.open(file, 'r').readlines
contents[linenum - 1] = newline
File.open(file, 'w') do |f|
contents.each {|line| f << line}
end
end
def replace(file, pattern, text)
contents = File.open(file, 'r').readlines
contents.each do |line|
line.gsub!(pattern, text)
end
File.open(file, 'w') do |f|
contents.each {|line| f << line}
end
end