syncwrap 1.5.2 → 2.0.0
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.
- 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
data/lib/syncwrap/aws.rb
DELETED
|
@@ -1,448 +0,0 @@
|
|
|
1
|
-
#--
|
|
2
|
-
# Copyright (c) 2011-2013 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 'json'
|
|
18
|
-
require 'aws-sdk'
|
|
19
|
-
require 'resolv'
|
|
20
|
-
|
|
21
|
-
require 'syncwrap/common'
|
|
22
|
-
|
|
23
|
-
# Supports host provisioning in EC2 via AWS APIs, creating and
|
|
24
|
-
# attaching EBS volumes, creating Route53 record sets, and as a remote
|
|
25
|
-
# task: building mdraid volumes.
|
|
26
|
-
#
|
|
27
|
-
# This module also includes a disk based cache of meta-data on created
|
|
28
|
-
# instances which allows automated role assignment (i.e. create an
|
|
29
|
-
# instance and run deploy tasks on it in a single pass.)
|
|
30
|
-
module SyncWrap::AWS
|
|
31
|
-
include SyncWrap::Common
|
|
32
|
-
|
|
33
|
-
# The json configuration file, parsed and passed directly to
|
|
34
|
-
# AWS::config method. This file should contain a json object with
|
|
35
|
-
# the minimal required keys: access_key_id, secret_access_key
|
|
36
|
-
# (default: ./private/aws.json)
|
|
37
|
-
attr_accessor :aws_config_json
|
|
38
|
-
|
|
39
|
-
# The cached aws instance json file (default: ./aws_instances.json)
|
|
40
|
-
attr_accessor :aws_instances_json
|
|
41
|
-
|
|
42
|
-
# Array of region strings to check for aws_import_instances
|
|
43
|
-
attr_accessor :aws_regions
|
|
44
|
-
|
|
45
|
-
# Array of imported or read instances represented as hashes.
|
|
46
|
-
attr_accessor :aws_instances
|
|
47
|
-
|
|
48
|
-
# Default options (which may be overridden) in call to
|
|
49
|
-
# aws_create_instance.
|
|
50
|
-
attr_accessor :aws_default_instance_options
|
|
51
|
-
|
|
52
|
-
# Default options (which may be overridden) in call to
|
|
53
|
-
# aws_create_security_group.
|
|
54
|
-
attr_accessor :aws_default_sg_options
|
|
55
|
-
|
|
56
|
-
# Default options for Route53 record set creation
|
|
57
|
-
attr_accessor :route53_default_rs_options
|
|
58
|
-
|
|
59
|
-
# DNS Resolver options for testing Route53 (default: Use public
|
|
60
|
-
# google name servers to avoid local negative caching)
|
|
61
|
-
attr_accessor :resolver_options
|
|
62
|
-
|
|
63
|
-
def initialize
|
|
64
|
-
@aws_config_json = 'private/aws.json'
|
|
65
|
-
@aws_regions = %w[ us-east-1 ]
|
|
66
|
-
@aws_instances_json = 'aws_instances.json'
|
|
67
|
-
@aws_instances = []
|
|
68
|
-
@aws_default_instance_options = {
|
|
69
|
-
:ebs_volumes => 0,
|
|
70
|
-
:ebs_volume_options => { :size => 16 }, #gb
|
|
71
|
-
:lvm_volumes => [ [ 1.00, '/data' ] ],
|
|
72
|
-
:security_groups => :default,
|
|
73
|
-
:instance_type => 'm1.medium',
|
|
74
|
-
:region => 'us-east-1'
|
|
75
|
-
}
|
|
76
|
-
@aws_default_sg_options = {
|
|
77
|
-
:region => 'us-east-1'
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
@route53_default_rs_options = {
|
|
81
|
-
:ttl => 300,
|
|
82
|
-
:wait => true
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
@resolver_options = {
|
|
86
|
-
:nameserver => [ '8.8.8.8', '8.8.4.4' ]
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
super
|
|
90
|
-
|
|
91
|
-
aws_configure if File.exist?( @aws_config_json )
|
|
92
|
-
aws_read_instances if File.exist?( @aws_instances_json )
|
|
93
|
-
end
|
|
94
|
-
|
|
95
|
-
def aws_configure
|
|
96
|
-
AWS.config( JSON.parse( IO.read( @aws_config_json ),
|
|
97
|
-
:symbolize_names => true ) )
|
|
98
|
-
end
|
|
99
|
-
|
|
100
|
-
# Create a security_group given name and options. :region is the
|
|
101
|
-
# only required option, :description is a good to have. Currently
|
|
102
|
-
# this is a no-op if the security group already exists.
|
|
103
|
-
def aws_create_security_group( name, opts = {} )
|
|
104
|
-
opts = deep_merge_hashes( @aws_default_sg_options, opts )
|
|
105
|
-
region = opts.delete( :region )
|
|
106
|
-
ec2 = AWS::EC2.new.regions[ region ]
|
|
107
|
-
unless ec2.security_groups.find { |sg| sg.name == name }
|
|
108
|
-
ec2.security_groups.create( name, opts )
|
|
109
|
-
end
|
|
110
|
-
end
|
|
111
|
-
|
|
112
|
-
# Create an instance, using name as the Name tag and assumed
|
|
113
|
-
# host name. For options see
|
|
114
|
-
# {AWS::EC2::InstanceCollection.create}[http://docs.aws.amazon.com/AWSRubySDK/latest/AWS/EC2/InstanceCollection.html#create-instance_method]
|
|
115
|
-
# with the following additions/differences:
|
|
116
|
-
#
|
|
117
|
-
# :count:: must be 1 or unspecified.
|
|
118
|
-
# :region:: Default 'us-east-1'
|
|
119
|
-
# :security_groups:: As per aws-sdk, but the special :default value
|
|
120
|
-
# is replaced with a single security group with
|
|
121
|
-
# same name as the :region option.
|
|
122
|
-
# :ebs_volumes:: The number of EBS volumes to create an attach to this instance.
|
|
123
|
-
# :ebs_volume_options:: A nested Hash of options, as per
|
|
124
|
-
# {AWS::EC2::VolumeCollection.create}[http://docs.aws.amazon.com/AWSRubySDK/latest/AWS/EC2/VolumeCollection.html#create-instance_method]
|
|
125
|
-
# with custom default :size 16 GB, and the same
|
|
126
|
-
# :availibility_zone as the instance.
|
|
127
|
-
# :lvm_volumes:: Ignored here.
|
|
128
|
-
# :roles:: Array of role Strings or Symbols (applied as Roles tag)
|
|
129
|
-
def aws_create_instance( name, opts = {} )
|
|
130
|
-
opts = deep_merge_hashes( @aws_default_instance_options, opts )
|
|
131
|
-
region = opts.delete( :region )
|
|
132
|
-
opts.delete( :lvm_volumes ) #unused here
|
|
133
|
-
|
|
134
|
-
ec2 = AWS::EC2.new.regions[ region ]
|
|
135
|
-
|
|
136
|
-
iopts = opts.dup
|
|
137
|
-
iopts.delete( :ebs_volumes )
|
|
138
|
-
iopts.delete( :ebs_volume_options )
|
|
139
|
-
iopts.delete( :roles )
|
|
140
|
-
|
|
141
|
-
if iopts[ :count ] && iopts[ :count ] != 1
|
|
142
|
-
raise ":count #{iopts[ :count ]} != 1 is not supported"
|
|
143
|
-
end
|
|
144
|
-
|
|
145
|
-
if iopts[ :security_groups ] == :default
|
|
146
|
-
iopts[ :security_groups ] = [ region ]
|
|
147
|
-
end
|
|
148
|
-
|
|
149
|
-
inst = ec2.instances.create( iopts )
|
|
150
|
-
|
|
151
|
-
inst.add_tag( 'Name', :value => name )
|
|
152
|
-
|
|
153
|
-
if opts[ :roles ]
|
|
154
|
-
inst.add_tag( 'Roles', :value => opts[ :roles ].join(' ') )
|
|
155
|
-
end
|
|
156
|
-
|
|
157
|
-
wait_for_running( inst )
|
|
158
|
-
|
|
159
|
-
if opts[ :ebs_volumes ] > 0
|
|
160
|
-
vopts = { :availability_zone => inst.availability_zone }.
|
|
161
|
-
merge( opts[ :ebs_volume_options ] )
|
|
162
|
-
|
|
163
|
-
attachments = opts[ :ebs_volumes ].times.map do |i|
|
|
164
|
-
vol = ec2.volumes.create( vopts )
|
|
165
|
-
wait_until( vol.id, 0.5 ) { vol.status == :available }
|
|
166
|
-
vol.attach_to( inst, "/dev/sdh#{i+1}" ) #=> Attachment
|
|
167
|
-
end
|
|
168
|
-
|
|
169
|
-
wait_until( "volumes to attach" ) do
|
|
170
|
-
!( attachments.any? { |a| a.status == :attaching } )
|
|
171
|
-
end
|
|
172
|
-
end
|
|
173
|
-
|
|
174
|
-
iprops = aws_instance_to_props( region, inst )
|
|
175
|
-
aws_instance_added( iprops )
|
|
176
|
-
aws_write_instances
|
|
177
|
-
iprops
|
|
178
|
-
end
|
|
179
|
-
|
|
180
|
-
# Create a Route53 DNS CNAME from iprops :name to :internet_name.
|
|
181
|
-
# Options are per {AWS::Route53::ResourceRecordSetCollection.create}[http://docs.aws.amazon.com/AWSRubySDK/latest/AWS/Route53/ResourceRecordSetCollection.html#create-instance_method]
|
|
182
|
-
# (currently undocumented) with the following addition:
|
|
183
|
-
#
|
|
184
|
-
# :domain_name:: name of the hosted zone and suffix for
|
|
185
|
-
# CNAME. Should terminate in a DOT '.'
|
|
186
|
-
# :wait:: If true, wait for CNAME to resolve
|
|
187
|
-
def route53_create_host_cname( iprops, opts = {} )
|
|
188
|
-
opts = deep_merge_hashes( @route53_default_rs_options, opts )
|
|
189
|
-
dname = dot_terminate( opts.delete( :domain_name ) )
|
|
190
|
-
do_wait = opts.delete( :wait )
|
|
191
|
-
rs_opts = opts.
|
|
192
|
-
merge( :resource_records => [ {:value => iprops[:internet_name]} ] )
|
|
193
|
-
|
|
194
|
-
r53 = AWS::Route53.new
|
|
195
|
-
zone = r53.hosted_zones.find { |hz| hz.name == dname } or
|
|
196
|
-
raise "Route53 Hosted Zone name #{dname} not found"
|
|
197
|
-
long_name = [ iprops[:name], dname ].join('.')
|
|
198
|
-
zone.rrsets.create( long_name, 'CNAME', rs_opts )
|
|
199
|
-
wait_for_dns_resolve( long_name, dname ) if do_wait
|
|
200
|
-
end
|
|
201
|
-
|
|
202
|
-
def wait_for_dns_resolve( long_name,
|
|
203
|
-
domain,
|
|
204
|
-
rtype = Resolv::DNS::Resource::IN::CNAME )
|
|
205
|
-
|
|
206
|
-
ns_addr = Resolv::DNS.open( @resolver_options ) do |rvr|
|
|
207
|
-
ns_n = rvr.getresource( domain, Resolv::DNS::Resource::IN::SOA ).mname
|
|
208
|
-
rvr.getaddress( ns_n ).to_s
|
|
209
|
-
end
|
|
210
|
-
|
|
211
|
-
sleep 3 # Initial wait
|
|
212
|
-
|
|
213
|
-
wait_until( "#{long_name} to resolve", 3.0 ) do
|
|
214
|
-
Resolv::DNS.open( :nameserver => ns_addr ) do |rvr|
|
|
215
|
-
rvr.getresources( long_name, rtype ).first
|
|
216
|
-
end
|
|
217
|
-
end
|
|
218
|
-
end
|
|
219
|
-
|
|
220
|
-
# Terminates an instance and permanently deletes any EBS volumes
|
|
221
|
-
# attached to /dev/sdh like via create. WARNING: potential for data
|
|
222
|
-
# loss! The minimum required propererties in iprops hash are :region
|
|
223
|
-
# and :id.
|
|
224
|
-
def aws_terminate_instance_and_ebs_volumes( iprops )
|
|
225
|
-
ec2 = AWS::EC2.new.regions[ iprops[ :region ] ]
|
|
226
|
-
inst = ec2.instances[ iprops[ :id ] ]
|
|
227
|
-
unless inst.exists?
|
|
228
|
-
raise "Instance #{iprops[:id]} does not exist in #{iprops[:region]}"
|
|
229
|
-
end
|
|
230
|
-
|
|
231
|
-
ebs_volumes = inst.block_devices.map do |dev|
|
|
232
|
-
ebs = dev[ :ebs ]
|
|
233
|
-
if ebs && dev[:device_name] =~ /dh\d+$/ && !ebs[:delete_on_termination]
|
|
234
|
-
ebs[ :volume_id ]
|
|
235
|
-
end
|
|
236
|
-
end.compact
|
|
237
|
-
|
|
238
|
-
inst.terminate
|
|
239
|
-
wait_until( "termination of #{inst.id}", 2.0 ) { inst.status == :terminated }
|
|
240
|
-
|
|
241
|
-
ebs_volumes = ebs_volumes.map do |vid|
|
|
242
|
-
volume = ec2.volumes[ vid ]
|
|
243
|
-
if volume.exists?
|
|
244
|
-
volume
|
|
245
|
-
else
|
|
246
|
-
puts "WARN: #{volume} doesn't exist"
|
|
247
|
-
nil
|
|
248
|
-
end
|
|
249
|
-
end.compact
|
|
250
|
-
|
|
251
|
-
ebs_volumes.each do |vol|
|
|
252
|
-
wait_until( "deletion of vol #{vol.id}" ) do
|
|
253
|
-
vol.status == :available || vol.status == :deleted
|
|
254
|
-
end
|
|
255
|
-
vol.delete if vol.status == :available
|
|
256
|
-
end
|
|
257
|
-
|
|
258
|
-
found = aws_find_instance( iprops )
|
|
259
|
-
if found
|
|
260
|
-
aws_instance_removed( found )
|
|
261
|
-
aws_write_instances
|
|
262
|
-
end
|
|
263
|
-
end
|
|
264
|
-
|
|
265
|
-
# Return instance properties, by example via iprops, either by [
|
|
266
|
-
# :id, :region ], :internet_name, :internet_ip, or :name, attempted
|
|
267
|
-
# in that order.
|
|
268
|
-
def aws_find_instance( iprops )
|
|
269
|
-
if iprops[:id]
|
|
270
|
-
@aws_instances.find do |r|
|
|
271
|
-
r[:id] == iprops[:id] && r[:region] == iprops[:region]
|
|
272
|
-
end
|
|
273
|
-
else
|
|
274
|
-
[ :internet_name, :internet_ip, :name ].inject( nil ) do |found, key|
|
|
275
|
-
found || @aws_instances.find { |r| r[key] == iprops[key] }
|
|
276
|
-
end
|
|
277
|
-
end
|
|
278
|
-
end
|
|
279
|
-
|
|
280
|
-
def aws_instance_added( inst )
|
|
281
|
-
@aws_instances << inst
|
|
282
|
-
@aws_instances.sort! { |p,n| p[:name] <=> n[:name] }
|
|
283
|
-
end
|
|
284
|
-
|
|
285
|
-
def aws_instance_removed( iprops )
|
|
286
|
-
@aws_instances.reject! do |row|
|
|
287
|
-
row[:id] == iprops[:id] && row[:region] == iprops[:region]
|
|
288
|
-
end
|
|
289
|
-
end
|
|
290
|
-
|
|
291
|
-
def wait_for_running( inst )
|
|
292
|
-
wait_until( "instance #{inst.id} to run", 2.0 ) { inst.status != :pending }
|
|
293
|
-
stat = inst.status
|
|
294
|
-
raise "Instance #{inst.id} has status #{stat}" unless stat == :running
|
|
295
|
-
nil
|
|
296
|
-
end
|
|
297
|
-
|
|
298
|
-
# Find running/pending instances in each of aws_regions, convert to
|
|
299
|
-
# props, and save in aws_instances list.
|
|
300
|
-
def aws_import_instances
|
|
301
|
-
|
|
302
|
-
instances = []
|
|
303
|
-
|
|
304
|
-
aws_regions.each do |region|
|
|
305
|
-
ec2 = AWS::EC2.new.regions[ region ]
|
|
306
|
-
|
|
307
|
-
found = ec2.instances.map do |inst|
|
|
308
|
-
next unless [ :running, :pending ].include?( inst.status )
|
|
309
|
-
aws_instance_to_props( region, inst )
|
|
310
|
-
end
|
|
311
|
-
|
|
312
|
-
instances += found.compact
|
|
313
|
-
end
|
|
314
|
-
|
|
315
|
-
@aws_instances = instances
|
|
316
|
-
end
|
|
317
|
-
|
|
318
|
-
def aws_instance_to_props( region, inst )
|
|
319
|
-
tags = inst.tags.to_h
|
|
320
|
-
name = tags[ 'Name' ]
|
|
321
|
-
roles = decode_roles( tags[ 'Roles' ] )
|
|
322
|
-
|
|
323
|
-
{ :id => inst.id,
|
|
324
|
-
:region => region,
|
|
325
|
-
:ami => inst.image_id,
|
|
326
|
-
:name => name,
|
|
327
|
-
:internet_name => inst.dns_name,
|
|
328
|
-
:internet_ip => inst.ip_address,
|
|
329
|
-
:internal_ip => inst.private_ip_address,
|
|
330
|
-
:instance_type => inst.instance_type,
|
|
331
|
-
:roles => roles }
|
|
332
|
-
end
|
|
333
|
-
|
|
334
|
-
# Read the aws_instances_json file, replacing in RAM AWS instances
|
|
335
|
-
def aws_read_instances
|
|
336
|
-
list = JSON.parse( IO.read( aws_instances_json ), :symbolize_names => true )
|
|
337
|
-
list.each do |inst|
|
|
338
|
-
inst[:roles] = ( inst[:roles] || [] ).map { |r| r.to_sym }
|
|
339
|
-
end
|
|
340
|
-
@aws_instances = list.sort { |p,n| p[:name] <=> n[:name] }
|
|
341
|
-
end
|
|
342
|
-
|
|
343
|
-
# Overwrite aws_instances_json file with the current AWS instances.
|
|
344
|
-
def aws_write_instances
|
|
345
|
-
File.open( aws_instances_json, 'w' ) do |fout|
|
|
346
|
-
aws_dump_instances( fout )
|
|
347
|
-
end
|
|
348
|
-
end
|
|
349
|
-
|
|
350
|
-
# Dump AWS instances as JSON but with custom layout of host per line.
|
|
351
|
-
def aws_dump_instances( fout = $stdout )
|
|
352
|
-
fout.puts '['
|
|
353
|
-
rows = @aws_instances.sort { |p,n| p[:name] <=> n[:name] }
|
|
354
|
-
rows.each_with_index do |row, i|
|
|
355
|
-
fout.puts( " " + JSON.generate( row, :space => ' ', :object_nl => ' ' ) +
|
|
356
|
-
( ( i == ( rows.length - 1 ) ) ? '' : ',' ) )
|
|
357
|
-
end
|
|
358
|
-
fout.puts ']'
|
|
359
|
-
end
|
|
360
|
-
|
|
361
|
-
def decode_roles( roles )
|
|
362
|
-
( roles || "" ).split( /\s+/ ).map { |r| r.to_sym }
|
|
363
|
-
end
|
|
364
|
-
|
|
365
|
-
# Runs create_lvm_volumes! if the first :lvm_volumes mount path does
|
|
366
|
-
# not yet exist.
|
|
367
|
-
def create_lvm_volumes( opts = {} )
|
|
368
|
-
opts = deep_merge_hashes( @aws_default_instance_options, opts )
|
|
369
|
-
unless exist?( opts[ :lvm_volumes ].first[1] )
|
|
370
|
-
create_lvm_volumes!( opts )
|
|
371
|
-
end
|
|
372
|
-
end
|
|
373
|
-
|
|
374
|
-
# Create an mdraid array from previously attached EBS volumes, then
|
|
375
|
-
# divvy up across N lvm mount points by ratio, creating filesystems and
|
|
376
|
-
# mounting. This mdadm and lvm recipe was originally derived from
|
|
377
|
-
# {Install MongoDB on Amazon EC2}[http://docs.mongodb.org/ecosystem/tutorial/install-mongodb-on-amazon-ec2/]
|
|
378
|
-
#
|
|
379
|
-
# === Options
|
|
380
|
-
# :ebs_volumes:: The number of EBS volumes previously created and
|
|
381
|
-
# attached to this instance.
|
|
382
|
-
# :lvm_volumes:: A table of [ slice, path (,name) ] rows where;
|
|
383
|
-
# slice is a floating point ratio in range (0.0,1.0],
|
|
384
|
-
# path is the mount point, and name is the lvm name,
|
|
385
|
-
# defaulting if omitted to the last path element. The
|
|
386
|
-
# sum of all slice values in the table should be 1.0.
|
|
387
|
-
def create_lvm_volumes!( opts = {} )
|
|
388
|
-
opts = deep_merge_hashes( @aws_default_instance_options, opts )
|
|
389
|
-
|
|
390
|
-
vol_count = opts[ :ebs_volumes ]
|
|
391
|
-
devices = vol_count.times.map { |i| "/dev/xvdh#{i+1}" }
|
|
392
|
-
|
|
393
|
-
cmd = <<-SH
|
|
394
|
-
#{dist_install_s( "mdadm", "lvm2", :minimal => true )}
|
|
395
|
-
mdadm --create /dev/md0 --level=10 --chunk=256 --raid-devices=#{vol_count} \
|
|
396
|
-
#{devices.join ' '}
|
|
397
|
-
echo "DEVICE #{devices.join ' '}" > /etc/mdadm.conf
|
|
398
|
-
mdadm --detail --scan >> /etc/mdadm.conf
|
|
399
|
-
SH
|
|
400
|
-
|
|
401
|
-
devices.each do |d|
|
|
402
|
-
cmd << <<-SH
|
|
403
|
-
blockdev --setra 128 #{d}
|
|
404
|
-
SH
|
|
405
|
-
end
|
|
406
|
-
|
|
407
|
-
cmd << <<-SH
|
|
408
|
-
blockdev --setra 128 /dev/md0
|
|
409
|
-
dd if=/dev/zero of=/dev/md0 bs=512 count=1
|
|
410
|
-
pvcreate /dev/md0
|
|
411
|
-
vgcreate vg0 /dev/md0
|
|
412
|
-
SH
|
|
413
|
-
|
|
414
|
-
opts[ :lvm_volumes ].each do | slice, path, name |
|
|
415
|
-
name ||= ( path =~ /\/(\w+)$/ ) && $1
|
|
416
|
-
cmd << <<-SH
|
|
417
|
-
lvcreate -l #{(slice * 100).round}%vg -n #{name} vg0
|
|
418
|
-
mke2fs -t ext4 -F /dev/vg0/#{name}
|
|
419
|
-
if [ -d #{path} ]; then
|
|
420
|
-
rm -rf #{path}
|
|
421
|
-
fi
|
|
422
|
-
mkdir -p #{path}
|
|
423
|
-
echo '/dev/vg0/#{name} #{path} ext4 defaults,auto,noatime,noexec 0 0' >> /etc/fstab
|
|
424
|
-
mount #{path}
|
|
425
|
-
SH
|
|
426
|
-
end
|
|
427
|
-
|
|
428
|
-
sudo cmd
|
|
429
|
-
end
|
|
430
|
-
|
|
431
|
-
# Wait until block returns truthy, sleeping for freq seconds between
|
|
432
|
-
# attempts. Writes desc and a sequence of DOTs on a single line
|
|
433
|
-
# until complete.
|
|
434
|
-
def wait_until( desc, freq = 1.0 )
|
|
435
|
-
$stdout.write( "Waiting for " + desc )
|
|
436
|
-
until (ret = yield) do
|
|
437
|
-
$stdout.write '.'
|
|
438
|
-
sleep freq
|
|
439
|
-
end
|
|
440
|
-
ret
|
|
441
|
-
ensure
|
|
442
|
-
puts
|
|
443
|
-
end
|
|
444
|
-
|
|
445
|
-
def dot_terminate( name )
|
|
446
|
-
( name =~ /\.$/ ) ? name : ( name + '.' )
|
|
447
|
-
end
|
|
448
|
-
end
|
data/lib/syncwrap/common.rb
DELETED
|
@@ -1,161 +0,0 @@
|
|
|
1
|
-
#--
|
|
2
|
-
# Copyright (c) 2011-2013 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/base'
|
|
18
|
-
|
|
19
|
-
# Common utility methods and variables.
|
|
20
|
-
module SyncWrap::Common
|
|
21
|
-
|
|
22
|
-
# The prefix for local non-distro installs (default: /usr/local)
|
|
23
|
-
attr_accessor :common_prefix
|
|
24
|
-
|
|
25
|
-
def initialize
|
|
26
|
-
super
|
|
27
|
-
@common_prefix = '/usr/local'
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
# Return true if remote file exists, as per "test -e"
|
|
31
|
-
def exist?( file )
|
|
32
|
-
exec_conditional { run "test -e #{file}" } == 0
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
# Put files or entire directories to remote.
|
|
36
|
-
#
|
|
37
|
-
# rput( src..., dest, {options} )
|
|
38
|
-
# rput( src, {options} )
|
|
39
|
-
#
|
|
40
|
-
# A trailing hash is interpreted as options, see below.
|
|
41
|
-
#
|
|
42
|
-
# If there is two or more remaining arguments, the last is
|
|
43
|
-
# interpreted as the remote destination. If there is a single
|
|
44
|
-
# remaining argument, the destination is implied by finding its base
|
|
45
|
-
# directory and prepending '/'. Thus for example:
|
|
46
|
-
#
|
|
47
|
-
# rput( 'etc/gemrc', :user => 'root' )
|
|
48
|
-
#
|
|
49
|
-
# has an implied destination of: `/etc/`. The src and destination
|
|
50
|
-
# are intrepreted as by `rsync`: glob patterns are expanded and
|
|
51
|
-
# trailing '/' is significant.
|
|
52
|
-
#
|
|
53
|
-
# ==== Options
|
|
54
|
-
# :user:: Files should be owned on destination by a user other than
|
|
55
|
-
# installer (ex: 'root') (implies sudo)
|
|
56
|
-
# :perms:: Permissions to set for install files.
|
|
57
|
-
# :excludes:: One or more rsync compatible --filter excludes, or
|
|
58
|
-
# :dev which excludes common developmoent tree droppings
|
|
59
|
-
# like '*~'
|
|
60
|
-
def rput( *args )
|
|
61
|
-
opts = args.last.is_a?( Hash ) && args.pop || {}
|
|
62
|
-
|
|
63
|
-
if args.length == 1
|
|
64
|
-
abspath = "/" + args.first
|
|
65
|
-
abspath = File.dirname( abspath ) + '/' unless abspath =~ %r{/$}
|
|
66
|
-
else
|
|
67
|
-
abspath = args.pop
|
|
68
|
-
end
|
|
69
|
-
|
|
70
|
-
flags = []
|
|
71
|
-
|
|
72
|
-
excludes = Array( opts[ :excludes ] )
|
|
73
|
-
flags += excludes.map do |e|
|
|
74
|
-
if e == :dev
|
|
75
|
-
'--cvs-exclude'
|
|
76
|
-
else
|
|
77
|
-
"--filter=#{e}"
|
|
78
|
-
end
|
|
79
|
-
end
|
|
80
|
-
|
|
81
|
-
if opts[ :perms ]
|
|
82
|
-
flags << '-p'
|
|
83
|
-
flags << "--chmod=#{opts[ :perms ]}"
|
|
84
|
-
end
|
|
85
|
-
|
|
86
|
-
if opts[ :user ]
|
|
87
|
-
# Use sudo to place files at remote.
|
|
88
|
-
user = opts[ :user ] || 'root'
|
|
89
|
-
flags << ( if user != 'root'
|
|
90
|
-
"--rsync-path=sudo -u #{user} rsync"
|
|
91
|
-
else
|
|
92
|
-
"--rsync-path=sudo rsync"
|
|
93
|
-
end )
|
|
94
|
-
end
|
|
95
|
-
|
|
96
|
-
cmd = [ flags, args, [ target_host, abspath ].join(':') ].flatten.compact
|
|
97
|
-
rsync( *cmd )
|
|
98
|
-
|
|
99
|
-
end
|
|
100
|
-
|
|
101
|
-
# Run args as shell command on the remote host. A line delimited
|
|
102
|
-
# argument is interpreted as multiple commands, otherwise arguments
|
|
103
|
-
# are joined as a single command.
|
|
104
|
-
#
|
|
105
|
-
# A trailing Hash is interpreted as options, see below.
|
|
106
|
-
#
|
|
107
|
-
# ==== options
|
|
108
|
-
# :error:: Set the error handling mode: If `:exit`, causes "set -e" to
|
|
109
|
-
# be passed as the first line of a multi-line
|
|
110
|
-
# command. (Default: :exit)
|
|
111
|
-
def run( *args )
|
|
112
|
-
raise "Include a remoting-specific module, e.g. RemoteTask"
|
|
113
|
-
end
|
|
114
|
-
|
|
115
|
-
# Run args under sudo on remote host. A line delimited argument is
|
|
116
|
-
# interpreted as multiple commands, otherwise arguments are joined
|
|
117
|
-
# as a single command.
|
|
118
|
-
#
|
|
119
|
-
# A trailing Hash is interpreted as options, see below.
|
|
120
|
-
#
|
|
121
|
-
# ==== options
|
|
122
|
-
# :user:: Run as specified user (default: root)
|
|
123
|
-
# :flags:: Additional sudo flags
|
|
124
|
-
# :shell:: Run command in a shell by wrapping it in sh -c "", and
|
|
125
|
-
# escaping quotes in the original joined args command.
|
|
126
|
-
# (default: true)
|
|
127
|
-
# :error:: Set the error handling mode: If `:exit`, causes "set -e" to
|
|
128
|
-
# be passed as the first line of a multi-line
|
|
129
|
-
# command. (Default: :exit, only applies if :shell)
|
|
130
|
-
def sudo( *args )
|
|
131
|
-
raise "Include a remoting-specific module, e.g. RemoteTask"
|
|
132
|
-
end
|
|
133
|
-
|
|
134
|
-
# Return the exit status of the first non-zero command result, or 0
|
|
135
|
-
# if all commands succeeded.
|
|
136
|
-
def exec_conditional
|
|
137
|
-
raise "Include a remoting-specific module, e.g. RemoteTask"
|
|
138
|
-
end
|
|
139
|
-
|
|
140
|
-
# Implement rsync as used by rput. Note that args should not be
|
|
141
|
-
# passed through shell interpretation, eg run via system( Array )
|
|
142
|
-
def rsync( *args )
|
|
143
|
-
raise "Include a remoting-specific module, e.g. RemoteTask"
|
|
144
|
-
end
|
|
145
|
-
|
|
146
|
-
# Returns the current target host when called from a running task.
|
|
147
|
-
def target_host
|
|
148
|
-
raise "Include a remoting-specific module, e.g. RemoteTask"
|
|
149
|
-
end
|
|
150
|
-
|
|
151
|
-
def deep_merge_hashes( h1, h2 )
|
|
152
|
-
h1.merge( h2 ) do |key, v1, v2|
|
|
153
|
-
if v1.is_a?( Hash ) && v2.is_a?( Hash )
|
|
154
|
-
deep_merge_hashes( v1, v2 )
|
|
155
|
-
else
|
|
156
|
-
v2
|
|
157
|
-
end
|
|
158
|
-
end
|
|
159
|
-
end
|
|
160
|
-
|
|
161
|
-
end
|
data/lib/syncwrap/ec2.rb
DELETED
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
#--
|
|
2
|
-
# Copyright (c) 2011-2013 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/common'
|
|
18
|
-
|
|
19
|
-
# Provisions for common Amazon EC2 image tasks.
|
|
20
|
-
module SyncWrap::EC2
|
|
21
|
-
include SyncWrap::Common
|
|
22
|
-
|
|
23
|
-
# The device name of the ephemeral storage device
|
|
24
|
-
# (default: 'xvdb')
|
|
25
|
-
attr_accessor :ec2_es_device
|
|
26
|
-
|
|
27
|
-
def initialize
|
|
28
|
-
super
|
|
29
|
-
|
|
30
|
-
@ec2_es_device = 'xvdb'
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
# WARNING: Destructive if run!
|
|
34
|
-
# Re-mkfs /mnt partition as ext4 if its ec2_es_device and is
|
|
35
|
-
# currently ext3
|
|
36
|
-
def ec2_reformat_mnt_as_ext4
|
|
37
|
-
rc = exec_conditional do
|
|
38
|
-
run "mount | grep '/dev/#{ec2_es_device} on /mnt'"
|
|
39
|
-
end
|
|
40
|
-
raise "Device /dev/#{ec2_es_device} not mounted on /mnt" unless rc == 0
|
|
41
|
-
|
|
42
|
-
rc = exec_conditional do
|
|
43
|
-
run "mount | grep '/dev/#{ec2_es_device} on /mnt type ext3'"
|
|
44
|
-
end
|
|
45
|
-
ec2_reformat_mnt_as_ext4! if rc == 0
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
# WARNING: Destructive!
|
|
49
|
-
# Re-mkfs /mnt partition as ext4
|
|
50
|
-
# See: http://serverfault.com/questions/317009
|
|
51
|
-
def ec2_reformat_mnt_as_ext4!
|
|
52
|
-
sudo <<-SH
|
|
53
|
-
umount /mnt
|
|
54
|
-
mkfs -t ext4 /dev/#{ec2_es_device}
|
|
55
|
-
mount /mnt
|
|
56
|
-
SH
|
|
57
|
-
end
|
|
58
|
-
|
|
59
|
-
end
|