class Terrafying::Components::VPC
Attributes
azs[R]
cidr[R]
id[R]
internal_ssh_security_group[R]
name[R]
ssh_group[R]
subnets[R]
zone[R]
Public Class Methods
create(name, cidr, options = {})
click to toggle source
# File lib/terrafying/components/vpc.rb, line 22 def self.create(name, cidr, options = {}) VPC.new.create name, cidr, options end
find(name)
click to toggle source
# File lib/terrafying/components/vpc.rb, line 18 def self.find(name) VPC.new.find name end
new()
click to toggle source
Calls superclass method
# File lib/terrafying/components/vpc.rb, line 26 def initialize super end
Public Instance Methods
allocate_subnets!(name, options = {})
click to toggle source
# File lib/terrafying/components/vpc.rb, line 396 def allocate_subnets!(name, options = {}) allocate_subnets_in!(self, name, options) end
allocate_subnets_in!(ctx, name, options = {})
click to toggle source
# File lib/terrafying/components/vpc.rb, line 400 def allocate_subnets_in!(ctx, name, options = {}) options = { public: false, bit_size: @subnet_size, internet: true, tags: {} }.merge(options) gateways = if options[:public] [@internet_gateway] * @azs.count elsif options[:internet] && !@nat_gateways.nil? @nat_gateways else [nil] * @azs.count end if @subnets.has_key?(name.to_sym) @subnets[name.to_sym].zip(gateways).map do |subnet, gateway| subnet_options = { tags: { subnet_name: name }.merge(options[:tags]).merge(@tags) } unless gateway.nil? if options[:public] subnet_options[:gateway] = gateway elsif options[:internet] subnet_options[:nat_gateway] = gateway end end ctx.add! Terrafying::Components::Subnet.create_in( self, name, subnet.az, subnet.cidr, subnet_options ) end else @subnets[name] = @azs.zip(gateways).map do |az, gateway| subnet_options = { tags: { subnet_name: name }.merge(options[:tags]).merge(@tags) } unless gateway.nil? if options[:public] subnet_options[:gateway] = gateway elsif options[:internet] subnet_options[:nat_gateway] = gateway end end ctx.add! Terrafying::Components::Subnet.create_in( self, name, az, extract_subnet!(options[:bit_size]), subnet_options ) end end end
create(name, raw_cidr, options = {})
click to toggle source
# File lib/terrafying/components/vpc.rb, line 86 def create(name, raw_cidr, options = {}) options = { subnet_size: 24, internet_access: true, nat_eips: [], azs: aws.availability_zones, tags: {}, ssh_group: DEFAULT_SSH_GROUP }.merge(options) if options[:parent_zone].nil? options[:parent_zone] = Zone.find(DEFAULT_ZONE) end if options[:subnets].nil? options[:subnets] = if options[:internet_access] { public: { public: true }, private: { internet: true } } else { private: {} } end end @name = name @cidr = raw_cidr @azs = options[:azs] @tags = options[:tags] @ssh_group = options[:ssh_group] cidr = NetAddr::CIDR.create(raw_cidr) @remaining_ip_space = NetAddr::Tree.new @remaining_ip_space.add! cidr @subnet_size = options[:subnet_size] @subnets = {} per_az_subnet_size = options[:subnets].values.reduce(0) do |memo, s| memo + (1 << (32 - s.fetch(:bit_size, @subnet_size))) end total_subnet_size = per_az_subnet_size * @azs.count if total_subnet_size > cidr.size raise 'Not enough space for subnets in CIDR' end @id = resource :aws_vpc, name, cidr_block: cidr.to_s, enable_dns_hostnames: true, tags: { Name: name, ssh_group: @ssh_group }.merge(@tags) @zone = add! Terrafying::Components::Zone.create("#{name}.#{options[:parent_zone].fqdn}", parent_zone: options[:parent_zone], tags: { vpc: @id }.merge(@tags)) dhcp = resource :aws_vpc_dhcp_options, name, domain_name: @zone.fqdn, domain_name_servers: ['AmazonProvidedDNS'], tags: { Name: name }.merge(@tags) resource :aws_vpc_dhcp_options_association, name, vpc_id: @id, dhcp_options_id: dhcp if options[:internet_access] if options[:nat_eips].empty? options[:nat_eips] = @azs.map { |az| resource :aws_eip, "#{name}-nat-gateway-#{az}", vpc: true } elsif options[:nat_eips].size != @azs.count raise 'The nubmer of nat eips has to match the number of AZs' end @internet_gateway = resource :aws_internet_gateway, name, vpc_id: @id, tags: { Name: name }.merge(@tags) allocate_subnets!(:nat_gateway, bit_size: 28, public: true) @nat_gateways = @azs.zip(@subnets[:nat_gateway], options[:nat_eips]).map do |az, subnet, eip| resource :aws_nat_gateway, "#{name}-#{az}", allocation_id: eip, subnet_id: subnet.id end end options[:subnets].each { |key, config| allocate_subnets! key, config } @internal_ssh_security_group = resource :aws_security_group, "#{name}-internal-ssh", name: "#{name}-internal-ssh", description: 'Allows SSH between machines inside the VPC CIDR', tags: @tags, vpc_id: @id, ingress: [ { from_port: 22, to_port: 22, protocol: 'tcp', cidr_blocks: [@cidr], description: nil, ipv6_cidr_blocks: nil, prefix_list_ids: nil, security_groups: nil, self: nil } ], egress: [ { from_port: 22, to_port: 22, protocol: 'tcp', cidr_blocks: [@cidr], description: nil, ipv6_cidr_blocks: nil, prefix_list_ids: nil, security_groups: nil, self: nil } ] self end
drop_subnet!(raw_cidr)
click to toggle source
# File lib/terrafying/components/vpc.rb, line 381 def drop_subnet!(raw_cidr) cidr = NetAddr::CIDR.create(raw_cidr) bit_size = cidr.bits target = @remaining_ip_space.longest_match(cidr) @remaining_ip_space.delete!(target) if target.bits != bit_size target.remainder(cidr).each do |rem| @remaining_ip_space.add!(rem) end end end
extract_subnet!(bit_size)
click to toggle source
# File lib/terrafying/components/vpc.rb, line 356 def extract_subnet!(bit_size) bit_size = 28 if bit_size > 28 # aws can't have smaller targets = @remaining_ip_space.find_space(Subnet: bit_size) if targets.count == 0 raise "Run out of ip space to allocate a /#{bit_size}" end target = targets[0] @remaining_ip_space.delete!(target) if target.bits == bit_size new_subnet = target else new_subnet = target.subnet(Bits: bit_size, Objectify: true)[0] target.remainder(new_subnet).each do |rem| @remaining_ip_space.add!(rem) end end new_subnet.to_s end
find(name)
click to toggle source
# File lib/terrafying/components/vpc.rb, line 30 def find(name) vpc = aws.vpc(name) @name = name @id = vpc.vpc_id @cidr = vpc.cidr_block @azs = aws.availability_zones @remaining_ip_space = NetAddr::Tree.new @remaining_ip_space.add! cidr @tags = {} @zone = Terrafying::Components::Zone.find_by_tag(vpc: @id) raise 'Failed to find zone' if @zone.nil? @subnets = aws.subnets_for_vpc(vpc.vpc_id).each_with_object({}) do |subnet, subnets| subnet_inst = Subnet.find(subnet.subnet_id) subnet_name_tag = subnet.tags.detect { |tag| tag.key == 'subnet_name' } key = if subnet_name_tag subnet_name_tag.value.to_sym else subnet_inst.public ? :public : :private end if subnets.key?(key) subnets[key] << subnet_inst else subnets[key] = [subnet_inst] end end # need to sort subnets so they are in az order @subnets.each { |_, s| s.sort! { |a, b| a.az <=> b.az } } @subnets.each do |_,subnet| subnet.each do |s| drop_subnet!(s.cidr) end end @nat_gateways = [] aws.nat_gateways_for_vpc(vpc.vpc_id).each do |nat_gateway| @nat_gateways << nat_gateway.nat_gateway_id end tags = vpc.tags.select { |tag| tag.key == 'ssh_group' } @ssh_group = if tags.count > 0 tags[0].value else DEFAULT_SSH_GROUP end @internal_ssh_security_group = aws.security_group("#{tf_safe(name)}-internal-ssh") self end
peer_with(other_vpc, options = {})
click to toggle source
# File lib/terrafying/components/vpc.rb, line 304 def peer_with(other_vpc, options = {}) options = { complete: false, peerings: [ { from: @subnets.values.flatten, to: other_vpc.subnets.values.flatten }, { from: other_vpc.subnets.values.flatten, to: @subnets.values.flatten } ] }.merge(options) other_vpc_ident = tf_safe(other_vpc.name) our_cidr = NetAddr::CIDR.create(@cidr) other_cidr = NetAddr::CIDR.create(other_vpc.cidr) if our_cidr.contains?(other_cidr[0]) || our_cidr.contains?(other_cidr.last) raise 'VPCs to be peered have overlapping CIDRs' end peering_connection = resource :aws_vpc_peering_connection, "#{@name}-to-#{other_vpc_ident}", peer_vpc_id: other_vpc.id, vpc_id: @id, auto_accept: true, tags: { Name: "#{@name} to #{other_vpc.name}" }.merge(@tags) if options[:complete] our_route_tables = @subnets.values.flatten.map(&:route_table).sort.uniq their_route_tables = other_vpc.subnets.values.flatten.map(&:route_table).sort.uniq (our_route_tables.product([other_vpc.cidr]) + their_route_tables.product([@cidr])).each do |route_table, cidr| hash = Digest::SHA2.hexdigest "#{route_table}-#{tf_safe(cidr)}" resource :aws_route, "#{@name}-#{other_vpc_ident}-peer-#{hash}", route_table_id: route_table, destination_cidr_block: cidr, vpc_peering_connection_id: peering_connection end else options[:peerings].each.with_index do |peering, _i| route_tables = peering[:from].map(&:route_table).sort.uniq cidrs = peering[:to].map(&:cidr).sort.uniq route_tables.product(cidrs).each do |route_table, cidr| hash = Digest::SHA2.hexdigest "#{route_table}-#{tf_safe(cidr)}" resource :aws_route, "#{@name}-#{other_vpc_ident}-peer-#{hash}", route_table_id: route_table, destination_cidr_block: cidr, vpc_peering_connection_id: peering_connection end end end end
peer_with_external(account_id, vpc_id, cidrs, options = {})
click to toggle source
# File lib/terrafying/components/vpc.rb, line 212 def peer_with_external(account_id, vpc_id, cidrs, options = {}) options = { region: 'eu-west-1', subnets: @subnets.values.flatten }.merge(options) peering_connection = resource :aws_vpc_peering_connection, "#{@name}-to-#{account_id}-#{vpc_id}", peer_owner_id: account_id, peer_vpc_id: vpc_id, peer_region: options[:region], vpc_id: @id, auto_accept: false, tags: { Name: "#{@name} to #{account_id}.#{vpc_id}" }.merge(@tags) our_route_tables = options[:subnets].map(&:route_table).sort.uniq our_route_tables.product(cidrs).each do |route_table, cidr| hash = Digest::SHA2.hexdigest "#{route_table}-#{tf_safe(cidr)}" resource :aws_route, "#{@name}-to-#{account_id}-#{vpc_id}-peer-#{hash}", route_table_id: route_table, destination_cidr_block: cidr, vpc_peering_connection_id: peering_connection end end
peer_with_vpn(ip_address, cidrs, options = {})
click to toggle source
# File lib/terrafying/components/vpc.rb, line 238 def peer_with_vpn(ip_address, cidrs, options = {}) options = { bgp_asn: 65_000, type: 'ipsec.1', tunnels: [], static_routes_only: true, subnets: @subnets.values.flatten }.merge(options) ident = tf_safe(Digest::SHA256.bubblebabble(ip_address)[0..11]+ip_address) if options[:tunnels].count > 2 raise 'You can only define a max of two tunnels' end customer_gateway = resource :aws_customer_gateway, ident, bgp_asn: options[:bgp_asn], ip_address: ip_address, type: options[:type], tags: { Name: "Connection to #{ip_address}" }.merge(@tags) vpn_gateway = resource :aws_vpn_gateway, ident, vpc_id: @id, tags: { Name: "Connection to #{ip_address}" }.merge(@tags) connection_config = { vpn_gateway_id: vpn_gateway, customer_gateway_id: customer_gateway, type: options[:type], static_routes_only: options[:static_routes_only], tags: { Name: "Connection to #{ip_address}" }.merge(@tags) } options[:tunnels].each.with_index do |tunnel, i| connection_config["tunnel#{i + 1}_inside_cidr"] = tunnel[:cidr] if tunnel.key?(:key) connection_config["tunnel#{i + 1}_preshared_key"] = tunnel[:key] end end connection = resource :aws_vpn_connection, ident, connection_config cidrs.each do |cidr| resource :aws_vpn_connection_route, "#{ident}-#{tf_safe(cidr)}", destination_cidr_block: cidr, vpn_connection_id: connection end route_tables = options[:subnets].map(&:route_table).sort.uniq route_tables.product(cidrs).each do |route_table, cidr| hash = Digest::SHA2.hexdigest "#{route_table}-#{tf_safe(cidr)}" resource :aws_route, "#{@name}-to-#{tf_safe(ip_address)}-peer-#{hash}", route_table_id: route_table, destination_cidr_block: cidr, gateway_id: vpn_gateway end end