Created
December 1, 2008 04:26
-
-
Save code/30620 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# File: dreamhost_agent.rb | |
# Author: Luke Hubbard | |
# Gist: http://gist.github.com/gists/30620 | |
# License: MIT | |
require 'rubygems' | |
require 'rubygists' | |
require 'net/ssh' | |
require 'net/scp' | |
require 'logger' | |
gist_require 21814 # dreamhost_adapter.rb | |
gist_require 21812 # password_generator.rb | |
class DreamhostAgent | |
LIBS = %w{ | |
http://kernel.org/pub/software/scm/git/git-1.6.0.3.tar.gz | |
ftp://ftp.ruby-lang.org/pub/ruby/1.8/ruby-1.8.7-p72.tar.gz | |
ftp://ftp.gnu.org/gnu/readline/readline-5.2.tar.gz | |
http://www.openssl.org/source/openssl-0.9.8i.tar.gz | |
http://rubyforge.iasi.roedu.net/files/rubygems/rubygems-1.3.1.tgz | |
} | |
DEFAULT_GEMS = %w{rails haml json mysql rack rspec hpricot capistrano capistrano-ext} | |
DEFAULT_OPTIONS = { | |
:shell=>'bash', | |
:lockdown_homedir=>true, | |
:disable_ftp=>true, | |
:sa_chk=>true, | |
:notify_disk=>true, | |
:quote_chk=>true, | |
:hard_quota=>(1024*100).to_s, # 100 GB | |
:app_path=>"app", | |
:mod_security_on=>true, | |
:passenger=>true, | |
:fastcgi=>false, | |
:www_redir=>"both", | |
:google_apps=>false, | |
:gmail=>false, | |
:gems=>DEFAULT_GEMS | |
} | |
REQUIRED_PARAMS = [:dh_user, :dh_password, :shell_user, :shell_password, :domain, :db_user, :db_prefix, :db_password] | |
NAME_SERVER = 'ns1.dreamhost.com' | |
attr_accessor :log, :options, :ssh, :scp, :api | |
def initialize(options={}) | |
self.log ||= Logger.new(STDOUT, :level=>:debug) | |
self.options = DEFAULT_OPTIONS.merge(options) | |
REQUIRED_PARAMS.each {|param| log.warn("Missing required param: #{param}") if options[param].nil? } | |
end | |
def [](name) | |
raise("Missing required param: #{name}") unless options.has_key?(name) | |
self.options[name] | |
end | |
def api | |
@api ||= DreamhostAdapter.new(self[:dh_user], self[:dh_password]) | |
end | |
def ssh | |
@ssh ||= Net::SSH.start(ip(self[:domain]), self[:shell_user], :password => self[:shell_password]) | |
end | |
def scp | |
@scp ||= Net::SCP.start(ip(self[:domain]), self[:shell_user], :password => self[:shell_password]) | |
end | |
def put(file, data) | |
scp.upload! StringIO.new(data), file | |
end | |
def ip(domain, ns=NAME_SERVER) | |
dig = `dig @#{ns} #{domain} | grep #{domain}`.split("\n").last | |
dig =~ /IN\s+A\s+([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})/ ? $1 : nil | |
end | |
def setup_user | |
api.add_user!( | |
:user=>self[:shell_user], # | |
:password=>self[:shell_password], # | |
:type=>'shell', | |
:shell=>self[:shell], | |
:lockdown_homedir=>self[:lockdown_homedir], | |
:disable_ftp=>self[:disable_ftp], | |
:sa_chk=>self[:sa_chk], | |
:notify_disk=>self[:notify_disk], | |
:quote_chk=>self[:quote_chk], | |
:hard_quota=>self[:hard_quota] | |
) | |
log.warn("It could take some time for this user to show up, wait 5 mins.") | |
end | |
def setup_domain | |
api.add_domain!( | |
:domain=>self[:domain], # | |
:user=>self[:shell_user], # | |
:relpath=>self[:app_path]+"/current/public", | |
:mod_security_on=>self[:mod_security_on], | |
:passenger=>self[:passenger], | |
:fastcgi=>self[:fastcgi], | |
:www_redir=>self[:www_redir], | |
:google_apps=>self[:google_apps], | |
:gmail=>self[:gmail] | |
) | |
end | |
def setup_database | |
api.add_mysql!( | |
:dbname=>self[:db_prefix]+'_development', # | |
:user=>self[:db_user], # | |
:password=>self[:db_password] # | |
) | |
api.add_mysql!( | |
:dbname=>self[:db_prefix]+'_test', # | |
:user=>self[:db_user], # | |
:password=>self[:db_password] # | |
) | |
api.add_mysql!( | |
:dbname=>self[:db_prefix]+'_staging', # | |
:user=>self[:db_user], # | |
:password=>self[:db_password] # | |
) | |
api.add_mysql!( | |
:dbname=>self[:db_prefix]+'_production', # | |
:user=>self[:db_user], # | |
:password=>self[:db_password] # | |
) | |
end | |
def download_libs | |
exec!("[ -d ~/tmp ] || (mkdir -p ~/tmp && echo 'Created temp dir ~/tmp')") | |
exec!('cd ~/tmp && '+LIBS.collect { |lib| "curl #{lib} | tar zxv" }.join('; ')) | |
end | |
def setup_dotfiles | |
put("~/.bash_profile", <<-EOF | |
EDITOR="/usr/bin/nano -w" | |
export PS1="\\[\\e]0;\\w\\a\\]\\n\\[\\e[32m\\]\\u@\\h \\[\\e[33m\\]\\w\\[\\e[0m\\]\\n\\\$ " | |
export LS_COLORS="no=00:fi=00:di=01;36:ln=01;35:pi=40;33:so=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01:ex=01;32:" | |
export LSCOLORS='GxFxcxdxCxegedabagacad' | |
# GENERAL | |
alias df='df -h' | |
alias du='du -h' | |
alias grep='grep --color' | |
alias ls='ls --color' | |
alias ll='ls -l' | |
. .bashrc | |
EOF | |
) | |
put("~/.bashrc", <<-EOF | |
export TZ="Europe/London" | |
export LD_LIBRARY_PATH="$HOME/.packages/lib" | |
export PATH="$HOME/.packages/bin:$HOME/.gems/bin:${PATH}" | |
export GEM_HOME="$HOME/.gems" | |
export GEM_PATH="$GEM_HOME:/usr/lib/ruby/gems/1.8" | |
EOF | |
) | |
exec!("mkdir -p ~/.ssh") | |
put("~/.ssh/authorized_keys", `cat ~/.ssh/id_rsa.pub`) | |
exec!("chmod -R 700 ~/.ssh") | |
# force relogin | |
close | |
end | |
def close | |
@scp = nil unless @scp.nil? | |
@ssh = nil unless @ssh.nil? | |
end | |
def exec!(cmd) | |
log.info cmd | |
result = ssh.exec!(cmd) | |
log.info result | |
result | |
end | |
def setup_libs | |
exec!(%{mkdir -p ~/.packages}) | |
exec!(%{cd ~/tmp/git-* && ./configure --prefix=$HOME/.packages NO_CURL=1 NO_MMAP=1 && make && make install}) | |
exec!(%{cd ~/tmp/readline-* && ./configure --prefix=$HOME/.packages && make && make install}) | |
exec!(%{cd ~/tmp/openssl-* && ./config --prefix=$HOME/.packages --openssldir=$HOME/openssl shared && make && make install}) | |
exec!(%{cd ~/tmp/ruby-* && ./configure --prefix=$HOME/.packages --with-openssl-dir=$HOME/.packages --with-readline-dir=$HOME/.packages && make && make install}) | |
exec!(%{cd ~/tmp/rubygems-* && ruby setup.rb config --prefix=$HOME/.packages && ruby setup.rb setup && ruby setup.rb install rubygems}) | |
end | |
def install_gems | |
exec!(%{gem install #{self[:gems].collect{|it| it.to_s}.join(' ')} --no-rdoc --no-ri}) | |
end | |
def prepare_capistrano | |
exec!(%{cd ~/#{self[:app_path]} && rm -rdf current && mkdir -p shared/config && mkdir -p shared/log && mkdir -p shared/pids && mkdir -p shared/system && mkdir -p releases}) | |
config = <<-CONFIG | |
default_run_options[:pty] = true | |
# Application | |
set :application, "" # THIS IS THE ONLY VARIABLE YOU MUST SET YOURSELF | |
# Connection | |
set :user, "#{self[:shell_user]}" | |
set :ssh_options, { :forward_agent => true } # You need to run 'ssh-add' before calling 'cap' | |
set :domain, "#{ip(self[:domain])}" # Using the IP instead of host incase of DNS issues. | |
# set :gems_for_project, %w(dr_nic_magic_models swiftiply) # list of gems to be installed | |
set :use_sudo, false | |
# Deployment | |
set :scm, "git" | |
set :scm_user, "#{self[:git_user]}" | |
set :repository, lambda { "[email protected]:#{self[:git_user]}/\#{application}.git" } | |
set :deploy_to, "/home/slice299/app" # fixed location for deployment | |
set :branch, "master" | |
set :git_enable_submodules, 1 | |
set :deploy_via, :remote_cache | |
# Database | |
set :db_prefix, "#{self[:db_prefix]}" | |
set :db_user, "#{self[:db_user]}" | |
set :db_password, "#{self[:db_password]}" | |
set :db_host, "#{self[:db_host]}" | |
# Update these if you're not running everything on one host. | |
role :app, domain | |
role :web, domain | |
role :db, domain, :primary => true | |
############################################################# | |
# Passenger | |
############################################################# | |
namespace :mod_rails do | |
desc <<-DESC | |
Restart the application altering tmp/restart.txt for mod_rails. | |
DESC | |
task :restart, :roles => :app do | |
run "touch \#{current_path}/tmp/restart.txt" | |
end | |
end | |
namespace :deploy do | |
%w(start restart).each { |name| task name, :roles => :app do mod_rails.restart end } | |
end | |
task :setup, :roles => [:app, :db, :web] do | |
desc "A setup task to put shared system, log, and database directories in place" | |
run <<-CMD | |
mkdir -p -m 775 \#{release_path} \#{shared_path}/system \#{shared_path}/db && | |
mkdir -p -m 777 \#{shared_path}/log \#{shared_path}/public/images/avatar | |
CMD | |
end | |
before "deploy:setup", :db | |
after "deploy:update_code", "db:symlink" | |
namespace :db do | |
desc "Create database yaml in shared path" | |
task :default do | |
db_config = ERB.new <<-EOF | |
base: &base | |
host: \#{db_host} | |
adapter: mysql | |
username: \#{db_user} | |
password: \#{db_password} | |
development: | |
database: \#{db_prefix}_development | |
<<: *base | |
test: | |
database: \#{db_prefix}_test | |
<<: *base | |
staging: | |
database: \#{db_prefix}_staging | |
<<: *base | |
production: | |
database: \#{db_prefix}_production | |
<<: *base | |
EOF | |
run "mkdir -p \#{shared_path}/config" | |
put db_config.result, "\#{shared_path}/config/database.yml" | |
end | |
desc "Make symlink for database yaml" | |
task :symlink do | |
run "ln -nfs \#{shared_path}/config/database.yml \#{release_path}/config/database.yml" | |
end | |
end | |
CONFIG | |
puts config | |
end | |
def setup! | |
log.info "Setting up user account" | |
setup_user | |
# to be safe we should wait 5-10 mins here. | |
log.info("Waiting for 5 mins for user to show up.. Go make coffee!") | |
sleep(60*5) | |
log.info "Adding domain to dreamhost" | |
setup_domain | |
log.info "Waiting for 2 mins for DNS to update." | |
sleep(60*2) | |
log.info "Adding database to #{self[:db_host]}" | |
setup_database | |
log.info "Adding dotfiles .bash_profile .bash_rc and ssh public key" | |
setup_dotfiles | |
log.info "Downloading packages for compilation" | |
download_libs | |
log.info "Compiling packages" | |
setup_libs | |
log.info "Installing gems" | |
install_gems | |
log.info "Preparing for capistrano" | |
prepare_capistrano | |
log.info "Install done! Wow that took a while." | |
puts options.inspect | |
puts ip(self[:domain]) | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment