rubber 3.1.0 → 3.2.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/CHANGELOG +10 -1
- data/Gemfile +2 -3
- data/lib/ext/fog/compute/digital_ocean_v2.rb +82 -0
- data/lib/rubber/cloud.rb +11 -2
- data/lib/rubber/cloud/aws.rb +9 -536
- data/lib/rubber/cloud/aws/base.rb +318 -0
- data/lib/rubber/cloud/aws/classic.rb +245 -0
- data/lib/rubber/cloud/{aws_table_store.rb → aws/table_store.rb} +2 -1
- data/lib/rubber/cloud/aws/vpc.rb +612 -0
- data/lib/rubber/cloud/base.rb +10 -2
- data/lib/rubber/cloud/digital_ocean.rb +62 -21
- data/lib/rubber/cloud/vagrant.rb +3 -3
- data/lib/rubber/instance.rb +13 -5
- data/lib/rubber/recipes/rubber/instances.rb +68 -16
- data/lib/rubber/recipes/rubber/setup.rb +12 -2
- data/lib/rubber/recipes/rubber/vpcs.rb +92 -0
- data/lib/rubber/tag.rb +1 -1
- data/lib/rubber/util.rb +8 -0
- data/lib/rubber/version.rb +1 -1
- data/rubber.gemspec +1 -1
- data/templates/base/config/rubber/rubber.yml +32 -1
- data/templates/nat_gateway/config/rubber/role/nat_gateway/nat.sh +45 -0
- data/templates/nat_gateway/templates.yml +2 -0
- data/test/cloud/aws/classic_test.rb +54 -0
- data/test/cloud/{aws_table_store_test.rb → aws/table_store_test.rb} +8 -8
- data/test/cloud/{aws_test.rb → aws/vpc_test.rb} +5 -5
- data/test/cloud/digital_ocean_test.rb +37 -19
- data/test/instance_test.rb +1 -1
- metadata +74 -93
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 4949f1406f5da62d1236bf1757dcf5a70e262094
|
4
|
+
data.tar.gz: 1b8dc7e8d6a18880408f5b1017d845f5ea4ef272
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: b3aadb75637b09101862f6f118a37ee0c9349944416aca49fad689eacf6e6b493d98fbb2abec1a15abc593ada43239284afa7cd544f844165269c3681be08651
|
7
|
+
data.tar.gz: f61a46a4d8923082c1c4610bf6f45bba4b0ec0734657d6f8b1764b2d31b2529e8774499937bedf45b6d150035da7675f4052c5823f6b3b43e250e313a7fddd81
|
data/CHANGELOG
CHANGED
@@ -1,9 +1,18 @@
|
|
1
|
+
3.2.0 (01/09/2016)
|
2
|
+
|
3
|
+
New Features:
|
4
|
+
============
|
5
|
+
|
6
|
+
[core] Added support for AWS VPCs.
|
7
|
+
[core] Added support for DigitalOcean v2 API.
|
8
|
+
|
9
|
+
|
1
10
|
3.1.0 (05/31/2015)
|
2
11
|
|
3
12
|
Improvements:
|
4
13
|
============
|
5
14
|
|
6
|
-
[core]
|
15
|
+
[core] Removed explicit dependency on 'json' gem. <61db9cc>
|
7
16
|
[core] Introduced env.enable_root_login_timeout parameter to configure timeout in enabling root login. <f8d1b65>
|
8
17
|
[passenger] Updated to Passenger 5.0.8. <c4a8261>
|
9
18
|
[passenger_nginx] Updated to Passenger 5.0.8. <c4a8261>
|
data/Gemfile
CHANGED
@@ -4,9 +4,8 @@ gem 'jruby-openssl', :platform => :jruby
|
|
4
4
|
gem 'unlimited-strength-crypto', :platform => :jruby
|
5
5
|
|
6
6
|
group :development do
|
7
|
-
|
8
|
-
|
9
|
-
gem 'fog', :git => 'https://github.com/fog/fog.git', :branch => 'master'
|
7
|
+
gem 'fog', '~> 1.36'
|
8
|
+
gem 'mime-types', '2.99'
|
10
9
|
end
|
11
10
|
|
12
11
|
# Specify your gem's dependencies in rubber.gemspec
|
@@ -0,0 +1,82 @@
|
|
1
|
+
require 'fog'
|
2
|
+
require 'fog/digitalocean/compute_v2'
|
3
|
+
require 'fog/digitalocean/requests/compute_v2/create_ssh_key'
|
4
|
+
require 'fog/digitalocean/requests/compute_v2/list_ssh_keys'
|
5
|
+
require 'fog/digitalocean/requests/compute_v2/delete_ssh_key'
|
6
|
+
|
7
|
+
module ::Fog
|
8
|
+
module Compute
|
9
|
+
class DigitalOceanV2
|
10
|
+
# Fixes an ssh key creation issue currently in fog 1.35.0
|
11
|
+
# This change currently in fog master:
|
12
|
+
# https://github.com/fog/fog/pull/3743
|
13
|
+
# However, unless it gets backported into 1.x, we'll need this patch until
|
14
|
+
# we update fog to 2.x
|
15
|
+
class Real
|
16
|
+
def create_ssh_key(name, public_key)
|
17
|
+
create_options = {
|
18
|
+
:name => name,
|
19
|
+
:public_key => public_key,
|
20
|
+
}
|
21
|
+
|
22
|
+
encoded_body = Fog::JSON.encode(create_options)
|
23
|
+
|
24
|
+
request(
|
25
|
+
:expects => [201],
|
26
|
+
:headers => {
|
27
|
+
'Content-Type' => "application/json; charset=UTF-8",
|
28
|
+
},
|
29
|
+
:method => 'POST',
|
30
|
+
:path => '/v2/account/keys',
|
31
|
+
:body => encoded_body,
|
32
|
+
)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
# These mocking improvements are not yet in fog master:
|
37
|
+
# https://github.com/fog/fog/pull/3748
|
38
|
+
class Mock
|
39
|
+
def create_ssh_key(name, public_key)
|
40
|
+
response = Excon::Response.new
|
41
|
+
response.status = 201
|
42
|
+
|
43
|
+
data[:ssh_keys] << {
|
44
|
+
"id" => Fog::Mock.random_numbers(6).to_i,
|
45
|
+
"fingerprint" => (["00"] * 16).join(':'),
|
46
|
+
"public_key" => public_key,
|
47
|
+
"name" => name
|
48
|
+
}
|
49
|
+
|
50
|
+
response.body ={
|
51
|
+
'ssh_key' => data[:ssh_keys].last
|
52
|
+
}
|
53
|
+
|
54
|
+
response
|
55
|
+
end
|
56
|
+
|
57
|
+
def list_ssh_keys
|
58
|
+
response = Excon::Response.new
|
59
|
+
response.status = 200
|
60
|
+
response.body = {
|
61
|
+
"ssh_keys" => data[:ssh_keys],
|
62
|
+
"links" => {},
|
63
|
+
"meta" => {
|
64
|
+
"total" => data[:ssh_keys].count
|
65
|
+
}
|
66
|
+
}
|
67
|
+
response
|
68
|
+
end
|
69
|
+
|
70
|
+
def delete_ssh_key(id)
|
71
|
+
self.data[:ssh_keys].select! do |key|
|
72
|
+
key["id"] != id
|
73
|
+
end
|
74
|
+
|
75
|
+
response = Excon::Response.new
|
76
|
+
response.status = 204
|
77
|
+
response
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
data/lib/rubber/cloud.rb
CHANGED
@@ -5,10 +5,19 @@ module Rubber
|
|
5
5
|
|
6
6
|
def self.get_provider(provider, env, capistrano)
|
7
7
|
require "rubber/cloud/#{provider}"
|
8
|
-
clazz = Rubber::Cloud.const_get(Rubber::Util.camelcase(provider))
|
9
8
|
provider_env = env.cloud_providers[provider]
|
10
|
-
|
9
|
+
|
10
|
+
# Check to see if we have a Rubber::Cloud::Provider::Factory class. If
|
11
|
+
# not, fall back to Rubber::Cloud::Provider
|
12
|
+
begin
|
13
|
+
factory = Rubber::Cloud.const_get(Rubber::Util.camelcase(provider))::Factory
|
14
|
+
return factory.get_provider(provider_env, capistrano)
|
15
|
+
rescue NameError
|
16
|
+
clazz = Rubber::Cloud.const_get(Rubber::Util.camelcase(provider))
|
17
|
+
return clazz.new(provider_env, capistrano)
|
18
|
+
end
|
11
19
|
end
|
12
20
|
|
13
21
|
end
|
14
22
|
end
|
23
|
+
|
data/lib/rubber/cloud/aws.rb
CHANGED
@@ -1,545 +1,18 @@
|
|
1
|
-
require 'rubber/cloud/fog'
|
2
|
-
require 'rubber/cloud/aws_table_store'
|
3
|
-
|
4
1
|
module Rubber
|
5
|
-
module Cloud
|
6
|
-
|
7
|
-
class Aws < Fog
|
8
|
-
|
9
|
-
def initialize(env, capistrano)
|
10
|
-
|
11
|
-
compute_credentials = {
|
12
|
-
:aws_access_key_id => env.access_key,
|
13
|
-
:aws_secret_access_key => env.secret_access_key
|
14
|
-
}
|
15
|
-
|
16
|
-
storage_credentials = {
|
17
|
-
:provider => 'AWS',
|
18
|
-
:aws_access_key_id => env.access_key,
|
19
|
-
:aws_secret_access_key => env.secret_access_key,
|
20
|
-
:path_style => true
|
21
|
-
}
|
22
|
-
|
23
|
-
@table_store = ::Fog::AWS::SimpleDB.new(compute_credentials)
|
24
|
-
|
25
|
-
compute_credentials[:region] = env.region
|
26
|
-
@elb = ::Fog::AWS::ELB.new(compute_credentials)
|
27
|
-
|
28
|
-
compute_credentials[:provider] = 'AWS' # We need to set the provider after the SimpleDB init because it fails if the provider value is specified.
|
29
|
-
|
30
|
-
storage_credentials[:region] = env.region
|
31
|
-
|
32
|
-
env['compute_credentials'] = compute_credentials
|
33
|
-
env['storage_credentials'] = storage_credentials
|
34
|
-
super(env, capistrano)
|
35
|
-
end
|
36
|
-
|
37
|
-
def table_store(table_key)
|
38
|
-
return Rubber::Cloud::AwsTableStore.new(@table_store, table_key)
|
39
|
-
end
|
40
|
-
|
41
|
-
def describe_instances(instance_id=nil)
|
42
|
-
instances = []
|
43
|
-
opts = {}
|
44
|
-
opts["instance-id"] = instance_id if instance_id
|
45
|
-
|
46
|
-
response = compute_provider.servers.all(opts)
|
47
|
-
response.each do |item|
|
48
|
-
instance = {}
|
49
|
-
instance[:id] = item.id
|
50
|
-
instance[:type] = item.flavor_id
|
51
|
-
instance[:external_host] = item.dns_name
|
52
|
-
instance[:external_ip] = item.public_ip_address
|
53
|
-
instance[:internal_host] = item.private_dns_name
|
54
|
-
instance[:internal_ip] = item.private_ip_address
|
55
|
-
instance[:state] = item.state
|
56
|
-
instance[:zone] = item.availability_zone
|
57
|
-
instance[:provider] = 'aws'
|
58
|
-
instance[:platform] = item.platform || Rubber::Platforms::LINUX
|
59
|
-
instance[:root_device_type] = item.root_device_type
|
60
|
-
instances << instance
|
61
|
-
end
|
62
|
-
|
63
|
-
return instances
|
64
|
-
end
|
65
|
-
|
66
|
-
def active_state
|
67
|
-
'running'
|
68
|
-
end
|
69
|
-
|
70
|
-
def stopped_state
|
71
|
-
'stopped'
|
72
|
-
end
|
73
|
-
|
74
|
-
def before_create_instance(instance_alias, role_names)
|
75
|
-
setup_security_groups(instance_alias, role_names)
|
76
|
-
end
|
77
|
-
|
78
|
-
def after_create_instance(instance)
|
79
|
-
# Sometimes tag creation will fail, indicating that the instance doesn't exist yet even though it does. It seems to
|
80
|
-
# be a propagation delay on Amazon's end, so the best we can do is wait and try again.
|
81
|
-
Rubber::Util.retry_on_failure(StandardError, :retry_sleep => 1, :retry_count => 120) do
|
82
|
-
Rubber::Tag::update_instance_tags(instance.name)
|
83
|
-
end
|
84
|
-
end
|
85
|
-
|
86
|
-
def after_refresh_instance(instance)
|
87
|
-
# Sometimes tag creation will fail, indicating that the instance doesn't exist yet even though it does. It seems to
|
88
|
-
# be a propagation delay on Amazon's end, so the best we can do is wait and try again.
|
89
|
-
Rubber::Util.retry_on_failure(StandardError, :retry_sleep => 1, :retry_count => 120) do
|
90
|
-
Rubber::Tag::update_instance_tags(instance.name)
|
91
|
-
end
|
92
|
-
end
|
93
|
-
|
94
|
-
def before_stop_instance(instance)
|
95
|
-
capistrano.fatal "Cannot stop spot instances!" if ! instance.spot_instance_request_id.nil?
|
96
|
-
capistrano.fatal "Cannot stop instances with instance-store root device!" if (instance.root_device_type != 'ebs')
|
97
|
-
end
|
98
|
-
|
99
|
-
def before_start_instance(instance)
|
100
|
-
capistrano.fatal "Cannot start spot instances!" if ! instance.spot_instance_request_id.nil?
|
101
|
-
capistrano.fatal "Cannot start instances with instance-store root device!" if (instance.root_device_type != 'ebs')
|
102
|
-
end
|
103
|
-
|
104
|
-
def after_start_instance(instance)
|
105
|
-
# Re-starting an instance will almost certainly give it a new set of IPs and DNS entries, so refresh the values.
|
106
|
-
capistrano.rubber.refresh_instance(instance.name)
|
107
|
-
|
108
|
-
# Static IPs, DNS, etc. need to be set up for the started instance.
|
109
|
-
capistrano.rubber.post_refresh
|
110
|
-
end
|
2
|
+
module Cloud
|
3
|
+
module Aws
|
111
4
|
|
112
|
-
|
5
|
+
class Factory
|
6
|
+
def self.get_provider(provider_env, capistrano)
|
7
|
+
require 'rubber/cloud/aws/vpc'
|
8
|
+
require 'rubber/cloud/aws/classic'
|
113
9
|
|
114
|
-
|
115
|
-
|
116
|
-
raise "Set #{k} in rubber.yml" unless "#{env[k]}".strip.size > 0
|
10
|
+
klazz = provider_env.vpc_alias ? Rubber::Cloud::Aws::Vpc : Rubber::Cloud::Aws::Classic
|
11
|
+
klazz.new provider_env, capistrano
|
117
12
|
end
|
118
|
-
raise "create_image can only be called from a capistrano scope" unless capistrano
|
119
|
-
|
120
|
-
ec2_key = env.key_file
|
121
|
-
ec2_pk = env.pk_file
|
122
|
-
ec2_cert = env.cert_file
|
123
|
-
|
124
|
-
ec2_key_dest = "/mnt/#{File.basename(ec2_key)}"
|
125
|
-
ec2_pk_dest = "/mnt/#{File.basename(ec2_pk)}"
|
126
|
-
ec2_cert_dest = "/mnt/#{File.basename(ec2_cert)}"
|
127
|
-
|
128
|
-
storage(env.image_bucket).ensure_bucket
|
129
|
-
|
130
|
-
capistrano.put(File.read(ec2_key), ec2_key_dest)
|
131
|
-
capistrano.put(File.read(ec2_pk), ec2_pk_dest)
|
132
|
-
capistrano.put(File.read(ec2_cert), ec2_cert_dest)
|
133
|
-
|
134
|
-
arch = capistrano.capture("uname -m").strip
|
135
|
-
arch = case arch when /i\d86/ then "i386" else arch end
|
136
|
-
|
137
|
-
capistrano.sudo_script "create_bundle", <<-CMD
|
138
|
-
export RUBYLIB=/usr/lib/site_ruby/
|
139
|
-
unset RUBYOPT
|
140
|
-
nohup ec2-bundle-vol --batch -d /mnt -k #{ec2_pk_dest} -c #{ec2_cert_dest} -u #{env.account} -p #{image_name} -r #{arch} &> /tmp/ec2-bundle-vol.log &
|
141
|
-
bg_pid=$!
|
142
|
-
sleep 1
|
143
|
-
|
144
|
-
echo "Creating image from instance volume..."
|
145
|
-
while kill -0 $bg_pid &> /dev/null; do
|
146
|
-
echo -n .
|
147
|
-
sleep 5
|
148
|
-
done
|
149
|
-
|
150
|
-
# this returns exit code even if pid has already died, and thus triggers fail fast shell error
|
151
|
-
wait $bg_pid
|
152
|
-
CMD
|
153
|
-
|
154
|
-
capistrano.sudo_script "register_bundle", <<-CMD
|
155
|
-
export RUBYLIB=/usr/lib/site_ruby/
|
156
|
-
unset RUBYOPT
|
157
|
-
echo "Uploading image to S3..."
|
158
|
-
ec2-upload-bundle --batch -b #{env.image_bucket} -m /mnt/#{image_name}.manifest.xml -a #{env.access_key} -s #{env.secret_access_key}
|
159
|
-
CMD
|
160
|
-
|
161
|
-
image_location = "#{env.image_bucket}/#{image_name}.manifest.xml"
|
162
|
-
response = compute_provider.register_image(image_name,
|
163
|
-
"rubber bundled image",
|
164
|
-
image_location)
|
165
|
-
return response.body["imageId"]
|
166
|
-
end
|
167
|
-
|
168
|
-
def destroy_image(image_id)
|
169
|
-
image = compute_provider.images.get(image_id)
|
170
|
-
raise "Could not find image: #{image_id}, aborting destroy_image" if image.nil?
|
171
|
-
|
172
|
-
location_parts = image.location.split('/')
|
173
|
-
bucket = location_parts.first
|
174
|
-
image_name = location_parts.last.gsub(/\.manifest\.xml$/, '')
|
175
|
-
|
176
|
-
image.deregister
|
177
|
-
|
178
|
-
storage(bucket).walk_tree(image_name) do |f|
|
179
|
-
f.destroy
|
180
|
-
end
|
181
|
-
end
|
182
|
-
|
183
|
-
def describe_load_balancers(name=nil)
|
184
|
-
lbs = []
|
185
|
-
response = name.nil? ? @elb.load_balancers.all() : [@elb.load_balancers.get(name)].compact
|
186
|
-
response.each do |item|
|
187
|
-
lb = {}
|
188
|
-
lb[:name] = item.id
|
189
|
-
lb[:dns_name] = item.dns_name
|
190
|
-
lb[:zones] = item.availability_zones
|
191
|
-
|
192
|
-
item.listeners.each do |litem|
|
193
|
-
listener = {}
|
194
|
-
listener[:protocol] = litem.protocol
|
195
|
-
listener[:port] = litem.lb_portPort
|
196
|
-
listener[:instance_port] = litem.instance_port
|
197
|
-
lb[:listeners] ||= []
|
198
|
-
lb[:listeners] << listener
|
199
|
-
end
|
200
|
-
|
201
|
-
lbs << lb
|
202
|
-
end
|
203
|
-
return lbs
|
204
|
-
end
|
205
|
-
|
206
|
-
def describe_availability_zones
|
207
|
-
zones = []
|
208
|
-
response = compute_provider.describe_availability_zones()
|
209
|
-
items = response.body["availabilityZoneInfo"]
|
210
|
-
items.each do |item|
|
211
|
-
zone = {}
|
212
|
-
zone[:name] = item["zoneName"]
|
213
|
-
zone[:state] =item["zoneState"]
|
214
|
-
zones << zone
|
215
|
-
end
|
216
|
-
return zones
|
217
|
-
end
|
218
|
-
|
219
|
-
def create_spot_instance_request(spot_price, ami, ami_type, security_groups, availability_zone, fog_options={})
|
220
|
-
response = compute_provider.spot_requests.create({:price => spot_price,
|
221
|
-
:image_id => ami,
|
222
|
-
:flavor_id => ami_type,
|
223
|
-
:groups => security_groups,
|
224
|
-
:availability_zone => availability_zone,
|
225
|
-
:key_name => env.key_name}.merge(Rubber::Util.symbolize_keys(fog_options)))
|
226
|
-
request_id = response.id
|
227
|
-
return request_id
|
228
|
-
end
|
229
|
-
|
230
|
-
def describe_spot_instance_requests(request_id=nil)
|
231
|
-
requests = []
|
232
|
-
opts = {}
|
233
|
-
opts["spot-instance-request-id"] = request_id if request_id
|
234
|
-
response = compute_provider.spot_requests.all(opts)
|
235
|
-
response.each do |item|
|
236
|
-
request = {}
|
237
|
-
request[:id] = item.id
|
238
|
-
request[:spot_price] = item.price
|
239
|
-
request[:state] = item.state
|
240
|
-
request[:created_at] = item.created_at
|
241
|
-
request[:type] = item.flavor_id
|
242
|
-
request[:image_id] = item.image_id
|
243
|
-
request[:instance_id] = item.instance_id
|
244
|
-
requests << request
|
245
|
-
end
|
246
|
-
return requests
|
247
|
-
end
|
248
|
-
|
249
|
-
def setup_security_groups(host=nil, roles=[])
|
250
|
-
rubber_cfg = Rubber::Configuration.get_configuration(Rubber.env)
|
251
|
-
scoped_env = rubber_cfg.environment.bind(roles, host)
|
252
|
-
security_group_defns = Hash[scoped_env.security_groups.to_a]
|
253
|
-
|
254
|
-
if scoped_env.auto_security_groups
|
255
|
-
sghosts = (scoped_env.rubber_instances.collect{|ic| ic.name } + [host]).uniq.compact
|
256
|
-
sgroles = (scoped_env.rubber_instances.all_roles + roles).uniq.compact
|
257
|
-
security_group_defns = inject_auto_security_groups(security_group_defns, sghosts, sgroles)
|
258
|
-
end
|
259
|
-
|
260
|
-
sync_security_groups(security_group_defns)
|
261
13
|
end
|
262
14
|
|
263
|
-
def describe_security_groups(group_name=nil)
|
264
|
-
groups = []
|
265
|
-
|
266
|
-
opts = {}
|
267
|
-
opts["group-name"] = group_name if group_name
|
268
|
-
response = compute_provider.security_groups.all(opts)
|
269
|
-
|
270
|
-
response.each do |item|
|
271
|
-
group = {}
|
272
|
-
group[:name] = item.name
|
273
|
-
group[:description] = item.description
|
274
|
-
|
275
|
-
item.ip_permissions.each do |ip_item|
|
276
|
-
group[:permissions] ||= []
|
277
|
-
rule = {}
|
278
|
-
|
279
|
-
rule[:protocol] = ip_item["ipProtocol"]
|
280
|
-
rule[:from_port] = ip_item["fromPort"]
|
281
|
-
rule[:to_port] = ip_item["toPort"]
|
282
|
-
|
283
|
-
ip_item["groups"].each do |rule_group|
|
284
|
-
rule[:source_groups] ||= []
|
285
|
-
source_group = {}
|
286
|
-
source_group[:account] = rule_group["userId"]
|
287
|
-
|
288
|
-
# Amazon doesn't appear to be returning the groupName value when running in a default VPC. It's possible
|
289
|
-
# it's only returned for EC2 Classic. This is distinctly in conflict with the API documents and thus
|
290
|
-
# appears to be a bug on Amazon's end. Nonetheless, we need to handle it because otherwise our security
|
291
|
-
# group rule matching logic will fail and it messes up our users.
|
292
|
-
#
|
293
|
-
# Since every top-level item has both an ID and a name, if we're lacking the groupName we can search
|
294
|
-
# through the items for the one matching the groupId we have and then use its name value. This should
|
295
|
-
# represent precisely the same data.
|
296
|
-
source_group[:name] = if rule_group["groupName"]
|
297
|
-
rule_group["groupName"]
|
298
|
-
elsif rule_group["groupId"]
|
299
|
-
matching_security_group = response.find { |item| item.group_id == rule_group["groupId"] }
|
300
|
-
matching_security_group ? matching_security_group.name : nil
|
301
|
-
else
|
302
|
-
nil
|
303
|
-
end
|
304
|
-
|
305
|
-
rule[:source_groups] << source_group
|
306
|
-
end if ip_item["groups"]
|
307
|
-
|
308
|
-
ip_item["ipRanges"].each do |ip_range|
|
309
|
-
rule[:source_ips] ||= []
|
310
|
-
rule[:source_ips] << ip_range["cidrIp"]
|
311
|
-
end if ip_item["ipRanges"]
|
312
|
-
|
313
|
-
group[:permissions] << rule
|
314
|
-
end
|
315
|
-
|
316
|
-
groups << group
|
317
|
-
end
|
318
|
-
|
319
|
-
groups
|
320
|
-
end
|
321
|
-
|
322
|
-
def create_volume(instance, volume_spec)
|
323
|
-
fog_options = Rubber::Util.symbolize_keys(volume_spec['fog_options'] || {})
|
324
|
-
volume_data = {
|
325
|
-
:size => volume_spec['size'], :availability_zone => volume_spec['zone']
|
326
|
-
}.merge(fog_options)
|
327
|
-
volume = compute_provider.volumes.create(volume_data)
|
328
|
-
volume.id
|
329
|
-
end
|
330
|
-
|
331
|
-
def after_create_volume(instance, volume_id, volume_spec)
|
332
|
-
# After we create an EBS volume, we need to attach it to the instance.
|
333
|
-
volume = compute_provider.volumes.get(volume_id)
|
334
|
-
server = compute_provider.servers.get(instance.instance_id)
|
335
|
-
volume.device = volume_spec['device']
|
336
|
-
volume.server = server
|
337
|
-
end
|
338
|
-
|
339
|
-
def before_destroy_volume(volume_id)
|
340
|
-
# Before we can destroy an EBS volume, we must detach it from any running instances.
|
341
|
-
volume = compute_provider.volumes.get(volume_id)
|
342
|
-
volume.force_detach
|
343
|
-
end
|
344
|
-
|
345
|
-
def destroy_volume(volume_id)
|
346
|
-
compute_provider.volumes.get(volume_id).destroy
|
347
|
-
end
|
348
|
-
|
349
|
-
def describe_volumes(volume_id=nil)
|
350
|
-
volumes = []
|
351
|
-
opts = {}
|
352
|
-
opts[:'volume-id'] = volume_id if volume_id
|
353
|
-
response = compute_provider.volumes.all(opts)
|
354
|
-
|
355
|
-
response.each do |item|
|
356
|
-
volume = {}
|
357
|
-
volume[:id] = item.id
|
358
|
-
volume[:status] = item.state
|
359
|
-
|
360
|
-
if item.server_id
|
361
|
-
volume[:attachment_instance_id] = item.server_id
|
362
|
-
volume[:attachment_status] = item.attached_at ? "attached" : "waiting"
|
363
|
-
end
|
364
|
-
|
365
|
-
volumes << volume
|
366
|
-
end
|
367
|
-
|
368
|
-
volumes
|
369
|
-
end
|
370
|
-
|
371
|
-
# resource_id is any Amazon resource ID (e.g., instance ID or volume ID)
|
372
|
-
# tags is a hash of tag_name => tag_value pairs
|
373
|
-
def create_tags(resource_id, tags)
|
374
|
-
# Tags need to be created individually in fog
|
375
|
-
tags.each do |k, v|
|
376
|
-
compute_provider.tags.create(:resource_id => resource_id,
|
377
|
-
:key => k.to_s, :value => v.to_s)
|
378
|
-
end
|
379
|
-
end
|
380
|
-
|
381
|
-
private
|
382
|
-
|
383
|
-
def create_security_group(group_name, group_description)
|
384
|
-
compute_provider.security_groups.create(:name => group_name, :description => group_description)
|
385
|
-
end
|
386
|
-
|
387
|
-
def destroy_security_group(group_name)
|
388
|
-
compute_provider.security_groups.get(group_name).destroy
|
389
|
-
end
|
390
|
-
|
391
|
-
def add_security_group_rule(group_name, protocol, from_port, to_port, source)
|
392
|
-
group = compute_provider.security_groups.get(group_name)
|
393
|
-
opts = {:ip_protocol => protocol || 'tcp'}
|
394
|
-
|
395
|
-
if source.instance_of? Hash
|
396
|
-
opts[:group] = {source[:account] => source[:name]}
|
397
|
-
else
|
398
|
-
opts[:cidr_ip] = source
|
399
|
-
end
|
400
|
-
|
401
|
-
group.authorize_port_range(from_port.to_i..to_port.to_i, opts)
|
402
|
-
end
|
403
|
-
|
404
|
-
def remove_security_group_rule(group_name, protocol, from_port, to_port, source)
|
405
|
-
group = compute_provider.security_groups.get(group_name)
|
406
|
-
opts = {:ip_protocol => protocol || 'tcp'}
|
407
|
-
|
408
|
-
if source.instance_of? Hash
|
409
|
-
opts[:group] = {source[:account] => source[:name]}
|
410
|
-
else
|
411
|
-
opts[:cidr_ip] = source
|
412
|
-
end
|
413
|
-
|
414
|
-
group.revoke_port_range(from_port.to_i..to_port.to_i, opts)
|
415
|
-
end
|
416
|
-
|
417
|
-
def sync_security_groups(groups)
|
418
|
-
return unless groups
|
419
|
-
|
420
|
-
groups = Rubber::Util::stringify(groups)
|
421
|
-
groups = isolate_groups(groups)
|
422
|
-
group_keys = groups.keys.clone()
|
423
|
-
|
424
|
-
# For each group that does already exist in cloud
|
425
|
-
cloud_groups = describe_security_groups()
|
426
|
-
cloud_groups.each do |cloud_group|
|
427
|
-
group_name = cloud_group[:name]
|
428
|
-
|
429
|
-
# skip those groups that don't belong to this project/env
|
430
|
-
next if env.isolate_security_groups && group_name !~ /^#{isolate_prefix}/
|
431
|
-
|
432
|
-
if group_keys.delete(group_name)
|
433
|
-
# sync rules
|
434
|
-
capistrano.logger.debug "Security Group already in cloud, syncing rules: #{group_name}"
|
435
|
-
group = groups[group_name]
|
436
|
-
|
437
|
-
# convert the special case default rule into what it actually looks like when
|
438
|
-
# we query ec2 so that we can match things up when syncing
|
439
|
-
rules = group['rules'].clone
|
440
|
-
group['rules'].each do |rule|
|
441
|
-
if [2, 3].include?(rule.size) && rule['source_group_name'] && rule['source_group_account']
|
442
|
-
rules << rule.merge({'protocol' => 'tcp', 'from_port' => '1', 'to_port' => '65535' })
|
443
|
-
rules << rule.merge({'protocol' => 'udp', 'from_port' => '1', 'to_port' => '65535' })
|
444
|
-
rules << rule.merge({'protocol' => 'icmp', 'from_port' => '-1', 'to_port' => '-1' })
|
445
|
-
rules.delete(rule)
|
446
|
-
end
|
447
|
-
end
|
448
|
-
|
449
|
-
rule_maps = []
|
450
|
-
|
451
|
-
# first collect the rule maps from the request (group/user pairs are duplicated for tcp/udp/icmp,
|
452
|
-
# so we need to do this up frnot and remove duplicates before checking against the local rubber rules)
|
453
|
-
cloud_group[:permissions].each do |rule|
|
454
|
-
source_groups = rule.delete(:source_groups)
|
455
|
-
if source_groups
|
456
|
-
source_groups.each do |source_group|
|
457
|
-
rule_map = rule.clone
|
458
|
-
rule_map.delete(:source_ips)
|
459
|
-
rule_map[:source_group_name] = source_group[:name]
|
460
|
-
rule_map[:source_group_account] = source_group[:account]
|
461
|
-
rule_map = Rubber::Util::stringify(rule_map)
|
462
|
-
rule_maps << rule_map unless rule_maps.include?(rule_map)
|
463
|
-
end
|
464
|
-
else
|
465
|
-
rule_map = Rubber::Util::stringify(rule)
|
466
|
-
rule_maps << rule_map unless rule_maps.include?(rule_map)
|
467
|
-
end
|
468
|
-
end if cloud_group[:permissions]
|
469
|
-
# For each rule, if it exists, do nothing, otherwise remove it as its no longer defined locally
|
470
|
-
rule_maps.each do |rule_map|
|
471
|
-
if rules.delete(rule_map)
|
472
|
-
# rules match, don't need to do anything
|
473
|
-
# logger.debug "Rule in sync: #{rule_map.inspect}"
|
474
|
-
else
|
475
|
-
# rules don't match, remove them from cloud and re-add below
|
476
|
-
answer = nil
|
477
|
-
msg = "Rule '#{rule_map.inspect}' exists in cloud, but not locally"
|
478
|
-
if env.prompt_for_security_group_sync
|
479
|
-
answer = Capistrano::CLI.ui.ask("#{msg}, remove from cloud? [y/N]: ")
|
480
|
-
else
|
481
|
-
capistrano.logger.info(msg)
|
482
|
-
end
|
483
|
-
|
484
|
-
if answer =~ /^y/
|
485
|
-
rule_map = Rubber::Util::symbolize_keys(rule_map)
|
486
|
-
if rule_map[:source_group_name]
|
487
|
-
remove_security_group_rule(group_name, rule_map[:protocol], rule_map[:from_port], rule_map[:to_port], {:name => rule_map[:source_group_name], :account => rule_map[:source_group_account]})
|
488
|
-
else
|
489
|
-
rule_map[:source_ips].each do |source_ip|
|
490
|
-
remove_security_group_rule(group_name, rule_map[:protocol], rule_map[:from_port], rule_map[:to_port], source_ip)
|
491
|
-
end if rule_map[:source_ips]
|
492
|
-
end
|
493
|
-
end
|
494
|
-
end
|
495
|
-
end
|
496
|
-
|
497
|
-
rules.each do |rule_map|
|
498
|
-
# create non-existing rules
|
499
|
-
capistrano.logger.debug "Missing rule, creating: #{rule_map.inspect}"
|
500
|
-
rule_map = Rubber::Util::symbolize_keys(rule_map)
|
501
|
-
if rule_map[:source_group_name]
|
502
|
-
add_security_group_rule(group_name, rule_map[:protocol], rule_map[:from_port], rule_map[:to_port], {:name => rule_map[:source_group_name], :account => rule_map[:source_group_account]})
|
503
|
-
else
|
504
|
-
rule_map[:source_ips].each do |source_ip|
|
505
|
-
add_security_group_rule(group_name, rule_map[:protocol], rule_map[:from_port], rule_map[:to_port], source_ip)
|
506
|
-
end if rule_map[:source_ips]
|
507
|
-
end
|
508
|
-
end
|
509
|
-
else
|
510
|
-
# delete group
|
511
|
-
answer = nil
|
512
|
-
msg = "Security group '#{group_name}' exists in cloud but not locally"
|
513
|
-
if env.prompt_for_security_group_sync
|
514
|
-
answer = Capistrano::CLI.ui.ask("#{msg}, remove from cloud? [y/N]: ")
|
515
|
-
else
|
516
|
-
capistrano.logger.debug(msg)
|
517
|
-
end
|
518
|
-
destroy_security_group(group_name) if answer =~ /^y/
|
519
|
-
end
|
520
|
-
end
|
521
|
-
|
522
|
-
# For each group that didnt already exist in cloud
|
523
|
-
group_keys.each do |group_name|
|
524
|
-
group = groups[group_name]
|
525
|
-
capistrano.logger.debug "Creating new security group: #{group_name}"
|
526
|
-
# create each group
|
527
|
-
create_security_group(group_name, group['description'])
|
528
|
-
# create rules for group
|
529
|
-
group['rules'].each do |rule_map|
|
530
|
-
capistrano.logger.debug "Creating new rule: #{rule_map.inspect}"
|
531
|
-
rule_map = Rubber::Util::symbolize_keys(rule_map)
|
532
|
-
if rule_map[:source_group_name]
|
533
|
-
add_security_group_rule(group_name, rule_map[:protocol], rule_map[:from_port], rule_map[:to_port], {:name => rule_map[:source_group_name], :account => rule_map[:source_group_account]})
|
534
|
-
else
|
535
|
-
rule_map[:source_ips].each do |source_ip|
|
536
|
-
add_security_group_rule(group_name, rule_map[:protocol], rule_map[:from_port], rule_map[:to_port], source_ip)
|
537
|
-
end if rule_map[:source_ips]
|
538
|
-
end
|
539
|
-
end
|
540
|
-
end
|
541
|
-
end
|
542
15
|
end
|
543
|
-
|
544
16
|
end
|
545
17
|
end
|
18
|
+
|