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
@@ -0,0 +1,612 @@
|
|
1
|
+
require 'rubber/cloud/aws/base'
|
2
|
+
require 'rubber/util'
|
3
|
+
|
4
|
+
module Rubber
|
5
|
+
module Cloud
|
6
|
+
|
7
|
+
class Aws::Vpc < Aws::Base
|
8
|
+
|
9
|
+
def before_create_instance(instance)
|
10
|
+
host_env = load_bound_env(instance.name)
|
11
|
+
cloud_env = host_env.cloud_providers[env.cloud_provider]
|
12
|
+
|
13
|
+
instance.network = cloud_env.vpc_alias
|
14
|
+
instance.vpc_cidr = cloud_env.vpc_cidr
|
15
|
+
|
16
|
+
# Remember that instance.network is our more generic term for vpc_alias
|
17
|
+
role_names = instance.roles.map(&:name)
|
18
|
+
instance.vpc_id = setup_vpc(instance.network, instance.vpc_cidr).id
|
19
|
+
instance.gateway = host_env.private_nic.gateway
|
20
|
+
private_public = instance.gateway == 'public' ? 'public' : 'private'
|
21
|
+
|
22
|
+
instance.subnet_id = setup_vpc_subnet(
|
23
|
+
instance.vpc_id,
|
24
|
+
instance.network,
|
25
|
+
host_env.private_nic,
|
26
|
+
instance.zone,
|
27
|
+
"#{instance.network} #{instance.zone} #{private_public}"
|
28
|
+
).subnet_id
|
29
|
+
|
30
|
+
setup_security_groups(instance.vpc_id, instance.name, instance.role_names)
|
31
|
+
end
|
32
|
+
|
33
|
+
def after_create_instance(instance)
|
34
|
+
super
|
35
|
+
|
36
|
+
# Creating an instance with both a subnet id and groups doesn't seem to
|
37
|
+
# result in the groups actually sticking. Lucky, VPC instances have
|
38
|
+
# mutable security groups
|
39
|
+
group_ids = describe_security_groups(instance.vpc_id).map { |g|
|
40
|
+
if instance.security_groups.include?(g[:name])
|
41
|
+
g[:group_id]
|
42
|
+
else
|
43
|
+
nil
|
44
|
+
end
|
45
|
+
}.compact
|
46
|
+
|
47
|
+
compute_provider.modify_instance_attribute(instance.instance_id, {
|
48
|
+
'GroupId' => group_ids
|
49
|
+
})
|
50
|
+
|
51
|
+
if instance.roles.map(&:name).include? "nat_gateway"
|
52
|
+
# NAT gateways need the sourceDestCheck attribute to be false for AWS
|
53
|
+
# to allow them to route traffic
|
54
|
+
server = compute_provider.servers.get(instance.instance_id)
|
55
|
+
|
56
|
+
server.network_interfaces.each do |interface|
|
57
|
+
# Sometimes we get a blank interface back
|
58
|
+
next unless interface.count > 0
|
59
|
+
|
60
|
+
interface_id = interface['networkInterfaceId']
|
61
|
+
|
62
|
+
compute_provider.modify_network_interface_attribute(
|
63
|
+
interface_id,
|
64
|
+
'sourceDestCheck',
|
65
|
+
false
|
66
|
+
)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def setup_security_groups(vpc_id, host=nil, roles=[])
|
72
|
+
rubber_cfg = Rubber::Configuration.get_configuration(Rubber.env)
|
73
|
+
scoped_env = rubber_cfg.environment.bind(roles, host)
|
74
|
+
security_group_defns = Hash[scoped_env.security_groups.to_a]
|
75
|
+
|
76
|
+
if scoped_env.auto_security_groups
|
77
|
+
sghosts = (scoped_env.rubber_instances.collect{|ic| ic.name } + [host]).uniq.compact
|
78
|
+
sgroles = (scoped_env.rubber_instances.all_roles + roles).uniq.compact
|
79
|
+
security_group_defns = inject_auto_security_groups(security_group_defns, sghosts, sgroles)
|
80
|
+
end
|
81
|
+
|
82
|
+
sync_security_groups(vpc_id, security_group_defns)
|
83
|
+
end
|
84
|
+
|
85
|
+
def setup_vpc(vpc_alias, vpc_cidr)
|
86
|
+
bound_env = load_bound_env
|
87
|
+
|
88
|
+
# First, check to see if the VPC is defined in the instance file. If it
|
89
|
+
# isn't, then check AWS for any VPCs with the same tag:RubberAlias.
|
90
|
+
# Failing that, create it
|
91
|
+
|
92
|
+
vpc = compute_provider.vpcs.all("tag:RubberAlias" => vpc_alias).first
|
93
|
+
vpc_id = vpc && vpc.id
|
94
|
+
|
95
|
+
if vpc_id
|
96
|
+
capistrano.logger.debug "Using #{vpc_id} #{vpc_alias}"
|
97
|
+
else
|
98
|
+
vpc = create_vpc(
|
99
|
+
"#{bound_env.app_name} #{Rubber.env}",
|
100
|
+
vpc_alias,
|
101
|
+
vpc_cidr
|
102
|
+
)
|
103
|
+
|
104
|
+
vpc_id = vpc.id
|
105
|
+
|
106
|
+
capistrano.logger.debug "Created #{vpc_id} #{vpc_alias}"
|
107
|
+
end
|
108
|
+
|
109
|
+
vpc
|
110
|
+
end
|
111
|
+
|
112
|
+
def setup_vpc_subnet(vpc_id, vpc_alias, private_nic, availability_zone, name)
|
113
|
+
subnet = find_or_create_vpc_subnet(
|
114
|
+
vpc_id,
|
115
|
+
vpc_alias,
|
116
|
+
name,
|
117
|
+
availability_zone,
|
118
|
+
private_nic.subnet_cidr,
|
119
|
+
private_nic.gateway
|
120
|
+
)
|
121
|
+
|
122
|
+
capistrano.logger.debug "Using #{subnet.subnet_id} #{name}"
|
123
|
+
|
124
|
+
subnet
|
125
|
+
end
|
126
|
+
|
127
|
+
def destroy_vpc(vpc_alias)
|
128
|
+
%w[
|
129
|
+
subnets
|
130
|
+
route_tables
|
131
|
+
security_groups
|
132
|
+
internet_gateways
|
133
|
+
].each do |resource_name|
|
134
|
+
destroy_vpc_resource(vpc_alias, resource_name.strip)
|
135
|
+
end
|
136
|
+
|
137
|
+
vpc = compute_provider.vpcs.all('tag:RubberAlias' => vpc_alias).first
|
138
|
+
if vpc
|
139
|
+
compute_provider.vpcs.destroy(vpc.id)
|
140
|
+
|
141
|
+
capistrano.logger.info "Destroyed #{vpc.id} #{vpc_alias}"
|
142
|
+
else
|
143
|
+
capistrano.logger.info "No VPC found with alias #{vpc_alias}"
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
def describe_vpcs
|
148
|
+
compute_provider.vpcs.all.map do |vpc|
|
149
|
+
subnets = compute_provider.subnets.all('vpc-id' => vpc.id).map do |subnet|
|
150
|
+
tags = compute_provider.tags.all('resource-id' => subnet.subnet_id)
|
151
|
+
|
152
|
+
{
|
153
|
+
id: subnet.subnet_id,
|
154
|
+
public: (tags.to_s == 'true'),
|
155
|
+
cidr_block: subnet.cidr_block
|
156
|
+
}
|
157
|
+
end
|
158
|
+
|
159
|
+
{
|
160
|
+
id: vpc.id,
|
161
|
+
name: vpc.tags['Name'],
|
162
|
+
rubber_alias: vpc.tags['RubberAlias'],
|
163
|
+
subnets: subnets
|
164
|
+
}
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
def destroy_internet_gateways(vpc_alias)
|
169
|
+
vpc = compute_provider.vpcs.all("tag:RubberAlias" => vpc_alias).first
|
170
|
+
gateways = compute_provider.internet_gateways.all('tag:RubberVpcAlias' => vpc_alias)
|
171
|
+
|
172
|
+
gateways.each do |gateway|
|
173
|
+
compute_provider.detach_internet_gateway(gateway.id, vpc.id)
|
174
|
+
|
175
|
+
sleep 5
|
176
|
+
|
177
|
+
gateway.reload
|
178
|
+
|
179
|
+
if gateway.attachment_set.length > 0
|
180
|
+
compute_provider.delete_tags gateway.id, { "RubberVpcAlias" => vpc_alias }
|
181
|
+
capistrano.logger.info "not destroying #{gateway.id} due to other VPC attachments"
|
182
|
+
else
|
183
|
+
gateway.destroy
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
capistrano.logger.info "destroyed internet_gateways"
|
188
|
+
end
|
189
|
+
|
190
|
+
def destroy_security_groups(vpc_alias)
|
191
|
+
vpc = compute_provider.vpcs.all("tag:RubberAlias" => vpc_alias).first
|
192
|
+
|
193
|
+
groups = compute_provider.security_groups.all('vpc-id' => vpc.id)
|
194
|
+
|
195
|
+
groups.all.each do |group|
|
196
|
+
begin
|
197
|
+
group.destroy
|
198
|
+
rescue ::Fog::Compute::AWS::Error => e
|
199
|
+
# Some groups cannot be deleted by users. Just ignore these
|
200
|
+
raise e unless e.message =~ /CannotDelete/
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
capistrano.logger.info "destroyed security_groups"
|
205
|
+
end
|
206
|
+
|
207
|
+
def destroy_vpc_resource(vpc_alias, resource_name_plural)
|
208
|
+
specific_call = "destroy_#{resource_name_plural}"
|
209
|
+
|
210
|
+
if self.respond_to? specific_call
|
211
|
+
should_destroy = self.send specific_call, vpc_alias
|
212
|
+
else
|
213
|
+
resources = compute_provider.send(resource_name_plural).all('tag:RubberVpcAlias' => vpc_alias)
|
214
|
+
|
215
|
+
resources.each(&:destroy)
|
216
|
+
|
217
|
+
capistrano.logger.info "destroyed #{resource_name_plural}"
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
def describe_security_groups(vpc_id, group_name=nil)
|
222
|
+
groups = []
|
223
|
+
|
224
|
+
# As of 10/2/2015, vpcId isn't a valid filter, so we have to filter
|
225
|
+
# manually
|
226
|
+
opts = {
|
227
|
+
'vpc-id' => vpc_id
|
228
|
+
}
|
229
|
+
opts["group-name"] = group_name if group_name
|
230
|
+
response = compute_provider.security_groups.all(opts)
|
231
|
+
|
232
|
+
response.each do |item|
|
233
|
+
group = {}
|
234
|
+
group[:group_id] = item.group_id
|
235
|
+
group[:name] = item.name
|
236
|
+
group[:description] = item.description
|
237
|
+
|
238
|
+
item.ip_permissions.each do |ip_item|
|
239
|
+
group[:permissions] ||= []
|
240
|
+
rule = {}
|
241
|
+
|
242
|
+
rule[:protocol] = ip_item["ipProtocol"]
|
243
|
+
rule[:from_port] = ip_item["fromPort"]
|
244
|
+
rule[:to_port] = ip_item["toPort"]
|
245
|
+
|
246
|
+
ip_item["groups"].each do |rule_group|
|
247
|
+
rule[:source_groups] ||= []
|
248
|
+
source_group = {}
|
249
|
+
source_group[:account] = rule_group["userId"]
|
250
|
+
|
251
|
+
# Amazon doesn't appear to be returning the groupName value when running in a default VPC. It's possible
|
252
|
+
# it's only returned for EC2 Classic. This is distinctly in conflict with the API documents and thus
|
253
|
+
# appears to be a bug on Amazon's end. Nonetheless, we need to handle it because otherwise our security
|
254
|
+
# group rule matching logic will fail and it messes up our users.
|
255
|
+
#
|
256
|
+
# Since every top-level item has both an ID and a name, if we're lacking the groupName we can search
|
257
|
+
# through the items for the one matching the groupId we have and then use its name value. This should
|
258
|
+
# represent precisely the same data.
|
259
|
+
source_group[:name] = if rule_group["groupName"]
|
260
|
+
rule_group["groupName"]
|
261
|
+
elsif rule_group["groupId"]
|
262
|
+
matching_security_group = response.find { |item| item.group_id == rule_group["groupId"] }
|
263
|
+
matching_security_group ? matching_security_group.name : nil
|
264
|
+
else
|
265
|
+
nil
|
266
|
+
end
|
267
|
+
|
268
|
+
source_group[:group_id] = rule_group["groupId"]
|
269
|
+
|
270
|
+
rule[:source_groups] << source_group
|
271
|
+
end if ip_item["groups"]
|
272
|
+
|
273
|
+
ip_item["ipRanges"].each do |ip_range|
|
274
|
+
rule[:source_ips] ||= []
|
275
|
+
rule[:source_ips] << ip_range["cidrIp"]
|
276
|
+
end if ip_item["ipRanges"]
|
277
|
+
|
278
|
+
group[:permissions] << rule
|
279
|
+
end
|
280
|
+
|
281
|
+
groups << group
|
282
|
+
end
|
283
|
+
|
284
|
+
groups
|
285
|
+
end
|
286
|
+
|
287
|
+
private
|
288
|
+
|
289
|
+
def create_vpc(name, vpc_alias, subnet_str)
|
290
|
+
vpc = compute_provider.vpcs.create(:cidr_block => subnet_str)
|
291
|
+
|
292
|
+
Rubber::Util.retry_on_failure(StandardError, :retry_sleep => 1, :retry_count => 120) do
|
293
|
+
create_tags(vpc.id,
|
294
|
+
:Name => name,
|
295
|
+
:Environment => Rubber.env,
|
296
|
+
:RubberAlias => vpc_alias)
|
297
|
+
end
|
298
|
+
|
299
|
+
vpc
|
300
|
+
end
|
301
|
+
|
302
|
+
def find_or_create_vpc_subnet(vpc_id, vpc_alias, name, availability_zone, cidr_block, gateway)
|
303
|
+
unless Rubber::Util.is_instance_id?(gateway) ||
|
304
|
+
Rubber::Util.is_internet_gateway_id?(gateway) ||
|
305
|
+
(gateway == 'public')
|
306
|
+
raise "gateway must be an instance id, gateway id, or \"public\""
|
307
|
+
end
|
308
|
+
|
309
|
+
subnet = compute_provider.subnets.all(
|
310
|
+
'tag:RubberVpcAlias' => vpc_alias,
|
311
|
+
'cidr-block' => cidr_block,
|
312
|
+
'availability-zone' => availability_zone
|
313
|
+
).first
|
314
|
+
|
315
|
+
unless subnet
|
316
|
+
subnet = compute_provider.subnets.create :vpc_id => vpc_id,
|
317
|
+
:cidr_block => cidr_block,
|
318
|
+
:availability_zone => availability_zone
|
319
|
+
|
320
|
+
|
321
|
+
Rubber::Util.retry_on_failure(StandardError, :retry_sleep => 1, :retry_count => 120) do
|
322
|
+
create_tags(subnet.subnet_id,
|
323
|
+
'Name' => name,
|
324
|
+
'Environment' => Rubber.env,
|
325
|
+
'RubberVpcAlias' => vpc_alias
|
326
|
+
)
|
327
|
+
end
|
328
|
+
|
329
|
+
route_table = compute_provider.route_tables.all(
|
330
|
+
'vpc-id' => vpc_id,
|
331
|
+
'association.subnet-id' => subnet.subnet_id,
|
332
|
+
'tag:RubberVpcAlias' => vpc_alias
|
333
|
+
).first
|
334
|
+
|
335
|
+
route_table_id = route_table && route_table.id
|
336
|
+
|
337
|
+
unless route_table_id
|
338
|
+
resp = compute_provider.create_route_table(vpc_id)
|
339
|
+
|
340
|
+
route_table_id = resp.body['routeTable'].first['routeTableId']
|
341
|
+
|
342
|
+
Rubber::Util.retry_on_failure(StandardError, :retry_sleep => 1, :retry_count => 120) do
|
343
|
+
create_tags(
|
344
|
+
route_table_id,
|
345
|
+
'Name' => name,
|
346
|
+
'Environment' => Rubber.env,
|
347
|
+
'RubberVpcAlias' => vpc_alias,
|
348
|
+
'Public' => (gateway == 'public')
|
349
|
+
)
|
350
|
+
end
|
351
|
+
|
352
|
+
compute_provider.associate_route_table(route_table_id, subnet.subnet_id)
|
353
|
+
end
|
354
|
+
|
355
|
+
if Rubber::Util.is_instance_id?(gateway)
|
356
|
+
compute_provider.create_route(route_table_id, "0.0.0.0/0", nil, gateway)
|
357
|
+
elsif Rubber::Util.is_internet_gateway_id?(gateway)
|
358
|
+
compute_provider.create_route(route_table_id, "0.0.0.0/0", gateway)
|
359
|
+
else
|
360
|
+
internet_gateway = find_or_create_vpc_internet_gateway(vpc_id, vpc_alias, "#{name} gateway")
|
361
|
+
|
362
|
+
compute_provider.create_route(route_table_id, "0.0.0.0/0", internet_gateway.id)
|
363
|
+
|
364
|
+
capistrano.logger.debug "Created #{subnet.subnet_id} #{name}"
|
365
|
+
end
|
366
|
+
end
|
367
|
+
|
368
|
+
subnet
|
369
|
+
end
|
370
|
+
|
371
|
+
def find_or_create_vpc_internet_gateway(vpc_id, vpc_alias, name)
|
372
|
+
gateway = compute_provider.internet_gateways.all(
|
373
|
+
'tag:RubberVpcAlias' => vpc_alias
|
374
|
+
).first
|
375
|
+
|
376
|
+
unless gateway
|
377
|
+
gateway = compute_provider.internet_gateways.create
|
378
|
+
gateway.attach(vpc_id)
|
379
|
+
Rubber::Util.retry_on_failure(StandardError, :retry_sleep => 1, :retry_count => 120) do
|
380
|
+
create_tags(
|
381
|
+
gateway.id,
|
382
|
+
'Name' => name,
|
383
|
+
'Environment' => Rubber.env,
|
384
|
+
'RubberVpcAlias' => vpc_alias
|
385
|
+
)
|
386
|
+
end
|
387
|
+
|
388
|
+
capistrano.logger.debug "Created #{gateway.id} #{name}"
|
389
|
+
end
|
390
|
+
|
391
|
+
gateway
|
392
|
+
end
|
393
|
+
|
394
|
+
def destroy_subnet(subnet_id)
|
395
|
+
compute_provider.subnets.destroy(subnet_id)
|
396
|
+
end
|
397
|
+
|
398
|
+
def create_security_group(vpc_id, group_name, group_description)
|
399
|
+
compute_provider.security_groups.create :vpc_id => vpc_id,
|
400
|
+
:name => group_name,
|
401
|
+
:description => group_description
|
402
|
+
end
|
403
|
+
|
404
|
+
def destroy_security_group(group_id)
|
405
|
+
compute_provider.security_groups.get(group_id).destroy
|
406
|
+
end
|
407
|
+
|
408
|
+
def add_security_group_rule(group_id, protocol, from_port, to_port, source)
|
409
|
+
group = compute_provider.security_groups.all('group-id' => group_id).first
|
410
|
+
# ip_protocol of -1 means all
|
411
|
+
# See: http://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_AuthorizeSecurityGroupIngress.html
|
412
|
+
opts = {:ip_protocol => protocol || '-1' }
|
413
|
+
|
414
|
+
if source.instance_of? Hash
|
415
|
+
opts[:group] = {source[:account] => (source[:id] || source[:name])}
|
416
|
+
else
|
417
|
+
opts[:cidr_ip] = source
|
418
|
+
end
|
419
|
+
|
420
|
+
# VPC Security Rules sometimes have nil to/from ports which means the
|
421
|
+
# entire range is authorized
|
422
|
+
from_port = 0 if from_port.nil?
|
423
|
+
to_port = 65535 if to_port.nil?
|
424
|
+
|
425
|
+
group.authorize_port_range(from_port.to_i..to_port.to_i, opts)
|
426
|
+
end
|
427
|
+
|
428
|
+
def remove_security_group_rule(group_id, protocol, from_port, to_port, source)
|
429
|
+
group = compute_provider.security_groups.get(group_id)
|
430
|
+
opts = {:ip_protocol => protocol || 'tcp'}
|
431
|
+
|
432
|
+
if source.instance_of? Hash
|
433
|
+
opts[:group] = {source[:account] => source[:name]}
|
434
|
+
else
|
435
|
+
opts[:cidr_ip] = source
|
436
|
+
end
|
437
|
+
|
438
|
+
group.revoke_port_range(from_port.to_i..to_port.to_i, opts)
|
439
|
+
end
|
440
|
+
|
441
|
+
def sync_security_groups(vpc_id, groups)
|
442
|
+
return unless groups
|
443
|
+
|
444
|
+
groups = Rubber::Util::stringify(groups)
|
445
|
+
groups = isolate_groups(groups)
|
446
|
+
group_keys = groups.keys.clone()
|
447
|
+
|
448
|
+
# For each group that does already exist in cloud
|
449
|
+
cloud_groups = describe_security_groups(vpc_id)
|
450
|
+
cloud_groups.each do |cloud_group|
|
451
|
+
group_name = cloud_group[:name]
|
452
|
+
group_id = cloud_group[:group_id]
|
453
|
+
|
454
|
+
# skip those groups that don't belong to this project/env
|
455
|
+
next if env.isolate_security_groups && group_name !~ /^#{isolate_prefix}/
|
456
|
+
|
457
|
+
if group_keys.delete(group_name)
|
458
|
+
# sync rules
|
459
|
+
capistrano.logger.debug "Security Group already in cloud, syncing rules: #{group_name}"
|
460
|
+
group = groups[group_name]
|
461
|
+
|
462
|
+
# Convert the special case default rule into what it actually looks like when
|
463
|
+
# we query ec2 so that we can match things up when syncing. Also,
|
464
|
+
# retain a reference to the default rule so we can add the source_group_id
|
465
|
+
# when we sync from the cloud
|
466
|
+
default_rules = []
|
467
|
+
|
468
|
+
rules = group['rules'].clone
|
469
|
+
group['rules'].each do |rule|
|
470
|
+
if [2, 3].include?(rule.size) && rule['source_group_name'] && rule['source_group_account']
|
471
|
+
# source_group_id value will be populated when we fetch rules from the cloud
|
472
|
+
# TODO we appear to have a mismatch on source_group_account for some reason
|
473
|
+
default_rule = rule.merge({
|
474
|
+
"source_group_id" => nil,
|
475
|
+
"protocol" => "-1",
|
476
|
+
"from_port" => "",
|
477
|
+
"to_port" => ""
|
478
|
+
})
|
479
|
+
rules << default_rule
|
480
|
+
default_rules << default_rule
|
481
|
+
rules.delete(rule)
|
482
|
+
end
|
483
|
+
end
|
484
|
+
|
485
|
+
rule_maps = []
|
486
|
+
|
487
|
+
# first collect the rule maps from the request (group/user pairs are duplicated for tcp/udp/icmp,
|
488
|
+
# so we need to do this up frnot and remove duplicates before checking against the local rubber rules)
|
489
|
+
cloud_group[:permissions].each do |rule|
|
490
|
+
source_groups = rule.delete(:source_groups)
|
491
|
+
if source_groups
|
492
|
+
source_groups.each do |source_group|
|
493
|
+
rule_map = rule.clone
|
494
|
+
rule_map.delete(:source_ips)
|
495
|
+
rule_map[:source_group_name] = source_group[:name]
|
496
|
+
rule_map[:source_group_id] = source_group[:group_id]
|
497
|
+
rule_map[:source_group_account] = source_group[:account]
|
498
|
+
|
499
|
+
# Update the special case default rule with the group id if
|
500
|
+
# appropriate
|
501
|
+
if (rule_map[:protocol] == "-1") && rule_map[:to_port].nil? && rule_map[:from_port].nil?
|
502
|
+
default_rules.each do |default_rule|
|
503
|
+
if default_rule['source_group_account'] == source_group[:account]
|
504
|
+
default_rule['source_group_id'] = rule_map[:source_group_id]
|
505
|
+
end
|
506
|
+
end
|
507
|
+
end
|
508
|
+
|
509
|
+
rule_map = Rubber::Util::stringify(rule_map)
|
510
|
+
rule_maps << rule_map unless rule_maps.include?(rule_map)
|
511
|
+
end
|
512
|
+
else
|
513
|
+
rule_map = Rubber::Util::stringify(rule)
|
514
|
+
rule_maps << rule_map unless rule_maps.include?(rule_map)
|
515
|
+
end
|
516
|
+
end if cloud_group[:permissions]
|
517
|
+
|
518
|
+
# For each rule, if it exists, do nothing, otherwise remove it as its no longer defined locally
|
519
|
+
rule_maps.each do |rule_map|
|
520
|
+
if rules.delete(rule_map)
|
521
|
+
# rules match, don't need to do anything
|
522
|
+
# logger.debug "Rule in sync: #{rule_map.inspect}"
|
523
|
+
else
|
524
|
+
# rules don't match, remove them from cloud and re-add below
|
525
|
+
answer = nil
|
526
|
+
msg = "Rule '#{rule_map.inspect}' exists in cloud, but not locally"
|
527
|
+
if env.prompt_for_security_group_sync
|
528
|
+
answer = Capistrano::CLI.ui.ask("#{msg}, remove from cloud? [y/N]: ")
|
529
|
+
else
|
530
|
+
capistrano.logger.info(msg)
|
531
|
+
end
|
532
|
+
|
533
|
+
if answer =~ /^y/
|
534
|
+
rule_map = Rubber::Util::symbolize_keys(rule_map)
|
535
|
+
if rule_map[:source_group_id]
|
536
|
+
remove_security_group_rule(group_id, rule_map[:protocol], rule_map[:from_port], rule_map[:to_port], {:id => rule_map[:source_group_id], :account => rule_map[:source_group_account]})
|
537
|
+
else
|
538
|
+
rule_map[:source_ips].each do |source_ip|
|
539
|
+
remove_security_group_rule(group_id, rule_map[:protocol], rule_map[:from_port], rule_map[:to_port], source_ip)
|
540
|
+
end if rule_map[:source_ips]
|
541
|
+
end
|
542
|
+
end
|
543
|
+
end
|
544
|
+
end
|
545
|
+
|
546
|
+
rules.each do |rule_map|
|
547
|
+
# create non-existing rules
|
548
|
+
capistrano.logger.debug "Missing rule, creating: #{rule_map.inspect}"
|
549
|
+
rule_map = Rubber::Util::symbolize_keys(rule_map)
|
550
|
+
if rule_map[:source_group_id]
|
551
|
+
add_security_group_rule(group_id, rule_map[:protocol], rule_map[:from_port], rule_map[:to_port], {:id => rule_map[:source_group_id], :account => rule_map[:source_group_account]})
|
552
|
+
else
|
553
|
+
rule_map[:source_ips].each do |source_ip|
|
554
|
+
add_security_group_rule(group_id, rule_map[:protocol], rule_map[:from_port], rule_map[:to_port], source_ip)
|
555
|
+
end if rule_map[:source_ips]
|
556
|
+
end
|
557
|
+
end
|
558
|
+
else
|
559
|
+
# delete group
|
560
|
+
answer = nil
|
561
|
+
msg = "Security group '#{group_name}' exists in cloud but not locally"
|
562
|
+
if env.prompt_for_security_group_sync
|
563
|
+
answer = Capistrano::CLI.ui.ask("#{msg}, remove from cloud? [y/N]: ")
|
564
|
+
else
|
565
|
+
capistrano.logger.debug(msg)
|
566
|
+
end
|
567
|
+
destroy_security_group(group_name) if answer =~ /^y/
|
568
|
+
end
|
569
|
+
end
|
570
|
+
|
571
|
+
# For each group that didnt already exist in cloud
|
572
|
+
group_keys.each do |group_name|
|
573
|
+
group = groups[group_name]
|
574
|
+
capistrano.logger.debug "Creating new security group: #{group_name}"
|
575
|
+
# create each group
|
576
|
+
new_group = create_security_group(vpc_id, group_name, group['description'])
|
577
|
+
group_id = new_group.group_id
|
578
|
+
# create rules for group
|
579
|
+
group['rules'].each do |rule_map|
|
580
|
+
capistrano.logger.debug "Creating new rule: #{rule_map.inspect}"
|
581
|
+
rule_map = Rubber::Util::symbolize_keys(rule_map)
|
582
|
+
if rule_map[:source_group_name]
|
583
|
+
source = { :account => rule_map[:source_group_account] }
|
584
|
+
|
585
|
+
if rule_map[:source_group_id]
|
586
|
+
source[:id] = rule_map[:source_group_id]
|
587
|
+
elsif rule_map[:source_group_name]
|
588
|
+
cloud_group = describe_security_groups(vpc_id, rule_map[:source_group_name]).first
|
589
|
+
|
590
|
+
if cloud_group
|
591
|
+
source[:id] = cloud_group[:group_id]
|
592
|
+
end
|
593
|
+
end
|
594
|
+
|
595
|
+
add_security_group_rule(group_id, rule_map[:protocol], rule_map[:from_port], rule_map[:to_port], source)
|
596
|
+
else
|
597
|
+
rule_map[:source_ips].each do |source_ip|
|
598
|
+
add_security_group_rule(group_id, rule_map[:protocol], rule_map[:from_port], rule_map[:to_port], source_ip)
|
599
|
+
end if rule_map[:source_ips]
|
600
|
+
end
|
601
|
+
end
|
602
|
+
end
|
603
|
+
end
|
604
|
+
|
605
|
+
def load_bound_env(host=nil)
|
606
|
+
rubber_cfg = Rubber::Configuration.get_configuration(Rubber.env)
|
607
|
+
scoped_env = rubber_cfg.environment.bind(nil, host)
|
608
|
+
end
|
609
|
+
end
|
610
|
+
end
|
611
|
+
end
|
612
|
+
|