Class: Rubber::Cloud::Aws::Vpc

Inherits:
Base show all
Defined in:
lib/rubber/cloud/aws/vpc.rb

Instance Attribute Summary

Attributes inherited from Base

#capistrano, #env

Instance Method Summary collapse

Methods inherited from Base

#active_state, #after_create_volume, #after_refresh_instance, #after_start_instance, #before_destroy_volume, #before_start_instance, #before_stop_instance, #create_image, #create_spot_instance_request, #create_tags, #create_volume, #describe_availability_zones, #describe_instances, #describe_load_balancers, #describe_spot_instance_requests, #describe_volumes, #destroy_image, #destroy_volume, #initialize, #stopped_state, #table_store

Methods inherited from Fog

#after_create_volume, #after_destroy_volume, #attach_static_ip, #before_create_volume, #before_destroy_volume, #compute_provider, #create_image, #create_instance, #create_static_ip, #create_volume, #describe_images, #describe_load_balancers, #describe_static_ips, #destroy_image, #destroy_instance, #destroy_spot_instance_request, #destroy_static_ip, #destroy_volume, #detach_static_ip, #initialize, #reboot_instance, #should_destroy_volume_when_instance_destroyed?, #start_instance, #stop_instance, #storage, #storage_provider, #table_store

Methods inherited from Base

#active_state, #after_refresh_instance, #after_start_instance, #after_stop_instance, #before_refresh_instance, #before_start_instance, #before_stop_instance, #initialize, #inject_auto_security_groups, #isolate_group_name, #isolate_groups, #isolate_prefix, #should_disable_password_based_ssh_login?

Constructor Details

This class inherits a constructor from Rubber::Cloud::Aws::Base

Instance Method Details

#after_create_instance(instance) ⇒ Object



41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
# File 'lib/rubber/cloud/aws/vpc.rb', line 41

def after_create_instance(instance)
  super

  # Creating an instance with both a subnet id and groups doesn't seem to
  # result in the groups actually sticking.  Lucky, VPC instances have
  # mutable security groups
  group_ids = describe_security_groups(instance.vpc_id).map { |g|
    if instance.security_groups.include?(g[:name])
      g[:group_id]
    else
      nil
    end
  }.compact

  compute_provider.modify_instance_attribute(instance.instance_id, {
                                               'GroupId' => group_ids
                                             })

  if instance.roles.map(&:name).include? "nat_gateway"
    # NAT gateways need the sourceDestCheck attribute to be false for AWS
    # to allow them to route traffic
    server = compute_provider.servers.get(instance.instance_id)

    server.network_interfaces.each do |interface|
      # Sometimes we get a blank interface back
      next unless interface.count > 0

      interface_id = interface['networkInterfaceId']

      compute_provider.modify_network_interface_attribute(
        interface_id,
        'sourceDestCheck',
        false
      )
    end
  end
end

#before_create_instance(instance) ⇒ Object



9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# File 'lib/rubber/cloud/aws/vpc.rb', line 9

def before_create_instance(instance)
  host_env = load_bound_env(instance.name)
  cloud_env = host_env.cloud_providers[env.cloud_provider]

  instance.network = cloud_env.vpc_alias
  instance.vpc_cidr = cloud_env.vpc_cidr

  # Remember that instance.network is our more generic term for vpc_alias
  role_names = instance.roles.map(&:name)
  instance.vpc_id = setup_vpc(instance.network, instance.vpc_cidr).id

  # Check the instance config for a private_nic, then the cloud provider
  private_nic = host_env.private_nic || cloud_env.private_nic

  unless private_nic
    raise "private_nic configuration required at either the instance level or cloud provider level"
  end

  instance.gateway = private_nic.gateway
  private_public = instance.gateway == 'public' ? 'public' : 'private'

  instance.subnet_id = setup_vpc_subnet(
    instance.vpc_id,
    instance.network,
    private_nic,
    instance.zone,
    "#{instance.network} #{instance.zone} #{private_public}"
  ).subnet_id

  setup_security_groups(instance.vpc_id, instance.name, instance.role_names)
end

#describe_security_groups(vpc_id, group_name = nil) ⇒ Object



