syncwrap 1.5.2 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/History.rdoc +19 -0
- data/Manifest.txt +82 -34
- data/README.rdoc +96 -48
- data/Rakefile +0 -65
- data/bin/syncwrap +27 -0
- data/examples/LAYOUT.rdoc +70 -0
- data/examples/Rakefile +16 -0
- data/examples/ec2.rb +44 -0
- data/examples/hello.rb +14 -0
- data/examples/hello_binding.rb +27 -0
- data/examples/jruby.rb +11 -0
- data/examples/private/aws.json +4 -0
- data/examples/rput.rb +24 -0
- data/examples/sync/home/bob/.ssh/authorized_keys +1 -0
- data/examples/sync/tmp/sample.erb +3 -0
- data/lib/syncwrap/amazon_ec2.rb +236 -0
- data/lib/syncwrap/amazon_ws.rb +308 -0
- data/lib/syncwrap/base.rb +4 -2
- data/lib/syncwrap/cli.rb +328 -0
- data/lib/syncwrap/component.rb +443 -0
- data/lib/syncwrap/components/commercial_jdk.rb +76 -0
- data/lib/syncwrap/components/cruby_vm.rb +144 -0
- data/lib/syncwrap/components/etc_hosts.rb +44 -0
- data/lib/syncwrap/{geminabox.rb → components/geminabox.rb} +12 -17
- data/lib/syncwrap/components/hashdot.rb +97 -0
- data/lib/syncwrap/components/iyyov.rb +144 -0
- data/lib/syncwrap/components/iyyov_daemon.rb +125 -0
- data/lib/syncwrap/components/jruby_vm.rb +122 -0
- data/lib/syncwrap/components/mdraid.rb +204 -0
- data/lib/syncwrap/components/network.rb +99 -0
- data/lib/syncwrap/components/open_jdk.rb +70 -0
- data/lib/syncwrap/components/postgresql.rb +159 -0
- data/lib/syncwrap/components/qpid.rb +303 -0
- data/lib/syncwrap/components/rhel.rb +71 -0
- data/lib/syncwrap/components/run_user.rb +99 -0
- data/lib/syncwrap/components/ubuntu.rb +85 -0
- data/lib/syncwrap/components/users.rb +200 -0
- data/lib/syncwrap/context.rb +260 -0
- data/lib/syncwrap/distro.rb +53 -60
- data/lib/syncwrap/formatter.rb +149 -0
- data/lib/syncwrap/host.rb +134 -0
- data/lib/syncwrap/main.rb +62 -0
- data/lib/syncwrap/path_util.rb +55 -0
- data/lib/syncwrap/rsync.rb +227 -0
- data/lib/syncwrap/ruby_support.rb +110 -0
- data/lib/syncwrap/shell.rb +207 -0
- data/lib/syncwrap.rb +367 -1
- data/{etc → sync/etc}/gemrc +1 -3
- data/sync/etc/hosts.erb +8 -0
- data/{etc/init.d/iyyov → sync/etc/init.d/iyyov.erb} +35 -7
- data/sync/etc/sysconfig/pgsql/postgresql.erb +2 -0
- data/sync/src/hashdot/Makefile.erb +98 -0
- data/sync/src/hashdot/profiles/default.hdp.erb +25 -0
- data/sync/src/hashdot/profiles/jruby-common.hdp +28 -0
- data/sync/src/hashdot/profiles/jruby-shortlived.hdp +9 -0
- data/sync/src/hashdot/profiles/jruby.hdp.erb +13 -0
- data/sync/src/hashdot/profiles/shortlived.hdp +6 -0
- data/sync/var/iyyov/default/config.rb +1 -0
- data/sync/var/iyyov/default/daemon.rb.erb +15 -0
- data/sync/var/iyyov/jobs.rb.erb +4 -0
- data/test/muddled_sync.rb +13 -0
- data/test/setup.rb +39 -0
- data/test/sync/d1/bar +1 -0
- data/test/sync/d1/foo.erb +1 -0
- data/test/sync/d3/d2/bar +1 -0
- data/test/sync/d3/d2/foo.erb +1 -0
- data/test/test_components.rb +108 -0
- data/test/test_context.rb +107 -0
- data/test/test_context_rput.rb +289 -0
- data/test/test_rsync.rb +138 -0
- data/test/test_shell.rb +233 -0
- data/test/test_space.rb +218 -0
- data/test/test_space_main.rb +40 -0
- data/test/zfile +1 -0
- metadata +204 -71
- data/etc/sysconfig/pgsql/postgresql +0 -2
- data/lib/syncwrap/aws.rb +0 -448
- data/lib/syncwrap/common.rb +0 -161
- data/lib/syncwrap/ec2.rb +0 -59
- data/lib/syncwrap/hashdot.rb +0 -70
- data/lib/syncwrap/iyyov.rb +0 -139
- data/lib/syncwrap/java.rb +0 -61
- data/lib/syncwrap/jruby.rb +0 -118
- data/lib/syncwrap/postgresql.rb +0 -135
- data/lib/syncwrap/qpid.rb +0 -251
- data/lib/syncwrap/remote_task.rb +0 -199
- data/lib/syncwrap/rhel.rb +0 -67
- data/lib/syncwrap/ubuntu.rb +0 -78
- data/lib/syncwrap/user_run.rb +0 -102
- data/test/test_syncwrap.rb +0 -202
- data/var/iyyov/jobs.rb +0 -11
- /data/{etc → sync/etc}/corosync/corosync.conf +0 -0
- /data/{etc → sync/etc}/corosync/uidgid.d/qpid +0 -0
- /data/{etc → sync/etc}/init.d/qpidd +0 -0
- /data/{etc → sync/etc}/sysctl.d/61-postgresql-shm.conf +0 -0
- /data/{usr/local → sync/jruby}/bin/jgem +0 -0
- /data/{postgresql → sync/postgresql}/rhel/pg_hba.conf +0 -0
- /data/{postgresql → sync/postgresql}/rhel/pg_ident.conf +0 -0
- /data/{postgresql → sync/postgresql}/rhel/postgresql.conf +0 -0
- /data/{postgresql → sync/postgresql}/ubuntu/environment +0 -0
- /data/{postgresql → sync/postgresql}/ubuntu/pg_ctl.conf +0 -0
- /data/{postgresql → sync/postgresql}/ubuntu/pg_hba.conf +0 -0
- /data/{postgresql → sync/postgresql}/ubuntu/pg_ident.conf +0 -0
- /data/{postgresql → sync/postgresql}/ubuntu/postgresql.conf +0 -0
- /data/{postgresql → sync/postgresql}/ubuntu/start.conf +0 -0
- /data/{usr → sync/usr}/local/etc/qpidd.conf +0 -0
@@ -0,0 +1,149 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (c) 2011-2014 David Kellum
|
3
|
+
#
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License"); you
|
5
|
+
# may not use this file except in compliance with the License. You
|
6
|
+
# may obtain a copy of the License at
|
7
|
+
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
13
|
+
# implied. See the License for the specific language governing
|
14
|
+
# permissions and limitations under the License.
|
15
|
+
#++
|
16
|
+
|
17
|
+
require 'thread'
|
18
|
+
require 'term/ansicolor'
|
19
|
+
|
20
|
+
module SyncWrap
|
21
|
+
|
22
|
+
class Formatter
|
23
|
+
include Term::ANSIColor
|
24
|
+
|
25
|
+
attr_reader :io
|
26
|
+
attr_reader :lock
|
27
|
+
attr_accessor :colorize
|
28
|
+
|
29
|
+
def initialize( io = $stdout )
|
30
|
+
@io = io
|
31
|
+
@lock = Mutex.new
|
32
|
+
@colorize = true
|
33
|
+
@newlined = true
|
34
|
+
@backtraces = {}
|
35
|
+
end
|
36
|
+
|
37
|
+
def sync( &block )
|
38
|
+
@lock.synchronize( &block )
|
39
|
+
end
|
40
|
+
|
41
|
+
def write_component( host, comp, mth, state )
|
42
|
+
io << yellow if colorize
|
43
|
+
io << '== ' << host.name << ' ' << comp.class << '#' << mth
|
44
|
+
io << ': ' << state
|
45
|
+
io << clear if colorize
|
46
|
+
io << "\n"
|
47
|
+
flush
|
48
|
+
end
|
49
|
+
|
50
|
+
def write_header( host, mode, opts, live = false )
|
51
|
+
olist = []
|
52
|
+
olist << "-#{opts[:sh_verbose]}" if opts[:sh_verbose] && mode != :rsync
|
53
|
+
olist << 'coalesce' if opts[:coalesce]
|
54
|
+
olist << 'dryrun' if opts[:dryrun]
|
55
|
+
olist << "accept:#{opts[:accept].join ','}" if opts[:accept]
|
56
|
+
olist << "user:#{opts[:user]}" if opts[:user]
|
57
|
+
olist << 'live' if live
|
58
|
+
|
59
|
+
io << yellow if colorize
|
60
|
+
io << '<-- ' << mode << ' ' << host.name
|
61
|
+
first = true
|
62
|
+
olist.each do |li|
|
63
|
+
if first
|
64
|
+
io << ' ('
|
65
|
+
first = false
|
66
|
+
else
|
67
|
+
io << ' '
|
68
|
+
end
|
69
|
+
io << li
|
70
|
+
end
|
71
|
+
io << ')' unless olist.empty?
|
72
|
+
io << clear if colorize
|
73
|
+
io << "\n"
|
74
|
+
flush
|
75
|
+
end
|
76
|
+
|
77
|
+
def write_result( result )
|
78
|
+
io << yellow if colorize
|
79
|
+
io << '--> ' << result
|
80
|
+
io << clear if colorize
|
81
|
+
io << "\n"
|
82
|
+
flush
|
83
|
+
end
|
84
|
+
|
85
|
+
def write_command_outputs( outputs, color = true )
|
86
|
+
outputs.each do |stream, buff|
|
87
|
+
write_command_output( stream, buff, color )
|
88
|
+
end
|
89
|
+
output_terminate
|
90
|
+
flush
|
91
|
+
end
|
92
|
+
|
93
|
+
def output_terminate
|
94
|
+
unless @newlined
|
95
|
+
io.puts
|
96
|
+
@newlined = true
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def write_command_output( stream, buff, color = true )
|
101
|
+
unless buff.empty?
|
102
|
+
if stream == :err && colorize && color
|
103
|
+
io << red << buff << clear
|
104
|
+
else
|
105
|
+
io << buff
|
106
|
+
end
|
107
|
+
@newlined = ( buff[-1] == "\n" )
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def write_error( host, error, comp = nil, mth = nil )
|
112
|
+
bt = error.backtrace
|
113
|
+
bt_num = @backtraces[ bt ]
|
114
|
+
if bt_num
|
115
|
+
bt = nil
|
116
|
+
else
|
117
|
+
@backtraces[ bt ] = bt_num = @backtraces.length + 1
|
118
|
+
end
|
119
|
+
|
120
|
+
io << yellow if colorize
|
121
|
+
io << '== ' << host.name << ' '
|
122
|
+
io << comp.class << '#' << mth << ' ' if comp && mth
|
123
|
+
io << "error"
|
124
|
+
io << ", same stack as" unless bt
|
125
|
+
io << " [" << bt_num << "]:\n"
|
126
|
+
|
127
|
+
io << red if colorize
|
128
|
+
io << short_cn( error.class ) << ': ' << error.message << "\n"
|
129
|
+
if bt
|
130
|
+
bt.each do |line|
|
131
|
+
break if line =~ /execute_component'$/
|
132
|
+
io.puts line
|
133
|
+
end
|
134
|
+
end
|
135
|
+
io << clear if colorize
|
136
|
+
flush
|
137
|
+
end
|
138
|
+
|
139
|
+
def flush
|
140
|
+
io.flush
|
141
|
+
end
|
142
|
+
|
143
|
+
def short_cn( cls )
|
144
|
+
cls.name.sub(/^SyncWrap::/,'')
|
145
|
+
end
|
146
|
+
|
147
|
+
end
|
148
|
+
|
149
|
+
end
|
@@ -0,0 +1,134 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (c) 2011-2014 David Kellum
|
3
|
+
#
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License"); you
|
5
|
+
# may not use this file except in compliance with the License. You
|
6
|
+
# may obtain a copy of the License at
|
7
|
+
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
13
|
+
# implied. See the License for the specific language governing
|
14
|
+
# permissions and limitations under the License.
|
15
|
+
#++
|
16
|
+
|
17
|
+
module SyncWrap
|
18
|
+
|
19
|
+
# Represents various host (server, machine instance) metadata and
|
20
|
+
# serves as a container for #roles and #components.
|
21
|
+
class Host
|
22
|
+
|
23
|
+
# The space in which this host was constructed.
|
24
|
+
attr_reader :space
|
25
|
+
|
26
|
+
# Array of role Symbols or (direct) Component instances in the
|
27
|
+
# order added.
|
28
|
+
attr_reader :contents
|
29
|
+
|
30
|
+
attr_reader :props
|
31
|
+
|
32
|
+
def initialize( space, props = {} )
|
33
|
+
@space = space
|
34
|
+
@props = {}
|
35
|
+
merge_props( props )
|
36
|
+
@contents = [ :all ]
|
37
|
+
end
|
38
|
+
|
39
|
+
# Return the :name property.
|
40
|
+
def name
|
41
|
+
self[ :name ]
|
42
|
+
end
|
43
|
+
|
44
|
+
# Return the property by (Symbol) key
|
45
|
+
def []( key )
|
46
|
+
@props[ key ]
|
47
|
+
end
|
48
|
+
|
49
|
+
# Set property by (Symbol) key to value. Note that the :roles
|
50
|
+
# property key is supported here, but is effectively the same as
|
51
|
+
# #add( val ). Roles are only added, never removed.
|
52
|
+
def []=( key, val )
|
53
|
+
key = key.to_sym
|
54
|
+
if key == :roles
|
55
|
+
add( *val )
|
56
|
+
else
|
57
|
+
@props[ key.to_sym ] = val
|
58
|
+
end
|
59
|
+
val
|
60
|
+
end
|
61
|
+
|
62
|
+
# Merge properties. Note that the :roles property key is
|
63
|
+
# supported here, but is affectively the same as #add( val ).
|
64
|
+
def merge_props( opts )
|
65
|
+
opts.each do |key,val|
|
66
|
+
self[ key ] = val
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def to_h
|
71
|
+
@props.merge( roles: roles )
|
72
|
+
end
|
73
|
+
|
74
|
+
# Add any number of roles (by Symbol) or (direct) Component
|
75
|
+
# instances.
|
76
|
+
def add( *args )
|
77
|
+
args.each do |arg|
|
78
|
+
case( arg )
|
79
|
+
when Symbol
|
80
|
+
@contents << arg unless @contents.include?( arg )
|
81
|
+
when Component
|
82
|
+
@contents << arg
|
83
|
+
else
|
84
|
+
raise "Invalid host #{name} addition: #{arg.inspect}"
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
# Return an Array of previously added role symbols.
|
90
|
+
def roles
|
91
|
+
@contents.select { |c| c.is_a?( Symbol ) }
|
92
|
+
end
|
93
|
+
|
94
|
+
# Return a flat Array of Component instances by traversing
|
95
|
+
# previously added roles and any direct components in order.
|
96
|
+
def components
|
97
|
+
@contents.inject([]) do |m,c|
|
98
|
+
if c.is_a?( Symbol )
|
99
|
+
m += space.role( c )
|
100
|
+
else
|
101
|
+
m << c
|
102
|
+
end
|
103
|
+
m
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
# Return the last component added to this Host prior to the given
|
108
|
+
# component (either directly or via a role), or nil if there is no
|
109
|
+
# such component.
|
110
|
+
def prior_component( component )
|
111
|
+
last = nil
|
112
|
+
@contents.each do |c|
|
113
|
+
if c.is_a?( Symbol )
|
114
|
+
space.role( c ).each do |rc|
|
115
|
+
return last if rc.equal?( component ) #identity
|
116
|
+
last = rc
|
117
|
+
end
|
118
|
+
else
|
119
|
+
return last if c.equal?( component ) #identity
|
120
|
+
last = c
|
121
|
+
end
|
122
|
+
end
|
123
|
+
nil
|
124
|
+
end
|
125
|
+
|
126
|
+
# Return the _last_ component which is a kind of the specified
|
127
|
+
# Class or Module clz, or nil if not found.
|
128
|
+
def component( clz )
|
129
|
+
components.reverse.find { |c| c.kind_of?( clz ) }
|
130
|
+
end
|
131
|
+
|
132
|
+
end
|
133
|
+
|
134
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (c) 2011-2014 David Kellum
|
3
|
+
#
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License"); you
|
5
|
+
# may not use this file except in compliance with the License. You may
|
6
|
+
# obtain a copy of the License at
|
7
|
+
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
13
|
+
# implied. See the License for the specific language governing
|
14
|
+
# permissions and limitations under the License.
|
15
|
+
#++
|
16
|
+
|
17
|
+
require 'syncwrap'
|
18
|
+
|
19
|
+
module SyncWrap
|
20
|
+
|
21
|
+
# A limited set of (private) methods for use at the top-level in
|
22
|
+
# a sync.rb. All of these methods delegate to the _current_
|
23
|
+
# Space.
|
24
|
+
module Main
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
# The current Space
|
29
|
+
def space # :doc:
|
30
|
+
Space.current
|
31
|
+
end
|
32
|
+
|
33
|
+
# Shorthand for space.role
|
34
|
+
def role( *args ) # :doc:
|
35
|
+
space.role( *args )
|
36
|
+
end
|
37
|
+
|
38
|
+
# Shorthand for space.role
|
39
|
+
def host( *args ) # :doc:
|
40
|
+
space.host( *args )
|
41
|
+
end
|
42
|
+
|
43
|
+
# Merge options given, or (without opts) return space.default_options
|
44
|
+
def options( opts = nil ) # :doc:
|
45
|
+
if opts
|
46
|
+
space.merge_default_options( opts )
|
47
|
+
else
|
48
|
+
space.default_options
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
# Shorthand for space.provider.profile
|
53
|
+
def profile( *args ) # :doc:
|
54
|
+
space.provider.profile( *args )
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
|
61
|
+
# Extend the top level Object with the Main module
|
62
|
+
self.extend SyncWrap::Main
|
@@ -0,0 +1,55 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (c) 2011-2014 David Kellum
|
3
|
+
#
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License"); you
|
5
|
+
# may not use this file except in compliance with the License. You may
|
6
|
+
# obtain a copy of the License at
|
7
|
+
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
13
|
+
# implied. See the License for the specific language governing
|
14
|
+
# permissions and limitations under the License.
|
15
|
+
#++
|
16
|
+
|
17
|
+
require 'pathname'
|
18
|
+
|
19
|
+
module SyncWrap
|
20
|
+
|
21
|
+
# Utility methods for handling paths.
|
22
|
+
module PathUtil
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def caller_path( clr )
|
27
|
+
clr.first =~ /^([^:]+):/ && File.dirname( $1 )
|
28
|
+
end
|
29
|
+
|
30
|
+
# Unless rpath is already absolute, expand it relative to the
|
31
|
+
# calling file, as computed from passed in clr (use your
|
32
|
+
# Kernel#caller)
|
33
|
+
def path_relative_to_caller( rpath, clr ) # :doc:
|
34
|
+
unless rpath =~ %r{^/}
|
35
|
+
from = caller_path( clr )
|
36
|
+
rpath = File.expand_path( rpath, from ) if from
|
37
|
+
end
|
38
|
+
rpath
|
39
|
+
end
|
40
|
+
|
41
|
+
# Return path relative to PWD if the result is shorter, otherwise
|
42
|
+
# return input path. Preserves any trailing '/'.
|
43
|
+
def relativize( path ) # :doc:
|
44
|
+
p = Pathname.new( path )
|
45
|
+
unless p.relative?
|
46
|
+
p = p.relative_path_from( Pathname.pwd ).to_s
|
47
|
+
p += '/' if path[-1] == '/'
|
48
|
+
path = p if p.length < path.length
|
49
|
+
end
|
50
|
+
path
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
@@ -0,0 +1,227 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (c) 2011-2014 David Kellum
|
3
|
+
#
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License"); you
|
5
|
+
# may not use this file except in compliance with the License. You may
|
6
|
+
# obtain a copy of the License at
|
7
|
+
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
13
|
+
# implied. See the License for the specific language governing
|
14
|
+
# permissions and limitations under the License.
|
15
|
+
#++
|
16
|
+
|
17
|
+
require 'tmpdir'
|
18
|
+
require 'erb'
|
19
|
+
require 'fileutils'
|
20
|
+
|
21
|
+
require 'syncwrap/path_util'
|
22
|
+
|
23
|
+
module SyncWrap
|
24
|
+
|
25
|
+
# Low level support for rsync command construction and template
|
26
|
+
# processing.
|
27
|
+
module Rsync
|
28
|
+
include PathUtil
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def rsync_args( host, srcs, target, opts = {} ) # :doc:
|
33
|
+
|
34
|
+
# -i --itemize-changes, used for counting changed files
|
35
|
+
flags = %w[ -i ]
|
36
|
+
|
37
|
+
# -r --recursive
|
38
|
+
flags << '-r' unless opts[:recursive] == false
|
39
|
+
|
40
|
+
# -l --links (recreate symlinks on the destination)
|
41
|
+
flags << '-l' unless opts[:links] == false
|
42
|
+
|
43
|
+
# -p --perms (set destination to source permissions)
|
44
|
+
# -E --executability (perserve execute only, default)
|
45
|
+
if opts[:perms] != false
|
46
|
+
if opts[:perms] == :p || opts[:perms].is_a?( String )
|
47
|
+
flags << '-p'
|
48
|
+
if opts[ :perms ].is_a?( String )
|
49
|
+
flags << "--chmod=#{opts[ :perms ]}"
|
50
|
+
end
|
51
|
+
else
|
52
|
+
flags << '-E'
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
# -c --checksum (to determine changes; not just size,time)
|
57
|
+
flags << '-c' unless opts[:checksum] == false
|
58
|
+
|
59
|
+
# -b --backup (make backups)
|
60
|
+
flags << '-b' unless opts[:backup] == false
|
61
|
+
|
62
|
+
# Pass ssh options via -e (--rsh) flag
|
63
|
+
ssh_flags = []
|
64
|
+
ssh_flags += opts[ :ssh_flags ] if opts[ :ssh_flags ]
|
65
|
+
if opts[ :ssh_options ]
|
66
|
+
ssh_flags += opts[:ssh_options].map { |o| ['-o', o.join('=')] }.flatten
|
67
|
+
end
|
68
|
+
if opts[ :ssh_user ]
|
69
|
+
ssh_flags += [ '-l', opts[ :ssh_user ] ]
|
70
|
+
ssh_flags += [ '-i', opts[ :ssh_user_pem ] ] if opts[ :ssh_user_pem ]
|
71
|
+
end
|
72
|
+
flags += [ '-e', "ssh #{ssh_flags.join ' '}" ] unless ssh_flags.empty?
|
73
|
+
|
74
|
+
if opts[ :user ]
|
75
|
+
# Use sudo to place files at remote.
|
76
|
+
user = opts[ :user ].to_s
|
77
|
+
flags << ( if user != 'root'
|
78
|
+
"--rsync-path=sudo -u #{user} rsync"
|
79
|
+
else
|
80
|
+
"--rsync-path=sudo rsync"
|
81
|
+
end )
|
82
|
+
end
|
83
|
+
|
84
|
+
excludes = Array( opts[ :excludes ] )
|
85
|
+
flags += excludes.map do |e|
|
86
|
+
if e == :dev
|
87
|
+
'--cvs-exclude'
|
88
|
+
else
|
89
|
+
"--exclude=#{e}"
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
flags << '-n' if opts[ :dryrun ]
|
94
|
+
|
95
|
+
target = [ host, target ].join(':') unless host == 'localhost'
|
96
|
+
|
97
|
+
[ 'rsync', flags, srcs, target ].flatten.compact
|
98
|
+
end
|
99
|
+
|
100
|
+
def expand_implied_target( srcs ) # :doc:
|
101
|
+
#FIXME: Honor absolute arg paths?
|
102
|
+
if srcs.length == 1
|
103
|
+
target = "/" + srcs.first
|
104
|
+
target = File.dirname( target ) + '/' unless target =~ %r{/$}
|
105
|
+
else
|
106
|
+
target = srcs.pop
|
107
|
+
end
|
108
|
+
[ srcs, target ]
|
109
|
+
end
|
110
|
+
|
111
|
+
# Resolves each srcs path via #resolve_source! Raises SourceNotFound
|
112
|
+
# if any not found.
|
113
|
+
def resolve_sources( srcs, sync_paths ) # :doc:
|
114
|
+
srcs.map { |path| resolve_source!( path, sync_paths ) }
|
115
|
+
end
|
116
|
+
|
117
|
+
# Resolve the specified source path via #resolve_source. Raises
|
118
|
+
# SourceNotFound if not found.
|
119
|
+
def resolve_source!( path, sync_paths ) # :doc:
|
120
|
+
resolve_source( path, sync_paths ) or
|
121
|
+
raise( SourceNotFound,
|
122
|
+
"#{path.inspect} not found in :sync_paths #{sync_paths.inspect}" )
|
123
|
+
end
|
124
|
+
|
125
|
+
# Resolve the specified source path to the first existing
|
126
|
+
# file/directory in sync_paths roots and returns a #relativize
|
127
|
+
# path. Also tries with an .erb suffix if a src does not have a
|
128
|
+
# trailing '/'. Preserves any trailing '/'. Returns nil if not
|
129
|
+
# found.
|
130
|
+
def resolve_source( path, sync_paths ) # :doc:
|
131
|
+
#FIXME: Honor absolute arg paths?
|
132
|
+
found = nil
|
133
|
+
sync_paths.each do |r|
|
134
|
+
candidate = File.join( r, path )
|
135
|
+
if File.exist?( candidate )
|
136
|
+
found = candidate
|
137
|
+
elsif candidate !~ %r{(\.erb|/)$}
|
138
|
+
candidate += '.erb'
|
139
|
+
if File.exist?( candidate )
|
140
|
+
found = candidate
|
141
|
+
end
|
142
|
+
end
|
143
|
+
break if found
|
144
|
+
end
|
145
|
+
|
146
|
+
found && relativize( found )
|
147
|
+
end
|
148
|
+
|
149
|
+
# Given file path within src, return any sub-directory path needed
|
150
|
+
# to reach file, or the empty string. This is also src trailing
|
151
|
+
# '/' aware.
|
152
|
+
def subpath( src, file ) # :doc:
|
153
|
+
src = src.sub( %r{/[^/]*$}, '' ) #remove trail slash or last element
|
154
|
+
File.dirname( file ).sub( /^#{src}\/?/, '' )
|
155
|
+
end
|
156
|
+
|
157
|
+
# Process templates in tmpdir and yield post-processed sources to
|
158
|
+
# block, cleaning up on exit.
|
159
|
+
def process_templates( srcs, opts ) # :doc:
|
160
|
+
bnd = opts[ :erb_binding ] or raise "required :erb_binding param missing"
|
161
|
+
erb_mode = opts[ :erb_mode ] || '<>' #Trim new line on "<% ... %>\n"
|
162
|
+
mktmpdir( 'syncwrap' ) do |tmp_dir|
|
163
|
+
processed_sources = []
|
164
|
+
out_dir = File.join( tmp_dir, 'd' ) #for default perms
|
165
|
+
srcs.each do |src|
|
166
|
+
erbs = find_source_erbs( src )
|
167
|
+
outname = nil
|
168
|
+
erbs.each do |erb|
|
169
|
+
spath = subpath( src, erb )
|
170
|
+
outname = File.join( out_dir, spath, File.basename( erb, '.erb' ) )
|
171
|
+
FileUtils.mkdir_p( File.dirname( outname ) )
|
172
|
+
perm = File.stat( erb ).mode
|
173
|
+
File.open( outname, "w", perm ) do |fout|
|
174
|
+
template = ERB.new( IO.read( erb ), nil, erb_mode )
|
175
|
+
template.filename = erb
|
176
|
+
fout.puts( template.result( bnd ) )
|
177
|
+
end
|
178
|
+
end
|
179
|
+
if erbs.length == 1 && src == erbs.first
|
180
|
+
processed_sources << outname
|
181
|
+
elsif !erbs.empty?
|
182
|
+
processed_sources << ( out_dir + '/' )
|
183
|
+
end
|
184
|
+
end
|
185
|
+
yield processed_sources
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
# Just like Dir.mktmpdir but with an attempt to workaround a JRuby
|
190
|
+
# 1.6.x bug. See https://jira.codehaus.org/browse/JRUBY-5678
|
191
|
+
def mktmpdir( prefix ) # :doc:
|
192
|
+
old_env_tmpdir = nil
|
193
|
+
newdir = nil
|
194
|
+
if defined?( JRUBY_VERSION ) && JRUBY_VERSION =~ /^1.6/
|
195
|
+
old_env_tmpdir = ENV['TMPDIR']
|
196
|
+
newdir = "/tmp/syncwrap.#{ENV['USER']}"
|
197
|
+
FileUtils.mkdir_p( newdir, mode: 0700 )
|
198
|
+
ENV['TMPDIR'] = newdir
|
199
|
+
end
|
200
|
+
Dir.mktmpdir( 'syncwrap' ) do |tmp_dir|
|
201
|
+
yield tmp_dir
|
202
|
+
end
|
203
|
+
ensure
|
204
|
+
FileUtils.rmdir( newdir ) if newdir
|
205
|
+
ENV['TMPDIR'] = old_env_tmpdir if old_env_tmpdir
|
206
|
+
end
|
207
|
+
|
208
|
+
def find_source_erbs( sources ) # :doc:
|
209
|
+
Array( sources ).inject([]) do |list, src|
|
210
|
+
if File.directory?( src )
|
211
|
+
list += find_source_erbs( expand_entries( src ) )
|
212
|
+
elsif src =~ /\.erb$/
|
213
|
+
list << src
|
214
|
+
end
|
215
|
+
list
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
def expand_entries( src ) # :doc:
|
220
|
+
Dir.entries( src ).
|
221
|
+
reject { |e| e =~ /^\.+$/ }.
|
222
|
+
map { |e| File.join( src, e ) }
|
223
|
+
end
|
224
|
+
|
225
|
+
end
|
226
|
+
|
227
|
+
end
|