ubistrano 1.2.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/MIT-LICENSE +20 -0
- data/README.markdown +162 -0
- data/Rakefile +25 -0
- data/bin/ubify +13 -0
- data/changelog.markdown +54 -0
- data/example/deploy.rb +29 -0
- data/gemspec.rb +19 -0
- data/lib/ubistrano.rb +83 -0
- data/lib/ubistrano/apache.rb +38 -0
- data/lib/ubistrano/deploy.rb +67 -0
- data/lib/ubistrano/ec2.rb +113 -0
- data/lib/ubistrano/gems.rb +29 -0
- data/lib/ubistrano/helpers.rb +287 -0
- data/lib/ubistrano/log.rb +20 -0
- data/lib/ubistrano/mysql.rb +85 -0
- data/lib/ubistrano/rails.rb +76 -0
- data/lib/ubistrano/sinatra.rb +20 -0
- data/lib/ubistrano/ssh.rb +56 -0
- data/lib/ubistrano/stage.rb +29 -0
- data/lib/ubistrano/ubuntu.rb +273 -0
- data/templates/apache/virtual_host.erb +32 -0
- data/templates/log/rotate.conf.erb +9 -0
- data/templates/rails/database.yml.erb +13 -0
- data/templates/ubuntu/apache.god.erb +31 -0
- data/templates/ubuntu/god.erb +36 -0
- data/templates/ubuntu/god.god.erb +1 -0
- data/templates/ubuntu/iptables.rules.erb +31 -0
- data/templates/ubuntu/mysql.god.erb +31 -0
- data/templates/ubuntu/sshd.god.erb +31 -0
- data/ubistrano.gemspec +32 -0
- metadata +93 -0
@@ -0,0 +1,67 @@
|
|
1
|
+
Capistrano::Configuration.instance(:must_exist).load do
|
2
|
+
|
3
|
+
namespace :deploy do
|
4
|
+
desc "Restart application"
|
5
|
+
task :restart, :roles => :app, :except => { :no_release => true } do
|
6
|
+
case platform
|
7
|
+
when :rails, :sinatra
|
8
|
+
run_each "touch #{current_path}/tmp/restart.txt"
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
desc "Start application"
|
13
|
+
task :start, :roles => :app do
|
14
|
+
apache.virtual_host.enable
|
15
|
+
end
|
16
|
+
|
17
|
+
desc "Stop application"
|
18
|
+
task :stop, :roles => :app do
|
19
|
+
apache.virtual_host.disable
|
20
|
+
end
|
21
|
+
|
22
|
+
desc "Deploy for the first time"
|
23
|
+
task :first, :roles => :app do
|
24
|
+
sudo_each [
|
25
|
+
"mkdir -p #{base_dir}",
|
26
|
+
"chown -R #{user}:#{user} #{base_dir}"
|
27
|
+
]
|
28
|
+
mysql.create
|
29
|
+
deploy.setup
|
30
|
+
case platform
|
31
|
+
when :php
|
32
|
+
deploy.update
|
33
|
+
when :rails
|
34
|
+
rails.config.default
|
35
|
+
deploy.update
|
36
|
+
deploy.migrate
|
37
|
+
when :sinatra
|
38
|
+
sinatra.config.default
|
39
|
+
deploy.update
|
40
|
+
end
|
41
|
+
apache.virtual_host.create
|
42
|
+
deploy.start
|
43
|
+
apache.reload
|
44
|
+
puts space(msg(:logrotate_suggest))
|
45
|
+
end
|
46
|
+
|
47
|
+
desc "Stop servers and destroy all files"
|
48
|
+
task :destroy, :roles => :app do
|
49
|
+
sudo_each "rm -Rf #{deploy_to}"
|
50
|
+
mysql.destroy.db
|
51
|
+
apache.virtual_host.destroy
|
52
|
+
end
|
53
|
+
|
54
|
+
namespace :web do
|
55
|
+
task :disable do
|
56
|
+
pub = "#{deploy_to}/current/public"
|
57
|
+
sudo_each "mv #{pub}/maintenance.html #{pub}/index.html"
|
58
|
+
end
|
59
|
+
|
60
|
+
task :enable do
|
61
|
+
pub = "#{deploy_to}/current/public"
|
62
|
+
sudo_each "mv #{pub}/index.html #{pub}/maintenance.html"
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
@@ -0,0 +1,113 @@
|
|
1
|
+
Capistrano::Configuration.instance(:must_exist).load do
|
2
|
+
|
3
|
+
namespace :ec2 do
|
4
|
+
desc "Set up a new EC2 instance and provision"
|
5
|
+
task :default, :roles => :web do
|
6
|
+
ec2.key_pair.install
|
7
|
+
ec2.instance.create
|
8
|
+
ec2.security.group.setup if yes("Set up the default security group? You only need to do this once.")
|
9
|
+
exit unless yes("Add the instance's IP address to config/deploy.rb. Continue?")
|
10
|
+
puts space(msg(:ec2_finished))
|
11
|
+
end
|
12
|
+
|
13
|
+
namespace :instance do
|
14
|
+
desc "Create a fresh Hardy instance"
|
15
|
+
task :create do
|
16
|
+
options = {
|
17
|
+
:image_id => ask('Press enter for Ubuntu Hardy or enter an AMI image id: ', 'ami-1c5db975'),
|
18
|
+
:key_name => "#{application}"
|
19
|
+
}
|
20
|
+
instance = ec2_api.run_instances(options).instancesSet.item[0]
|
21
|
+
instance_id = instance.instanceId
|
22
|
+
pp instance
|
23
|
+
ip = ec2_api.allocate_address.publicIp
|
24
|
+
ec2_api.associate_address(:instance_id => instance_id, :public_ip => ip)
|
25
|
+
puts "\nYour instance id is: #{instance_id}"
|
26
|
+
puts "Your IP address is: #{ip}"
|
27
|
+
end
|
28
|
+
|
29
|
+
desc "Restart an instance"
|
30
|
+
task :restart do
|
31
|
+
ec2.instances
|
32
|
+
pp ec2_api.reboot_instances(:instance_id => ask("Restart which instance ids?"))
|
33
|
+
end
|
34
|
+
|
35
|
+
desc "Destroy an instance"
|
36
|
+
task :destroy do
|
37
|
+
ec2.instances
|
38
|
+
instance_id = ask("Terminate which instance ids?")
|
39
|
+
ec2_api.terminate_instances(:instance_id => instance_id)
|
40
|
+
ip = ec2_api.describe_addresses.addressesSet.item.select { |x| x.instanceId == instance_id }.first
|
41
|
+
ec2_api.release_address(:public_ip => ip.publicIp) if ip
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
desc "List your EC2 instances"
|
46
|
+
task :instances do
|
47
|
+
pp ec2_api.describe_instances
|
48
|
+
end
|
49
|
+
|
50
|
+
desc "List IPs for this EC2 account"
|
51
|
+
task :ips do
|
52
|
+
pp ec2_api.describe_addresses
|
53
|
+
end
|
54
|
+
|
55
|
+
namespace :key_pair do
|
56
|
+
desc "Install key pair for SSH"
|
57
|
+
task :install do
|
58
|
+
begin
|
59
|
+
out = ec2_api.create_keypair(:key_name => application.to_s)
|
60
|
+
key = out.keyMaterial
|
61
|
+
rescue EC2::InvalidKeyPairDuplicate
|
62
|
+
ec2.key_pair.remove
|
63
|
+
ec2.key_pair.install
|
64
|
+
end
|
65
|
+
File.open(File.expand_path("~/.ssh/id_rsa-#{application}"), 'w') { |f| f.write key }
|
66
|
+
end
|
67
|
+
|
68
|
+
desc "Install key pair for SSH"
|
69
|
+
task :remove do
|
70
|
+
ec2_api.delete_keypair(:key_name => application.to_s)
|
71
|
+
`rm ~/.ssh/id_rsa-#{application}`
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
namespace :security do
|
76
|
+
namespace :group do
|
77
|
+
desc "Open standard ports for default security group"
|
78
|
+
task :setup do
|
79
|
+
[ 22, 80, 443 ].each do |port|
|
80
|
+
ec2_api.authorize_security_group_ingress(
|
81
|
+
:group_name => 'default', :cidr_ip => '0.0.0.0/0', :from_port => port, :to_port => port, :ip_protocol => 'tcp'
|
82
|
+
)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
desc "Describe default security group"
|
87
|
+
task :describe do
|
88
|
+
pp ec2_api.describe_security_groups(:group_name => 'default')
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
namespace :api_tools do
|
94
|
+
desc "Install ec2 api tools locally"
|
95
|
+
task :install, :roles => :web do
|
96
|
+
`cd ~ && curl http://s3.amazonaws.com/ec2-downloads/ec2-api-tools.zip -O`
|
97
|
+
`cd ~ && unzip ec2-api-tools.zip`
|
98
|
+
`cd ~ && rm ec2-api-tools.zip`
|
99
|
+
`mv ~/ec2-api-tools-* ~/.ec2`
|
100
|
+
end
|
101
|
+
|
102
|
+
desc "Install ec2 api tools locally"
|
103
|
+
task :remove, :roles => :web do
|
104
|
+
`rm -Rf ~/.ec2`
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def ec2_api
|
109
|
+
@ec2_api ||= EC2::Base.new(:access_key_id => ec2_access_key, :secret_access_key => ec2_secret_key)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
Capistrano::Configuration.instance(:must_exist).load do
|
2
|
+
|
3
|
+
namespace :gems do
|
4
|
+
desc "List gems on remote server"
|
5
|
+
task :list, :roles => :app do
|
6
|
+
run_puts "gem list"
|
7
|
+
end
|
8
|
+
|
9
|
+
desc "Update gems on remote server"
|
10
|
+
task :update, :roles => :app do
|
11
|
+
sudo_each [
|
12
|
+
"gem update --system",
|
13
|
+
"gem update"
|
14
|
+
]
|
15
|
+
end
|
16
|
+
|
17
|
+
desc "Install a remote gem"
|
18
|
+
task :install, :roles => :app do
|
19
|
+
gem_install ask('Enter the name of the gem to install:')
|
20
|
+
end
|
21
|
+
|
22
|
+
desc "Uninstall a remote gem"
|
23
|
+
task :uninstall, :roles => :app do
|
24
|
+
gem_name = ask 'Enter the name of the gem to remove:'
|
25
|
+
sudo "gem uninstall #{gem_name}"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
@@ -0,0 +1,287 @@
|
|
1
|
+
require 'erb'
|
2
|
+
|
3
|
+
Capistrano::Configuration.instance(:must_exist).load do
|
4
|
+
|
5
|
+
# Install
|
6
|
+
|
7
|
+
def gem_install(name, options='')
|
8
|
+
sudo_puts "gem install #{name} #{options} --no-rdoc --no-ri -q"
|
9
|
+
end
|
10
|
+
|
11
|
+
def install_source(source)
|
12
|
+
path, source = unpack_source source
|
13
|
+
yield path
|
14
|
+
sudo "rm -Rf #{source}"
|
15
|
+
end
|
16
|
+
|
17
|
+
def make_install(path)
|
18
|
+
";cd #{path} && ./configure && make && sudo make install"
|
19
|
+
end
|
20
|
+
|
21
|
+
def unpack_source(source)
|
22
|
+
url = sources[source]
|
23
|
+
name = File.basename url
|
24
|
+
src = "/home/#{user}/sources"
|
25
|
+
base = nil
|
26
|
+
[ 'tar.gz', 'tgz' ].each do |ext|
|
27
|
+
base = name[0..((ext.length + 2) * -1)] if name.include?(ext)
|
28
|
+
end
|
29
|
+
run_each [
|
30
|
+
"mkdir -p #{src}",
|
31
|
+
"cd #{src} && wget --quiet #{url}",
|
32
|
+
"tar -xzvf #{src}/#{name} -C #{src}"
|
33
|
+
]
|
34
|
+
[ "#{src}/#{base}", src ]
|
35
|
+
end
|
36
|
+
|
37
|
+
|
38
|
+
# Files
|
39
|
+
|
40
|
+
def add_line(file, *lines)
|
41
|
+
lines.each do |line|
|
42
|
+
sudo_each "echo \"#{line}\" | sudo tee -a #{file}"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def change_line(file, from, to)
|
47
|
+
sudo_each "sed -i 's/#{from}/#{to}/' #{file}"
|
48
|
+
end
|
49
|
+
|
50
|
+
def remove_line(file, *lines)
|
51
|
+
lines.each do |line|
|
52
|
+
change_line file, line, ''
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def get_ssh_key(key)
|
57
|
+
key.gsub!('.pub', '')
|
58
|
+
key = File.expand_path("~/.ssh/#{key}")
|
59
|
+
key = Dir[key + '.pub', key].first
|
60
|
+
if key
|
61
|
+
keys = File.open(key).collect { |line| line.strip.empty? ? nil : line.strip }.compact
|
62
|
+
keys.join("\n")
|
63
|
+
else
|
64
|
+
nil
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def upload_from_erb(destination, bind=nil, options={})
|
69
|
+
# options[ :chown => owner of file (default: deploy user),
|
70
|
+
# :chmod => 0644 etc
|
71
|
+
# :folder => 'postfix' etc,
|
72
|
+
# :name => name of template if differs from destination ]
|
73
|
+
if destination.respond_to?(:uniq)
|
74
|
+
destination.each { |d| upload_from_erb d, bind, options }
|
75
|
+
else
|
76
|
+
template = File.basename destination
|
77
|
+
template = template[1..-1] if template[0..0] == '.'
|
78
|
+
folder = options[:folder] ? options[:folder] + '/' : ''
|
79
|
+
template = File.expand_path("../../templates/#{folder}#{options[:name]||template}.erb", File.dirname(__FILE__))
|
80
|
+
template = File.read template
|
81
|
+
sudo "touch #{destination}"
|
82
|
+
sudo "chown #{user} #{destination}"
|
83
|
+
put ERB.new(template).result(bind || binding), destination
|
84
|
+
sudo("chown #{options[:chown]} #{destination}") if options[:chown]
|
85
|
+
sudo("chmod #{options[:chmod]} #{destination}") if options[:chmod]
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
|
90
|
+
# MySQL
|
91
|
+
|
92
|
+
def mysql_run(sql)
|
93
|
+
if sql.respond_to?(:uniq)
|
94
|
+
sql.each { |s| mysql_run s }
|
95
|
+
else
|
96
|
+
run "echo \"#{sql}\" | #{mysql_call}"
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def mysql_call
|
101
|
+
"mysql -f -u root --password=#{mysql_root_password || ''}"
|
102
|
+
end
|
103
|
+
|
104
|
+
|
105
|
+
# Questions
|
106
|
+
|
107
|
+
def ask(question, default='')
|
108
|
+
question = "\n" + question.join("\n") if question.respond_to?(:uniq)
|
109
|
+
answer = Capistrano::CLI.ui.ask(space(question)).strip
|
110
|
+
answer.empty? ? default : answer
|
111
|
+
end
|
112
|
+
|
113
|
+
def yes(question)
|
114
|
+
question = "\n" + question.join("\n") if question.respond_to?(:uniq)
|
115
|
+
question += ' (y/n)'
|
116
|
+
ask(question).downcase.include? 'y'
|
117
|
+
end
|
118
|
+
|
119
|
+
def space(str)
|
120
|
+
"\n#{'=' * 80}\n#{str}"
|
121
|
+
end
|
122
|
+
|
123
|
+
|
124
|
+
# Runners
|
125
|
+
|
126
|
+
def run_each(*args, &block)
|
127
|
+
cmd = args[0]
|
128
|
+
sudo = args[1]
|
129
|
+
if cmd.respond_to?(:uniq)
|
130
|
+
cmd.each { |c| run_each c, sudo, &block }
|
131
|
+
elsif sudo
|
132
|
+
puts space("sudo #{cmd}")
|
133
|
+
sudo(cmd) { |ch, st, data| block.call(data) if block }
|
134
|
+
else
|
135
|
+
puts space(cmd)
|
136
|
+
run(cmd) { |ch, st, data| block.call(data) if block }
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
def sudo_each(cmds, &block)
|
141
|
+
run_each cmds, true, &block
|
142
|
+
end
|
143
|
+
|
144
|
+
def run_puts(cmds, &block)
|
145
|
+
run_each(cmds) { |data| puts data }
|
146
|
+
end
|
147
|
+
|
148
|
+
def sudo_puts(cmds, &block)
|
149
|
+
sudo_each(cmds) { |data| puts data }
|
150
|
+
end
|
151
|
+
|
152
|
+
|
153
|
+
# Messages
|
154
|
+
|
155
|
+
def msg(type)
|
156
|
+
case type
|
157
|
+
when :about_templates
|
158
|
+
"Let's set up an Ubuntu server! (Tested with 8.04 LTS Hardy)
|
159
|
+
|
160
|
+
With each task, Ubistrano will describe what it is doing, and wait for a yes/no."
|
161
|
+
when :add_user
|
162
|
+
"Please ssh into your server (use -i only for EC2):
|
163
|
+
ssh root@#{host} -i ~/.ssh/id_rsa-#{application}
|
164
|
+
|
165
|
+
Add your deploy user:
|
166
|
+
adduser #{user}
|
167
|
+
|
168
|
+
Continue?"
|
169
|
+
when :aptitude_default
|
170
|
+
"Do you want me to run aptitude update, upgrade, and install build-essential?
|
171
|
+
If not, instructions for doing it manually will be displayed."
|
172
|
+
when :aptitude_instructions
|
173
|
+
"Please run these manually:
|
174
|
+
sudo aptitude update
|
175
|
+
sudo aptitude upgrade
|
176
|
+
sudo aptitude build-essential
|
177
|
+
|
178
|
+
Continue?"
|
179
|
+
when :create_keys
|
180
|
+
"May I generate an rsa ssh key pair in your ~/.ssh folder?"
|
181
|
+
when :create_server_keys
|
182
|
+
"May I generate an rsa ssh key pair on the server?
|
183
|
+
The public key will be displayed for adding to your GitHub account."
|
184
|
+
when :ec2_finished
|
185
|
+
"All finished! Run the following commands:
|
186
|
+
sudo chmod 600 ~/.ssh/id_rsa-#{application}
|
187
|
+
cap ubuntu"
|
188
|
+
when :god
|
189
|
+
"May I install God?"
|
190
|
+
when :god_apache
|
191
|
+
"Would you like God to monitor apache?
|
192
|
+
See #{File.expand_path '../../', File.dirname(__FILE__)}/templates/ubuntu/apache.god.erb"
|
193
|
+
when :god_mysql
|
194
|
+
"Would you like God to monitor mysql?
|
195
|
+
See #{File.expand_path '../../', File.dirname(__FILE__)}/templates/ubuntu/mysql.god.erb"
|
196
|
+
when :god_sshd
|
197
|
+
"Would you like God to monitor sshd?
|
198
|
+
See #{File.expand_path '../../', File.dirname(__FILE__)}/templates/ubuntu/sshd.god.erb"
|
199
|
+
when :god_finished
|
200
|
+
"Please run the following commands:
|
201
|
+
ssh #{user}@#{host}
|
202
|
+
sudo /etc/init.d/god start
|
203
|
+
sudo /etc/init.d/god start
|
204
|
+
|
205
|
+
Continue?"
|
206
|
+
when :iptables
|
207
|
+
"May I update your server's iptables, limiting access to SSH, HTTP, HTTPS, and ping only?
|
208
|
+
See #{File.expand_path '../../', File.dirname(__FILE__)}/templates/ubuntu/iptables.rules.erb"
|
209
|
+
when :logrotate
|
210
|
+
"May I add a logrotate entry for this application?
|
211
|
+
See #{File.expand_path '../../', File.dirname(__FILE__)}/templates/log/rotate.conf.erb"
|
212
|
+
when :logrotate_suggest
|
213
|
+
"All finished! Run `cap log:rotate` to add log rotating.
|
214
|
+
"
|
215
|
+
when :mysqltuner
|
216
|
+
"Would you like to install MySQLTuner and receive instructions for running it?"
|
217
|
+
when :mysqltuner_instructions
|
218
|
+
"Please ssh to your server and run `sudo mysqltuner`.
|
219
|
+
Continue?"
|
220
|
+
when :passenger
|
221
|
+
"Please run the following commands:
|
222
|
+
ssh #{user}@#{host}
|
223
|
+
sudo passenger-install-apache2-module
|
224
|
+
|
225
|
+
The apache config file is found at /etc/apache2/apache2.conf.
|
226
|
+
Reload apache?"
|
227
|
+
when :run_ubuntu_install
|
228
|
+
"Client and server configuration complete.
|
229
|
+
|
230
|
+
Please run the second half of the install:
|
231
|
+
cap ubuntu:install
|
232
|
+
|
233
|
+
"
|
234
|
+
when :secure_mysql
|
235
|
+
"It is highly recommended you run mysql_secure_installation manually:
|
236
|
+
ssh #{user}@#{host}
|
237
|
+
mysql_secure_installation
|
238
|
+
|
239
|
+
See http://dev.mysql.com/doc/refman/5.1/en/mysql-secure-installation.html
|
240
|
+
Continue?"
|
241
|
+
when :sinatra_install
|
242
|
+
"Would you like to run install.rb (from your app) if it exists?"
|
243
|
+
when :sshd_config
|
244
|
+
"May I update your server's sshd_config with the following settings?
|
245
|
+
Port #{port}
|
246
|
+
PermitRootLogin no
|
247
|
+
X11Forwarding no
|
248
|
+
UsePAM no
|
249
|
+
UseDNS no
|
250
|
+
"
|
251
|
+
when :ssh_config
|
252
|
+
"May I update your server's ssh_config with the following settings?
|
253
|
+
StrictHostKeyChecking no
|
254
|
+
"
|
255
|
+
when :ubuntu_restart
|
256
|
+
"Its probably a good idea to restart the server now.
|
257
|
+
OK?"
|
258
|
+
when :ubuntu_restart_2
|
259
|
+
"Please wait a little while for your server to restart.
|
260
|
+
|
261
|
+
Continue?"
|
262
|
+
when :ubuntu_finished
|
263
|
+
"That's it! Glad you made it.
|
264
|
+
|
265
|
+
Use `cap deploy:first` to set up your PHP, Rails, or Sinatra app.
|
266
|
+
Use `cap deploy` for all subsequent deploys.
|
267
|
+
|
268
|
+
"
|
269
|
+
when :upload_keys
|
270
|
+
"Would you like to upload a ssh key to the deploy user's authorized_keys?"
|
271
|
+
when :upload_keys_2
|
272
|
+
"Please enter a key in ~/.ssh to copy to the the deploy user's authorized_keys."
|
273
|
+
when :visudo
|
274
|
+
"Please ssh into your server (use -i only for EC2):
|
275
|
+
ssh root@#{host} -i ~/.ssh/id_rsa-#{application}
|
276
|
+
|
277
|
+
Edit your sudoers file:
|
278
|
+
visudo
|
279
|
+
|
280
|
+
Add the following line:
|
281
|
+
deploy ALL=NOPASSWD: ALL
|
282
|
+
|
283
|
+
Continue?"
|
284
|
+
end
|
285
|
+
end
|
286
|
+
|
287
|
+
end
|