229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
# File 'lib/rubber/cloud/aws/vpc.rb', line 229

def describe_security_groups(vpc_id, group_name=nil)
  groups = []

  # As of 10/2/2015, vpcId isn't a valid filter, so we have to filter
  # manually
  opts = {
    'vpc-id' => vpc_id
  }
  opts["group-name"] = group_name if group_name
  response = compute_provider.security_groups.all(opts)

  response.each do |item|
    group = {}
    group[:group_id] = item.group_id
    group[:name] = item.name
    group[:description] = item.description

    item.ip_permissions.each do |ip_item|
      group[:permissions] ||= []
      rule = {}

      rule[:protocol] = ip_item["ipProtocol"]
      rule[:from_port] = ip_item["fromPort"]
      rule[:to_port] = ip_item["toPort"]

      ip_item["groups"].each do |rule_group|
        rule[:source_groups] ||= []
        source_group = {}
        source_group[:account] = rule_group["userId"]

        # Amazon doesn't appear to be returning the groupName value when running in a default VPC.  It's possible
        # it's only returned for EC2 Classic.  This is distinctly in conflict with the API documents and thus
        # appears to be a bug on Amazon's end.  Nonetheless, we need to handle it because otherwise our security
        # group rule matching logic will fail and it messes up our users.
        #
        # Since every top-level item has both an ID and a name, if we're lacking the groupName we can search
        # through the items for the one matching the groupId we have and then use its name value.  This should
        # represent precisely the same data.
        source_group[:name] = if rule_group["groupName"]
                                rule_group["groupName"]
                              elsif rule_group["groupId"]
                                matching_security_group = response.find { |item| item.group_id == rule_group["groupId"] }
                                matching_security_group ? matching_security_group.name : nil
                              else
                                nil
                              end

        source_group[:group_id] = rule_group["groupId"]

        rule[:source_groups] << source_group
      end if ip_item["groups"]

      ip_item["ipRanges"].each do |ip_range|
        rule[:source_ips] ||= []
        rule[:source_ips] << ip_range["cidrIp"]
      end if ip_item["ipRanges"]

      group[:permissions] << rule
    end

    groups << group
  end

  groups
end

#describe_vpcsObject



155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
# File 'lib/rubber/cloud/aws/vpc.rb', line 155

def describe_vpcs
  compute_provider.vpcs.all.map do |vpc|
    subnets = compute_provider.subnets.all('vpc-id' => vpc.id).map do |subnet|
      tags = compute_provider.tags.all('resource-id' => subnet.subnet_id)

      {
        id: subnet.subnet_id,
        public: (tags.to_s == 'true'),
        cidr_block: subnet.cidr_block
      }
    end

    {
      id: vpc.id,
      name: vpc.tags['Name'],
      rubber_alias: vpc.tags['RubberAlias'],
      subnets: subnets
    }
  end
end

#destroy_internet_gateways(vpc_alias) ⇒ Object



176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
# File 'lib/rubber/cloud/aws/vpc.rb', line 176

def destroy_internet_gateways(vpc_alias)
  vpc = compute_provider.vpcs.all("tag:RubberAlias" => vpc_alias).first
  gateways = compute_provider.internet_gateways.all('tag:RubberVpcAlias' => vpc_alias)

  gateways.each do |gateway|
    compute_provider.detach_internet_gateway(gateway.id, vpc.id)

    sleep 5

    gateway.reload

    if gateway.attachment_set.length > 0
      compute_provider.delete_tags gateway.id, { "RubberVpcAlias" => vpc_alias }
      capistrano.logger.info "not destroying #{gateway.id} due to other VPC attachments"
    else
      gateway.destroy
    end
  end

  capistrano.logger.info "destroyed internet_gateways"
end

#destroy_security_groups(vpc_alias) ⇒ Object



198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
# File 'lib/rubber/cloud/aws/vpc.rb', line 198

def destroy_security_groups(vpc_alias)
  vpc = compute_provider.vpcs.all("tag:RubberAlias" => vpc_alias).first

  groups = compute_provider.security_groups.all('vpc-id' => vpc.id)

  groups.all.each do |group|
    begin
      group.destroy
    rescue ::Fog::Compute::AWS::Error => e
      # Some groups cannot be deleted by users.  Just ignore these
      raise e unless e.message =~ /CannotDelete/
    end
  end

  capistrano.logger.info "destroyed security_groups"
