deprec-core 3.1.0 → 3.1.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
|