deprec-core 3.1.0 → 3.1.1
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/README.md +7 -0
- data/lib/deprec-core/capistrano_extensions.rb +418 -0
- data/lib/deprec-core/version.rb +1 -1
- data/lib/vmbuilder_plugins/all.rb +20 -0
- data/lib/vmbuilder_plugins/apt.rb +93 -0
- data/lib/vmbuilder_plugins/emerge.rb +76 -0
- data/lib/vmbuilder_plugins/gem.rb +90 -0
- data/lib/vmbuilder_plugins/std.rb +203 -0
- metadata +8 -1
data/README.md
ADDED
@@ -0,0 +1,418 @@
|
|
1
|
+
# Copyright 2006-2008 by Mike Bailey. All rights reserved.
|
2
|
+
require 'capistrano'
|
3
|
+
require 'fileutils'
|
4
|
+
|
5
|
+
module Deprec2
|
6
|
+
|
7
|
+
# Temporarily modify ROLES if HOSTS not set
|
8
|
+
# Capistrano's default behaviour is for HOSTS to override ROLES
|
9
|
+
def for_roles(roles)
|
10
|
+
old_roles = ENV['ROLES']
|
11
|
+
ENV['ROLES'] = roles.to_s unless ENV['HOSTS']
|
12
|
+
yield
|
13
|
+
ENV['ROLES'] = old_roles.to_s unless ENV['HOSTS']
|
14
|
+
end
|
15
|
+
|
16
|
+
|
17
|
+
# Temporarily ignore ROLES and HOSTS
|
18
|
+
def ignoring_roles_and_hosts
|
19
|
+
old_roles = ENV['ROLES']
|
20
|
+
old_hosts = ENV['HOSTS']
|
21
|
+
ENV['ROLES'] = nil
|
22
|
+
ENV['HOSTS'] = nil
|
23
|
+
yield
|
24
|
+
ENV['ROLES'] = old_roles
|
25
|
+
ENV['HOSTS'] = old_hosts
|
26
|
+
end
|
27
|
+
|
28
|
+
DEPREC_TEMPLATES_BASE = File.join(File.dirname(__FILE__), 'templates')
|
29
|
+
|
30
|
+
# Render template (usually a config file)
|
31
|
+
#
|
32
|
+
# Usually we render it to a file on the local filesystem.
|
33
|
+
# This way, we keep a copy of the config file under source control.
|
34
|
+
# We can make manual changes if required and push to new hosts.
|
35
|
+
#
|
36
|
+
# If the options hash contains :path then it's written to that path.
|
37
|
+
# If it contains :remote => true, the file will instead be written to remote targets
|
38
|
+
# If options[:path] and options[:remote] are missing, it just returns the rendered
|
39
|
+
# template as a string (good for debugging).
|
40
|
+
#
|
41
|
+
# XXX I would like to get rid of :render_template_to_file
|
42
|
+
# XXX Perhaps pass an option to this function to write to remote
|
43
|
+
#
|
44
|
+
def render_template(app, options={})
|
45
|
+
template = options[:template]
|
46
|
+
path = options[:path] || nil
|
47
|
+
remote = options[:remote] || false
|
48
|
+
mode = options[:mode] || 0755
|
49
|
+
owner = options[:owner] || nil
|
50
|
+
stage = exists?(:stage) ? fetch(:stage).to_s : ''
|
51
|
+
# replace this with a check for the file
|
52
|
+
if ! template
|
53
|
+
puts "render_template() requires a value for the template!"
|
54
|
+
return false
|
55
|
+
end
|
56
|
+
|
57
|
+
# If local copies of deprec templates exist they will be used
|
58
|
+
# If you don't specify the location with the local_template_dir option
|
59
|
+
# it defaults to config/templates.
|
60
|
+
# e.g. config/templates/nginx/nginx.conf.erb
|
61
|
+
local_template = File.join(local_template_dir, app.to_s, template)
|
62
|
+
if File.exists?(local_template)
|
63
|
+
puts
|
64
|
+
puts "Using local template (#{local_template})"
|
65
|
+
template = ERB.new(IO.read(local_template), nil, '-')
|
66
|
+
else
|
67
|
+
template = ERB.new(IO.read(File.join(DEPREC_TEMPLATES_BASE, app.to_s, template)), nil, '-')
|
68
|
+
end
|
69
|
+
rendered_template = template.result(binding)
|
70
|
+
|
71
|
+
if remote
|
72
|
+
# render to remote machine
|
73
|
+
puts 'You need to specify a path to render the template to!' unless path
|
74
|
+
exit unless path
|
75
|
+
sudo "test -d #{File.dirname(path)} || #{sudo} mkdir -p #{File.dirname(path)}"
|
76
|
+
std.su_put rendered_template, path, '/tmp/', :mode => mode
|
77
|
+
sudo "chown #{owner} #{path}" if defined?(owner)
|
78
|
+
elsif path
|
79
|
+
# render to local file
|
80
|
+
full_path = File.join('config', stage, app.to_s, path)
|
81
|
+
path_dir = File.dirname(File.expand_path(full_path))
|
82
|
+
if File.exists?(full_path)
|
83
|
+
if IO.read(full_path) == rendered_template
|
84
|
+
puts "[skip] Identical file exists (#{full_path})."
|
85
|
+
return false
|
86
|
+
elsif overwrite?(full_path, rendered_template)
|
87
|
+
File.delete(full_path)
|
88
|
+
else
|
89
|
+
puts "[skip] Not overwriting #{full_path}"
|
90
|
+
return false
|
91
|
+
end
|
92
|
+
end
|
93
|
+
FileUtils.mkdir_p "#{path_dir}" if ! File.directory?(path_dir)
|
94
|
+
# added line above to make windows compatible
|
95
|
+
# system "mkdir -p #{path_dir}" if ! File.directory?(path_dir)
|
96
|
+
File.open(File.expand_path(full_path), 'w'){|f| f.write rendered_template }
|
97
|
+
puts "[done] #{full_path} written"
|
98
|
+
else
|
99
|
+
# render to string
|
100
|
+
return rendered_template
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def overwrite?(full_path, rendered_template)
|
105
|
+
if defined?(overwrite_all)
|
106
|
+
if overwrite_all == true
|
107
|
+
return true
|
108
|
+
else
|
109
|
+
return false
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
# XXX add :always and :never later - not sure how to set persistent value from here
|
114
|
+
# response = Capistrano::CLI.ui.ask "File exists. Overwrite? ([y]es, [n]o, [a]lways, n[e]ver)" do |q|
|
115
|
+
puts
|
116
|
+
response = Capistrano::CLI.ui.ask "File exists (#{full_path}).
|
117
|
+
Overwrite? ([y]es, [n]o, [d]iff)" do |q|
|
118
|
+
q.default = 'n'
|
119
|
+
end
|
120
|
+
|
121
|
+
case response
|
122
|
+
when 'y'
|
123
|
+
return true
|
124
|
+
when 'n'
|
125
|
+
return false
|
126
|
+
when 'd'
|
127
|
+
require 'tempfile'
|
128
|
+
tf = Tempfile.new("deprec_diff")
|
129
|
+
tf.puts(rendered_template)
|
130
|
+
tf.close
|
131
|
+
puts
|
132
|
+
puts "Running diff -u current_file new_file_if_you_overwrite"
|
133
|
+
puts
|
134
|
+
system "diff -u #{full_path} #{tf.path} | less"
|
135
|
+
puts
|
136
|
+
overwrite?(full_path, rendered_template)
|
137
|
+
# XXX add :always and :never later - not sure how to set persistent value from here
|
138
|
+
# when 'a'
|
139
|
+
# set :overwrite_all, true
|
140
|
+
# when 'e'
|
141
|
+
# set :overwrite_all, false
|
142
|
+
end
|
143
|
+
|
144
|
+
end
|
145
|
+
|
146
|
+
def render_template_to_file(template_name, destination_file_name, templates_dir = DEPREC_TEMPLATES_BASE)
|
147
|
+
template_name += '.conf' if File.extname(template_name) == '' # XXX this to be removed
|
148
|
+
|
149
|
+
file = File.join(templates_dir, template_name)
|
150
|
+
buffer = render :template => File.read(file)
|
151
|
+
|
152
|
+
temporary_location = "/tmp/#{template_name}"
|
153
|
+
put buffer, temporary_location
|
154
|
+
sudo "cp #{temporary_location} #{destination_file_name}"
|
155
|
+
delete temporary_location
|
156
|
+
end
|
157
|
+
|
158
|
+
# Copy configs to server(s). Note there is no :pull task. No changes should
|
159
|
+
# be made to configs on the servers so why would you need to pull them back?
|
160
|
+
def push_configs(app, files)
|
161
|
+
app = app.to_s
|
162
|
+
stage = exists?(:stage) ? fetch(:stage).to_s : ''
|
163
|
+
|
164
|
+
files.each do |file|
|
165
|
+
full_local_path = File.join('config', stage, app, file[:path])
|
166
|
+
if File.exists?(full_local_path)
|
167
|
+
# If the file path is relative we will prepend a path to this projects
|
168
|
+
# own config directory for this service.
|
169
|
+
if file[:path][0,1] != '/'
|
170
|
+
full_remote_path = File.join(deploy_to, app, file[:path])
|
171
|
+
else
|
172
|
+
full_remote_path = file[:path]
|
173
|
+
end
|
174
|
+
sudo "test -d #{File.dirname(full_remote_path)} || #{sudo} mkdir -p #{File.dirname(full_remote_path)}"
|
175
|
+
std.su_put File.read(full_local_path), full_remote_path, '/tmp/', :mode=>file[:mode]
|
176
|
+
sudo "chown #{file[:owner]} #{full_remote_path}"
|
177
|
+
else
|
178
|
+
# Render directly to remote host.
|
179
|
+
render_template(app, file.merge(:remote => true))
|
180
|
+
end
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
def teardown_connections
|
185
|
+
sessions.keys.each do |server|
|
186
|
+
sessions[server].close
|
187
|
+
sessions.delete(server)
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
def append_to_file_if_missing(filename, value, options={})
|
192
|
+
# XXX sort out single quotes in 'value' - they'l break command!
|
193
|
+
# XXX if options[:requires_sudo] and :use_sudo then use sudo
|
194
|
+
sudo <<-END
|
195
|
+
sh -c '
|
196
|
+
grep -F "#{value}" #{filename} > /dev/null 2>&1 ||
|
197
|
+
echo "#{value}" >> #{filename}
|
198
|
+
'
|
199
|
+
END
|
200
|
+
end
|
201
|
+
|
202
|
+
# create new user account on target system
|
203
|
+
def useradd(user, options={})
|
204
|
+
options[:shell] ||= '/bin/bash' # new accounts on ubuntu 6.06.1 have been getting /bin/sh
|
205
|
+
switches = ''
|
206
|
+
switches += " --shell=#{options[:shell]} " if options[:shell]
|
207
|
+
switches += ' --create-home ' unless options[:homedir] == false
|
208
|
+
switches += " --gid #{options[:group]} " unless options[:group].nil?
|
209
|
+
invoke_command "grep '^#{user}:' /etc/passwd || #{sudo} /usr/sbin/useradd #{switches} #{user}",
|
210
|
+
:via => run_method
|
211
|
+
end
|
212
|
+
|
213
|
+
# create a new group on target system
|
214
|
+
def groupadd(group, options={})
|
215
|
+
via = options.delete(:via) || run_method
|
216
|
+
# XXX I don't like specifying the path to groupadd - need to sort out paths before long
|
217
|
+
invoke_command "grep '#{group}:' /etc/group || #{sudo} /usr/sbin/groupadd #{group}", :via => via
|
218
|
+
end
|
219
|
+
|
220
|
+
# add group to the list of groups this user belongs to
|
221
|
+
def add_user_to_group(user, group)
|
222
|
+
invoke_command "groups #{user} | grep ' #{group} ' || #{sudo} /usr/sbin/usermod -G #{group} -a #{user}",
|
223
|
+
:via => run_method
|
224
|
+
end
|
225
|
+
|
226
|
+
# create directory if it doesn't already exist
|
227
|
+
# set permissions and ownership
|
228
|
+
# XXX move mode, path and
|
229
|
+
def mkdir(path, options={})
|
230
|
+
via = options.delete(:via) || :run
|
231
|
+
# XXX need to make sudo commands wrap the whole command (sh -c ?)
|
232
|
+
# XXX removed the extra 'sudo' from after the '||' - need something else
|
233
|
+
invoke_command "test -d #{path} || #{sudo if via == :sudo} mkdir -p #{path}"
|
234
|
+
invoke_command "chmod #{sprintf("%3o",options[:mode]||0755)} #{path}", :via => via if options[:mode]
|
235
|
+
invoke_command "chown -R #{options[:owner]} #{path}", :via => via if options[:owner]
|
236
|
+
groupadd(options[:group], :via => via) if options[:group]
|
237
|
+
invoke_command "chgrp -R #{options[:group]} #{path}", :via => via if options[:group]
|
238
|
+
end
|
239
|
+
|
240
|
+
def create_src_dir
|
241
|
+
mkdir(src_dir, :mode => 0775, :group => group_src, :via => :sudo)
|
242
|
+
end
|
243
|
+
|
244
|
+
# download source pkg if we don't already have it
|
245
|
+
def download_src(src_pkg, src_dir=src_dir)
|
246
|
+
set_pkg_defaults(src_pkg)
|
247
|
+
create_src_dir
|
248
|
+
# check if file exists and if we have an MD5 hash or bytecount to compare
|
249
|
+
# against if so, compare and decide if we need to download again
|
250
|
+
if defined?(src_pkg[:md5sum])
|
251
|
+
md5_clause = " && echo '#{src_pkg[:md5sum]}' | md5sum -c - "
|
252
|
+
end
|
253
|
+
case src_pkg[:download_method]
|
254
|
+
# when getting source with git
|
255
|
+
when :git
|
256
|
+
# ensure git is installed
|
257
|
+
apt.install( {:base => %w(git-core)}, :stable) #TODO fix this to test ubuntu version <hardy might need specific git version for full git submodules support
|
258
|
+
pkg_dir = File.join(src_dir, src_pkg[:dir])
|
259
|
+
run "if [ -d #{pkg_dir} ]; then cd #{pkg_dir} && #{sudo} git checkout master && #{sudo} git pull && #{sudo} git submodule init && #{sudo} git submodule update; else #{sudo} git clone #{src_pkg[:url]} #{pkg_dir} && cd #{pkg_dir} && #{sudo} git submodule init && #{sudo} git submodule update ; fi"
|
260
|
+
# Checkout the revision wanted if defined
|
261
|
+
if src_pkg[:version]
|
262
|
+
run "cd #{pkg_dir} && git branch | grep '#{src_pkg[:version]}$' && #{sudo} git branch -D '#{src_pkg[:version]}'; exit 0"
|
263
|
+
run "cd #{pkg_dir} && #{sudo} git checkout -b #{src_pkg[:version]} #{src_pkg[:version]}"
|
264
|
+
end
|
265
|
+
|
266
|
+
# when getting source with wget
|
267
|
+
when :http
|
268
|
+
# ensure wget is installed
|
269
|
+
apt.install( {:base => %w(wget)}, :stable )
|
270
|
+
# XXX replace with invoke_command
|
271
|
+
run "cd #{src_dir} && test -f #{src_pkg[:filename]} #{md5_clause} || #{sudo} wget --quiet --timestamping #{src_pkg[:url]}"
|
272
|
+
|
273
|
+
when :deb
|
274
|
+
run "cd #{src_dir} && test -f #{src_pkg[:filename]} #{md5_clause} || #{sudo} wget --quiet --timestamping #{src_pkg[:url]}"
|
275
|
+
|
276
|
+
else
|
277
|
+
puts "DOWNLOAD SRC: Download method not recognised. src_pkg[:download_method]: #{src_pkg[:download_method]}"
|
278
|
+
end
|
279
|
+
end
|
280
|
+
|
281
|
+
# unpack src and make it writable by the group
|
282
|
+
def unpack_src(src_pkg, src_dir=src_dir)
|
283
|
+
set_pkg_defaults(src_pkg)
|
284
|
+
pkg_dir = File.join([src_dir, src_pkg[:dir]].compact)
|
285
|
+
case src_pkg[:download_method]
|
286
|
+
# when unpacking git sources - nothing to do
|
287
|
+
when :deb
|
288
|
+
puts "UNPACK SRC: nothing to do for deb installs"
|
289
|
+
when :git
|
290
|
+
puts "UNPACK SRC: nothing to do for git installs"
|
291
|
+
when :http
|
292
|
+
run "test -d #{pkg_dir}.old && #{sudo} rm -fr #{pkg_dir}.old; exit 0"
|
293
|
+
run "test -d #{pkg_dir} && #{sudo} mv #{pkg_dir} #{pkg_dir}.old; exit 0"
|
294
|
+
run "cd #{src_dir} && #{sudo} #{src_pkg[:unpack]}" if src_pkg[:unpack] != ''
|
295
|
+
run "#{sudo} chgrp -R #{group} #{pkg_dir}"
|
296
|
+
run "#{sudo} chmod -R g+w #{pkg_dir}"
|
297
|
+
else
|
298
|
+
puts "UNPACK SRC: Download method not recognised. src_pkg[:download_method]: #{src_pkg[:download_method]} "
|
299
|
+
end
|
300
|
+
end
|
301
|
+
|
302
|
+
def set_pkg_defaults(pkg)
|
303
|
+
pkg[:filename] ||= File.basename(pkg[:url])
|
304
|
+
pkg[:download_method] ||= :http
|
305
|
+
pkg[:post_install] ||= ''
|
306
|
+
case pkg[:download_method]
|
307
|
+
when :http
|
308
|
+
pkg[:dir] ||= pkg[:filename].sub(/(\.tgz|\.tar\.gz)/,'')
|
309
|
+
pkg[:unpack] ||= "tar zxf #{pkg[:filename]};"
|
310
|
+
pkg[:configure] ||= './configure ;'
|
311
|
+
pkg[:make] ||= 'make;'
|
312
|
+
pkg[:install] ||= 'make install;'
|
313
|
+
when :deb
|
314
|
+
pkg[:dir] ||= ''
|
315
|
+
pkg[:unpack] ||= ''
|
316
|
+
pkg[:configure] ||= ''
|
317
|
+
pkg[:make] ||= ''
|
318
|
+
pkg[:install] ||= "dpkg -i #{pkg[:filename]}"
|
319
|
+
end
|
320
|
+
end
|
321
|
+
|
322
|
+
# install pkg from source
|
323
|
+
def install_from_src(src_pkg, src_dir=src_dir)
|
324
|
+
install_deps(src_pkg[:deps])
|
325
|
+
set_pkg_defaults(src_pkg)
|
326
|
+
pkg_dir = File.join([src_dir, src_pkg[:dir]].compact)
|
327
|
+
unpack_src(src_pkg, src_dir)
|
328
|
+
apt.install( {:base => %w(build-essential)}, :stable )
|
329
|
+
run "cd #{pkg_dir} && #{sudo} #{src_pkg[:configure]}" if src_pkg[:configure] != ''
|
330
|
+
run "cd #{pkg_dir} && #{sudo} #{src_pkg[:make]}" if src_pkg[:make] != ''
|
331
|
+
run "cd #{pkg_dir} && #{sudo} #{src_pkg[:install]}" if src_pkg[:install] != ''
|
332
|
+
run "cd #{pkg_dir} && #{sudo} #{src_pkg[:post_install]}" if src_pkg[:post_install] != ''
|
333
|
+
end
|
334
|
+
|
335
|
+
def install_deps(packages=[])
|
336
|
+
apt.install({:base => Array(packages)}, :stable)
|
337
|
+
end
|
338
|
+
|
339
|
+
def read_database_yml
|
340
|
+
stage = exists?(:stage) ? fetch(:stage).to_s : ''
|
341
|
+
db_config = YAML.load_file(File.join('config', stage, 'database.yml'))
|
342
|
+
set :db_user, db_config[rails_env]["username"]
|
343
|
+
set :db_password, db_config[rails_env]["password"]
|
344
|
+
set :db_name, db_config[rails_env]["database"]
|
345
|
+
end
|
346
|
+
|
347
|
+
|
348
|
+
##
|
349
|
+
# Run a command and ask for input when input_query is seen.
|
350
|
+
# Sends the response back to the server.
|
351
|
+
#
|
352
|
+
# +input_query+ is a regular expression that defaults to /^Password/.
|
353
|
+
#
|
354
|
+
# Can be used where +run+ would otherwise be used.
|
355
|
+
#
|
356
|
+
# run_with_input 'ssh-keygen ...', /^Are you sure you want to overwrite\?/
|
357
|
+
|
358
|
+
def run_with_input(shell_command, input_query=/^Password/, response=nil)
|
359
|
+
handle_command_with_input(:run, shell_command, input_query, response)
|
360
|
+
end
|
361
|
+
|
362
|
+
##
|
363
|
+
# Run a command using sudo and ask for input when a regular expression is seen.
|
364
|
+
# Sends the response back to the server.
|
365
|
+
#
|
366
|
+
# See also +run_with_input+
|
367
|
+
#
|
368
|
+
# +input_query+ is a regular expression
|
369
|
+
|
370
|
+
def sudo_with_input(shell_command, input_query=/^Password/, response=nil)
|
371
|
+
handle_command_with_input(:sudo, shell_command, input_query, response)
|
372
|
+
end
|
373
|
+
|
374
|
+
def invoke_with_input(shell_command, input_query=/^Password/, response=nil)
|
375
|
+
handle_command_with_input(run_method, shell_command, input_query, response)
|
376
|
+
end
|
377
|
+
|
378
|
+
##
|
379
|
+
# Run a command using sudo and continuously pipe the results back to the console.
|
380
|
+
#
|
381
|
+
# Similar to the built-in +stream+, but for privileged users.
|
382
|
+
|
383
|
+
def sudo_stream(command)
|
384
|
+
sudo(command) do |ch, stream, out|
|
385
|
+
puts out if stream == :out
|
386
|
+
if stream == :err
|
387
|
+
puts "[err : #{ch[:host]}] #{out}"
|
388
|
+
break
|
389
|
+
end
|
390
|
+
end
|
391
|
+
end
|
392
|
+
|
393
|
+
private
|
394
|
+
|
395
|
+
##
|
396
|
+
# Does the actual capturing of the input and streaming of the output.
|
397
|
+
#
|
398
|
+
# local_run_method: run or sudo
|
399
|
+
# shell_command: The command to run
|
400
|
+
# input_query: A regular expression matching a request for input: /^Please enter your password/
|
401
|
+
|
402
|
+
def handle_command_with_input(local_run_method, shell_command, input_query, response=nil)
|
403
|
+
send(local_run_method, shell_command) do |channel, stream, data|
|
404
|
+
logger.info data, channel[:host]
|
405
|
+
if data =~ input_query
|
406
|
+
if response
|
407
|
+
channel.send_data "#{response}\n"
|
408
|
+
else
|
409
|
+
response = ::Capistrano::CLI.password_prompt "#{data}"
|
410
|
+
channel.send_data "#{response}\n"
|
411
|
+
end
|
412
|
+
end
|
413
|
+
end
|
414
|
+
end
|
415
|
+
|
416
|
+
end
|
417
|
+
|
418
|
+
Capistrano.plugin :deprec2, Deprec2
|
data/lib/deprec-core/version.rb
CHANGED
@@ -0,0 +1,20 @@
|
|
1
|
+
# =all.rb: Load all the Capistrano Plugins in the directory.
|
2
|
+
#
|
3
|
+
# Require all other ruby files in the directory.
|
4
|
+
#
|
5
|
+
# ----
|
6
|
+
# Copyright (c) 2007 Neil Wilson, Aldur Systems Ltd
|
7
|
+
#
|
8
|
+
# Licensed under the GNU Public License v2. No warranty is provided.
|
9
|
+
# ----
|
10
|
+
# = Usage
|
11
|
+
#
|
12
|
+
# require 'vmbuilder_plugins/all'
|
13
|
+
|
14
|
+
# Splitting and joining __FILE__ deals with the current directory case
|
15
|
+
# properly
|
16
|
+
Dir[File.join( File.dirname(__FILE__), '*.rb')].each do |plugin_name|
|
17
|
+
unless plugin_name == File.join(File.dirname(__FILE__), File.basename(__FILE__))
|
18
|
+
require plugin_name
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
# =apt.rb: Debian 'apt' Installer library
|
2
|
+
# Capistrano plugin module to install and manage apt packages
|
3
|
+
#
|
4
|
+
# ----
|
5
|
+
# Copyright (c) 2007 Neil Wilson, Aldur Systems Ltd
|
6
|
+
#
|
7
|
+
# Licensed under the GNU Public License v2. No warranty is provided.
|
8
|
+
|
9
|
+
require 'capistrano'
|
10
|
+
|
11
|
+
# = Purpose
|
12
|
+
# Apt is a Capistrano plugin module providing a set of methods
|
13
|
+
# that invoke the *apt* package manager (as used in Debian and Ubuntu)
|
14
|
+
#
|
15
|
+
# Installs within Capistrano as the plugin _apt_.
|
16
|
+
#
|
17
|
+
# =Usage
|
18
|
+
#
|
19
|
+
# require 'vmbuilder_plugins/apt'
|
20
|
+
#
|
21
|
+
# Prefix all calls to the library with <tt>apt.</tt>
|
22
|
+
#
|
23
|
+
module Apt
|
24
|
+
|
25
|
+
# Default apt-get command - reduces any interactivity to the minimum.
|
26
|
+
APT_GET="DEBCONF_TERSE='yes' DEBIAN_PRIORITY='critical' DEBIAN_FRONTEND=noninteractive apt-get"
|
27
|
+
|
28
|
+
# Run the apt install program across the package list in 'packages'.
|
29
|
+
# Select those packages referenced by <tt>:base</tt> and the +version+
|
30
|
+
# of the distribution you want to use.
|
31
|
+
def install(packages, version, options={})
|
32
|
+
update
|
33
|
+
special_options="--allow-unauthenticated" if version != :stable
|
34
|
+
send(run_method, %{
|
35
|
+
sh -c "#{APT_GET} -qyu --force-yes #{special_options.to_s} install #{package_list(packages, version)}"
|
36
|
+
}, options)
|
37
|
+
end
|
38
|
+
|
39
|
+
# Run an apt clean
|
40
|
+
def clean(options={})
|
41
|
+
send(run_method, %{sh -c "#{APT_GET} -qy clean"}, options)
|
42
|
+
end
|
43
|
+
|
44
|
+
# Run an apt autoclean
|
45
|
+
def autoclean(options={})
|
46
|
+
send(run_method, %{sh -c "#{APT_GET} -qy autoclean"}, options)
|
47
|
+
end
|
48
|
+
|
49
|
+
# Run an apt distribution upgrade
|
50
|
+
def dist_upgrade(options={})
|
51
|
+
update
|
52
|
+
send(run_method, %{sh -c "#{APT_GET} -qy dist-upgrade"}, options)
|
53
|
+
end
|
54
|
+
|
55
|
+
# Run an apt upgrade. Use dist_upgrade instead if you want to upgrade
|
56
|
+
# the critical base packages.
|
57
|
+
def upgrade(options={})
|
58
|
+
update
|
59
|
+
send(run_method, %{sh -c "#{APT_GET} -qy upgrade"}, options)
|
60
|
+
end
|
61
|
+
|
62
|
+
# Run an apt update.
|
63
|
+
def update(options={})
|
64
|
+
send(run_method, %{sh -c "#{APT_GET} -qy update"}, options)
|
65
|
+
end
|
66
|
+
|
67
|
+
# RPM package install via alien
|
68
|
+
def rpm_install(packages, options={})
|
69
|
+
install({:base => %w(wget alien) }, :base)
|
70
|
+
send(run_method, "wget -Ncq #{packages.join(' ')}", options)
|
71
|
+
files=packages.collect { |package| File.basename(package) }
|
72
|
+
send(run_method, "alien -i #{files.join(' ')}", options)
|
73
|
+
end
|
74
|
+
|
75
|
+
# Clear the source list and package cache
|
76
|
+
def clear_cache(options={})
|
77
|
+
clean
|
78
|
+
cmd="rm -f /var/cache/apt/*.bin /var/lib/apt/lists/*_* /var/lib/apt/lists/partial/*"
|
79
|
+
send(run_method, cmd, options)
|
80
|
+
end
|
81
|
+
|
82
|
+
private
|
83
|
+
|
84
|
+
# Provides a string containing all the package names in the base
|
85
|
+
#list plus those in +version+.
|
86
|
+
def package_list(packages, version)
|
87
|
+
Array(packages[:base]).join(' ') + ' ' + Array(packages[version]).join(' ')
|
88
|
+
end
|
89
|
+
|
90
|
+
end
|
91
|
+
|
92
|
+
Capistrano.plugin :apt, Apt
|
93
|
+
# vim: nowrap sw=2 sts=2 ts=8 ff=unix ft=ruby:
|
@@ -0,0 +1,76 @@
|
|
1
|
+
# =emerge.rb: Gentoo 'emerge' Installer library
|
2
|
+
# Capistrano task library to install and manage portage packages
|
3
|
+
#
|
4
|
+
# Copyright (c) 2007 monki(Wesley Beary)
|
5
|
+
#
|
6
|
+
# inspiration: vmbuilder by Neil Wilson, Aldur Systems Ltd
|
7
|
+
#
|
8
|
+
# Licenced under the GNU Public License v2. No warranty is provided.
|
9
|
+
|
10
|
+
require 'capistrano'
|
11
|
+
|
12
|
+
# =Purpose
|
13
|
+
# emerge is a Capistrano plugin module providing a set of methods
|
14
|
+
# that invoke the portage package manage (as used in Gentoo)
|
15
|
+
#
|
16
|
+
# Installs within Capistrano as the plugin _emerge_.
|
17
|
+
#
|
18
|
+
# =Usage
|
19
|
+
#
|
20
|
+
# require 'marshall/plugins/emerge'
|
21
|
+
#
|
22
|
+
# Prefix all calls to the library with <tt>emerge.</tt>
|
23
|
+
#
|
24
|
+
module Emerge
|
25
|
+
# Default emerge command - reduce interactivity to the minimum
|
26
|
+
EMERGE="emerge -q"
|
27
|
+
|
28
|
+
# Emerge a new package or packages
|
29
|
+
def install(packages, options={})
|
30
|
+
cmd = <<-CMD
|
31
|
+
sh -c "#{EMERGE} #{packages.join(" ")}"
|
32
|
+
CMD
|
33
|
+
sudo(cmd, options)
|
34
|
+
end
|
35
|
+
|
36
|
+
# Run clean old/unused packages
|
37
|
+
def clean(options={})
|
38
|
+
cmd = <<-CMD
|
39
|
+
sh -c "#{EMERGE} -clean"
|
40
|
+
CMD
|
41
|
+
sudo(cmd, options)
|
42
|
+
end
|
43
|
+
|
44
|
+
# Upgrade installed package list
|
45
|
+
def upgrade(options={})
|
46
|
+
cmd = <<-CMD
|
47
|
+
sh -c "#{EMERGE} --sync"
|
48
|
+
CMD
|
49
|
+
sudo(cmd, options)
|
50
|
+
end
|
51
|
+
|
52
|
+
# Update portage
|
53
|
+
def update_system(options={})
|
54
|
+
cmd = <<-CMD
|
55
|
+
sh -c "#{EMERGE} portage"
|
56
|
+
CMD
|
57
|
+
sudo(cmd, options)
|
58
|
+
end
|
59
|
+
|
60
|
+
# Update all installed packages
|
61
|
+
def update(options={})
|
62
|
+
cmd = <<-CMD
|
63
|
+
sh -c "#{EMERGE} --update --deep --newuse world"
|
64
|
+
CMD
|
65
|
+
sudo(cmd, options)
|
66
|
+
end
|
67
|
+
|
68
|
+
# Boot script manipulation command
|
69
|
+
def rc_update(packages, setting)
|
70
|
+
packages.each do |service|
|
71
|
+
sudo "rc_update add #{service} #{setting}"
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
Capistrano.plugin :emerge, Emerge
|
@@ -0,0 +1,90 @@
|
|
1
|
+
# =gem.rb: Gem Installer library
|
2
|
+
# Capistrano library to install and manage Ruby Gems.
|
3
|
+
#
|
4
|
+
# ----
|
5
|
+
# Copyright (c) 2007 Neil Wilson, Aldur Systems Ltd
|
6
|
+
#
|
7
|
+
# Licensed under the GNU Public License v2. No warranty is provided.
|
8
|
+
|
9
|
+
require 'capistrano'
|
10
|
+
|
11
|
+
# = Purpose
|
12
|
+
# Gem is a Capistrano plugin module providing a set of methods
|
13
|
+
# that invoke the *gem* package manager.
|
14
|
+
#
|
15
|
+
# Installs within Capistrano as the plugin _gem_.
|
16
|
+
#
|
17
|
+
# =Usage
|
18
|
+
#
|
19
|
+
# require 'vmbuilder_plugins/gem'
|
20
|
+
#
|
21
|
+
# Prefix all calls to the library with <tt>gem.</tt>
|
22
|
+
#
|
23
|
+
module Gem
|
24
|
+
|
25
|
+
# Default install command
|
26
|
+
#
|
27
|
+
# * doesn't install documentation
|
28
|
+
# * installs all required dependencies automatically.
|
29
|
+
#
|
30
|
+
GEM_INSTALL="gem install --no-rdoc --no-ri"
|
31
|
+
GEM_UPDATE=GEM_INSTALL.sub("install", "update")
|
32
|
+
|
33
|
+
# Upgrade the *gem* system to the latest version. Runs via *sudo*
|
34
|
+
def update_system
|
35
|
+
send(run_method, "#{GEM_UPDATE} --system")
|
36
|
+
end
|
37
|
+
|
38
|
+
# Updates all the installed gems to the latest version. Runs via *sudo*.
|
39
|
+
# Don't use this command if any of the gems require a version selection.
|
40
|
+
def upgrade
|
41
|
+
send(run_method, GEM_UPDATE)
|
42
|
+
end
|
43
|
+
|
44
|
+
# Removes old versions of gems from installation area.
|
45
|
+
def cleanup
|
46
|
+
send(run_method, "gem cleanup")
|
47
|
+
end
|
48
|
+
|
49
|
+
# Installs the gems detailed in +packages+, selecting version +version+ if
|
50
|
+
# specified.
|
51
|
+
#
|
52
|
+
# +packages+ can be a single string or an array of strings.
|
53
|
+
#
|
54
|
+
def install(packages, version=nil)
|
55
|
+
send(run_method,"#{GEM_INSTALL} #{if version then '-v '+version.to_s end} #{Array(packages).join(' ')}")
|
56
|
+
end
|
57
|
+
|
58
|
+
# Auto selects a gem from a list and installs it.
|
59
|
+
#
|
60
|
+
# *gem* has no mechanism on the command line of disambiguating builds for
|
61
|
+
# different platforms, and instead asks the user. This method has the necessary
|
62
|
+
# conversation to select the +version+ relevant to +platform+ (or the one nearest
|
63
|
+
# the top of the list if you don't specify +version+).
|
64
|
+
def select(package, version=nil, platform='ruby')
|
65
|
+
selections={}
|
66
|
+
cmd="#{GEM_INSTALL} #{if version then '-v '+version.to_s end} #{package}"
|
67
|
+
send run_method, cmd do |channel, stream, data|
|
68
|
+
data.each_line do | line |
|
69
|
+
case line
|
70
|
+
when /\s(\d+).*\(#{platform}\)/
|
71
|
+
if selections[channel[:host]].nil?
|
72
|
+
selections[channel[:host]]=$1.dup+"\n"
|
73
|
+
logger.info "Selecting #$&", "#{stream} :: #{channel[:host]}"
|
74
|
+
end
|
75
|
+
when /\s\d+\./
|
76
|
+
# Discard other selections from data stream
|
77
|
+
when /^>/
|
78
|
+
channel.send_data selections[channel[:host]]
|
79
|
+
logger.debug line, "#{stream} :: #{channel[:host]}"
|
80
|
+
else
|
81
|
+
logger.info line, "#{stream} :: #{channel[:host]}"
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
end
|
88
|
+
|
89
|
+
Capistrano.plugin :gem2, Gem
|
90
|
+
# vim: nowrap sw=2 sts=2 ts=8 ff=unix ft=ruby:
|
@@ -0,0 +1,203 @@
|
|
1
|
+
# =std.rb: Capistrano Standard Methods
|
2
|
+
# Standard library of procedures and functions that you can use with Capistrano.
|
3
|
+
#
|
4
|
+
# ----
|
5
|
+
# Copyright (c) 2007 Neil Wilson, Aldur Systems Ltd
|
6
|
+
#
|
7
|
+
# Licensed under the GNU Public License v2. No warranty is provided.
|
8
|
+
|
9
|
+
require 'capistrano'
|
10
|
+
|
11
|
+
# = Purpose
|
12
|
+
# Std is a Capistrano plugin that provides a set of standard methods refactored
|
13
|
+
# out of several Capistrano task libraries.
|
14
|
+
#
|
15
|
+
# Installs within Capistrano as the plugin _std_
|
16
|
+
#
|
17
|
+
# = Usage
|
18
|
+
#
|
19
|
+
# require 'vmbuilder_plugins/std'
|
20
|
+
#
|
21
|
+
# Prefix all calls to the library with <tt>std.</tt>
|
22
|
+
module Std
|
23
|
+
|
24
|
+
begin
|
25
|
+
# Use the Mmap class if it is available
|
26
|
+
# http://moulon.inra.fr/ruby/mmap.html
|
27
|
+
require 'mmap'
|
28
|
+
MMAP=true #:nodoc:
|
29
|
+
rescue LoadError
|
30
|
+
# no MMAP class, use normal reads instead
|
31
|
+
MMAP=false #:nodoc:
|
32
|
+
end
|
33
|
+
|
34
|
+
# Copies the files specified by +file_pattern+ to +destination+
|
35
|
+
#
|
36
|
+
# Error checking is minimal - a pattern onto a single file will result in +destination+
|
37
|
+
# containing the data from the last file only.
|
38
|
+
#
|
39
|
+
# Installs via *sudo*, +options+ are as for *put*.
|
40
|
+
def fput(file_pattern, destination, options={})
|
41
|
+
logger.info file_pattern
|
42
|
+
Dir.glob(file_pattern) do |fname|
|
43
|
+
if File.readable?(fname) then
|
44
|
+
if MMAP
|
45
|
+
logger.debug "Using Memory Mapped File Upload"
|
46
|
+
fdata=Mmap.new(fname,"r", Mmap::MAP_SHARED, :advice => Mmap::MADV_SEQUENTIAL)
|
47
|
+
else
|
48
|
+
fdata=File.open(fname).read
|
49
|
+
end
|
50
|
+
su_put(fdata, destination, File.join('/tmp',File.basename(fname)), options)
|
51
|
+
else
|
52
|
+
logger.error "Unable to read file #{fname}"
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
# Upload +data+ to +temporary_area+ before installing it in
|
58
|
+
# +destination+ using sudo.
|
59
|
+
#
|
60
|
+
# +options+ are as for *put*
|
61
|
+
#
|
62
|
+
def su_put(data, destination, temporary_area='/tmp', options={})
|
63
|
+
temporary_area = File.join(temporary_area,"#{File.basename(destination)}-$CAPISTRANO:HOST$")
|
64
|
+
put(data, temporary_area, options)
|
65
|
+
send run_method, <<-CMD
|
66
|
+
sh -c "install -m#{sprintf("%3o",options[:mode]||0755)} #{temporary_area} #{destination} &&
|
67
|
+
rm -f #{temporary_area}"
|
68
|
+
CMD
|
69
|
+
end
|
70
|
+
|
71
|
+
# Copies the +file_pattern+, which is assumed to be a tar
|
72
|
+
# file of some description (gzipped or plain), and unpacks it into
|
73
|
+
# +destination+.
|
74
|
+
def unzip(file_pattern, destination, options={})
|
75
|
+
Dir.glob(file_pattern) do |fname|
|
76
|
+
if File.readable?(fname) then
|
77
|
+
target="/tmp/#{File.basename(fname)}"
|
78
|
+
if MMAP
|
79
|
+
logger.debug "Using Memory Mapped File Upload"
|
80
|
+
fdata=Mmap.new(fname,"r", Mmap::MAP_SHARED, :advice => Mmap::MADV_SEQUENTIAL)
|
81
|
+
else
|
82
|
+
fdata=File.open(fname).read
|
83
|
+
end
|
84
|
+
put(fdata, target, options)
|
85
|
+
send run_method, <<-CMD
|
86
|
+
sh -c "cd #{destination} &&
|
87
|
+
zcat -f #{target} | tar xvf - &&
|
88
|
+
rm -f #{target}"
|
89
|
+
CMD
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
# Wrap this around your task calls to catch the no servers error and
|
95
|
+
# ignore it
|
96
|
+
#
|
97
|
+
# std.ignore_no_servers_error do
|
98
|
+
# activate_mysql
|
99
|
+
# end
|
100
|
+
#
|
101
|
+
def ignore_no_servers_error (&block)
|
102
|
+
begin
|
103
|
+
yield
|
104
|
+
rescue RuntimeError => failure
|
105
|
+
if failure.message =~ /no servers matched/
|
106
|
+
logger.debug "Ignoring 'no servers matched' error in task #{current_task.name}"
|
107
|
+
else
|
108
|
+
raise
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
# Wrap this around your task to force a connection as root.
|
114
|
+
# Flushes the session cache before and after the connection.
|
115
|
+
#
|
116
|
+
# std.connect_as_root do
|
117
|
+
# install_sudo
|
118
|
+
# end
|
119
|
+
#
|
120
|
+
def connect_as_root (&block)
|
121
|
+
begin
|
122
|
+
tempuser = user
|
123
|
+
set :user, "root"
|
124
|
+
actor.sessions.delete_if { true }
|
125
|
+
yield tempuser
|
126
|
+
ensure
|
127
|
+
set :user, tempuser if tempuser
|
128
|
+
actor.sessions.delete_if { true }
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
#Returns a random string of alphanumeric characters of size +size+
|
133
|
+
#Useful for passwords, usernames and the like.
|
134
|
+
def random_string(size=10)
|
135
|
+
s = ""
|
136
|
+
size.times { s << (i = rand(62); i += ((i < 10) ? 48 : ((i < 36) ? 55 : 61 ))).chr }
|
137
|
+
s
|
138
|
+
end
|
139
|
+
|
140
|
+
|
141
|
+
# Return a relative path from the destination directory +from_str+
|
142
|
+
# to the target file/directory +to_str+. Used to create relative
|
143
|
+
# symbolic link paths.
|
144
|
+
def relative_path (from_str, to_str)
|
145
|
+
require 'pathname'
|
146
|
+
Pathname.new(to_str).relative_path_from(Pathname.new(from_str)).to_s
|
147
|
+
end
|
148
|
+
|
149
|
+
# Run a ruby command file on the servers
|
150
|
+
#
|
151
|
+
def ruby(cmd, options={}, &block)
|
152
|
+
temp_name = random_string + ".rb"
|
153
|
+
begin
|
154
|
+
put(cmd, temp_name, :mode => 0700)
|
155
|
+
send(run_method, "ruby #{temp_name}", options, &block)
|
156
|
+
ensure
|
157
|
+
delete temp_name
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
# Run a patchfile on the servers
|
162
|
+
# Ignores reverses and rejects.
|
163
|
+
#
|
164
|
+
def patch(patchfile, level = '0', where = '/')
|
165
|
+
temp_name = random_string
|
166
|
+
begin
|
167
|
+
fput(patchfile, temp_name, :mode => 0600)
|
168
|
+
send(run_method, %{
|
169
|
+
patch -p#{level} -tNd #{where} -r /dev/null < #{temp_name} || true
|
170
|
+
})
|
171
|
+
ensure
|
172
|
+
delete temp_name
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
# Deletes the given file(s) from all servers targetted by the current
|
177
|
+
# task, but runs the +delete+ command according to the current setting
|
178
|
+
# of <tt>:use_sudo</tt>.
|
179
|
+
#
|
180
|
+
# If <tt>:recursive => true</tt> is specified, it may be used to remove
|
181
|
+
# directories.
|
182
|
+
def su_delete(path, options={})
|
183
|
+
cmd = "rm -%sf #{path}" % (options[:recursive] ? "r" : "")
|
184
|
+
send(run_method, cmd, options)
|
185
|
+
end
|
186
|
+
|
187
|
+
# Render a template file and upload it to the servers
|
188
|
+
#
|
189
|
+
def put_template(template, destination, options={})
|
190
|
+
if MMAP
|
191
|
+
logger.debug "Using Memory Mapped File Upload"
|
192
|
+
fdata=Mmap.new(template,"r", Mmap::MAP_SHARED, :advice => Mmap::MADV_SEQUENTIAL)
|
193
|
+
else
|
194
|
+
fdata=File.read(template)
|
195
|
+
end
|
196
|
+
put(render(:template => fdata), destination, options)
|
197
|
+
end
|
198
|
+
|
199
|
+
end
|
200
|
+
|
201
|
+
Capistrano.plugin :std, Std
|
202
|
+
#
|
203
|
+
# vim: nowrap sw=2 sts=2 ts=8 ff=unix ft=ruby:
|
metadata
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
name: deprec-core
|
3
3
|
version: !ruby/object:Gem::Version
|
4
4
|
prerelease:
|
5
|
-
version: 3.1.
|
5
|
+
version: 3.1.1
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Mike Bailey
|
@@ -25,10 +25,17 @@ extra_rdoc_files: []
|
|
25
25
|
files:
|
26
26
|
- .gitignore
|
27
27
|
- Gemfile
|
28
|
+
- README.md
|
28
29
|
- Rakefile
|
29
30
|
- deprec-core.gemspec
|
30
31
|
- lib/deprec-core.rb
|
32
|
+
- lib/deprec-core/capistrano_extensions.rb
|
31
33
|
- lib/deprec-core/version.rb
|
34
|
+
- lib/vmbuilder_plugins/all.rb
|
35
|
+
- lib/vmbuilder_plugins/apt.rb
|
36
|
+
- lib/vmbuilder_plugins/emerge.rb
|
37
|
+
- lib/vmbuilder_plugins/gem.rb
|
38
|
+
- lib/vmbuilder_plugins/std.rb
|
32
39
|
homepage: ""
|
33
40
|
licenses: []
|
34
41
|
|