1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
|
require "rsync_command/version"
require "rsync_command/ssh_options"
require "rsync_command/thread_pool"
require 'monitor'
class RsyncCommand
attr_accessor :failures, :logger
def initialize(options={})
@options = options.dup
@logger = @options.delete(:logger)
@flags = @options.delete(:flags)
@failures = []
@failures.extend(MonitorMixin)
end
#
# takes an Enumerable and iterates each item in the list in parallel.
#
def asynchronously(array, &block)
pool = ThreadPool.new
array.each do |item|
pool.schedule(item, &block)
end
pool.shutdown
end
#
# runs rsync, recording failures
#
def exec(src, dest, options={})
@failures.synchronize do
@failures.clear
end
rsync_cmd = command(src, dest, options)
if options[:chdir]
rsync_cmd = "cd '#{options[:chdir]}'; #{rsync_cmd}"
end
@logger.debug rsync_cmd if @logger
ok = system(rsync_cmd)
unless ok
@failures.synchronize do
@failures << {:source => src, :dest => dest, :options => options.dup}
end
end
end
#
# returns true if last exec returned a failure
#
def failed?
@failures && @failures.any?
end
#
# build rsync command
#
def command(src, dest, options={})
src = remote_address(src)
dest = remote_address(dest)
options = @options.merge(options)
flags = []
flags << @flags if @flags
flags << options[:flags] if options.has_key?(:flags)
flags << '--delete' if options[:delete]
flags << includes(options[:includes]) if options.has_key?(:includes)
flags << excludes(options[:excludes]) if options.has_key?(:excludes)
flags << SshOptions.new(options[:ssh]).to_flags if options.has_key?(:ssh)
"rsync #{flags.compact.join(' ')} #{src} #{dest}"
end
private
#
# Creates an rsync location if the +address+ is a hash with keys :user, :host, and :path
# (each component is optional). If +address+ is a string, we just pass it through.
#
def remote_address(address)
if address.is_a? String
address # assume it is already formatted.
elsif address.is_a? Hash
[[address[:user], address[:host]].compact.join('@'), address[:path]].compact.join(':')
end
end
def excludes(patterns)
[patterns].flatten.compact.map { |p| "--exclude='#{p}'" }
end
def includes(patterns)
[patterns].flatten.compact.map { |p| "--include='#{p}'" }
end
end
|