end

#destroy_vpc(vpc_alias) ⇒ Object



135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
# File 'lib/rubber/cloud/aws/vpc.rb', line 135

def destroy_vpc(vpc_alias)
  %w[
     subnets
     route_tables
     security_groups
     internet_gateways 
  ].each do |resource_name|
    destroy_vpc_resource(vpc_alias, resource_name.strip)
  end

  vpc = compute_provider.vpcs.all('tag:RubberAlias' => vpc_alias).first
  if vpc
    compute_provider.vpcs.destroy(vpc.id)

    capistrano.logger.info "Destroyed #{vpc.id} #{vpc_alias}"
  else
    capistrano.logger.info "No VPC found with alias #{vpc_alias}"
  end
end

#destroy_vpc_resource(vpc_alias, resource_name_plural) ⇒ Object



215
216
217
218
219
220
221
222
223
224
225
226
227
# File 'lib/rubber/cloud/aws/vpc.rb', line 215

def destroy_vpc_resource(vpc_alias, resource_name_plural)
  specific_call = "destroy_#{resource_name_plural}"

  if self.respond_to? specific_call
    should_destroy = self.send specific_call, vpc_alias
  else
    resources = compute_provider.send(resource_name_plural).all('tag:RubberVpcAlias' => vpc_alias)

    resources.each(&:destroy)

    capistrano.logger.info "destroyed #{resource_name_plural}"
  end
end

#setup_security_groups(vpc_id, host = nil, roles = []) ⇒ Object



79
80
81
82
83
84
85
86
87
88
89
90
91
# File 'lib/rubber/cloud/aws/vpc.rb', line 79

def setup_security_groups(vpc_id, host=nil, roles=[])
  rubber_cfg = Rubber::Configuration.get_configuration(Rubber.env)
  scoped_env = rubber_cfg.environment.bind(roles, host)
  security_group_defns = Hash[scoped_env.security_groups.to_a]

  if scoped_env.auto_security_groups
    sghosts = (scoped_env.rubber_instances.collect{|ic| ic.name } + [host]).uniq.compact
    sgroles = (scoped_env.rubber_instances.all_roles + roles).uniq.compact
    security_group_defns = inject_auto_security_groups(security_group_defns, sghosts, sgroles)
  end

  sync_security_groups(vpc_id, security_group_defns)
end

#setup_vpc(vpc_alias, vpc_cidr) ⇒ Object



93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
# File 'lib/rubber/cloud/aws/vpc.rb', line 93

def setup_vpc(vpc_alias, vpc_cidr)
  bound_env = load_bound_env

  # First, check to see if the VPC is defined in the instance file.  If it
  # isn't, then check AWS for any VPCs with the same tag:RubberAlias.
  # Failing that, create it

  vpc = compute_provider.vpcs.all("tag:RubberAlias" => vpc_alias).first
  vpc_id = vpc && vpc.id

  if vpc_id
    capistrano.logger.debug "Using #{vpc_id} #{vpc_alias}"
  else
    vpc = create_vpc(
      "#{bound_env.app_name} #{Rubber.env}",
      vpc_alias,
      vpc_cidr
    )

    vpc_id = vpc.id

    capistrano.logger.debug "Created #{vpc_id} #{vpc_alias}"
  end

  vpc
end

#setup_vpc_subnet(vpc_id, vpc_alias, private_nic, availability_zone, name) ⇒ Object



120
121
122
123
124
125
126
127
128
129
130
131
132
133
# File 'lib/rubber/cloud/aws/vpc.rb', line 120

def setup_vpc_subnet(vpc_id, vpc_alias, private_nic, availability_zone, name)
  subnet = find_or_create_vpc_subnet(
    vpc_id,
    vpc_alias,
    name,
    availability_zone,
    private_nic.subnet_cidr,
    private_nic.gateway
  )

  capistrano.logger.debug "Using #{subnet.subnet_id} #{name}"

  subnet
end