class Chef::Provider::AwsSubnet

Public Instance Methods

action_create() click to toggle source
Calls superclass method
# File lib/chef/provider/aws_subnet.rb, line 11
def action_create
  subnet = super

  unless new_resource.map_public_ip_on_launch.nil?
    update_map_public_ip_on_launch(subnet)
  end

  update_route_table(subnet) unless new_resource.route_table.nil?

  update_network_acl(subnet)
end

Protected Instance Methods

create_aws_object() click to toggle source
# File lib/chef/provider/aws_subnet.rb, line 25
def create_aws_object
  cidr_block = new_resource.cidr_block
  cidr_block ||= Chef::Resource::AwsVpc.get_aws_object(new_resource.vpc, resource: new_resource).cidr_block
  options = { vpc_id: new_resource.vpc, cidr_block: cidr_block }
  options[:availability_zone] = new_resource.availability_zone if new_resource.availability_zone
  options = Chef::Provisioning::AWSDriver::AWSResource.lookup_options(options, resource: new_resource)

  converge_by "create subnet #{new_resource.name} with CIDR #{cidr_block} in VPC #{new_resource.vpc} (#{options[:vpc_id]}) in #{region}" do
    subnet = new_resource.driver.ec2_resource.create_subnet(options)
    retry_with_backoff(::Aws::EC2::Errors::InvalidSubnetIDNotFound) do
      new_resource.driver.ec2_resource.create_tags(resources: [subnet.id], tags: [{ key: "Name", value: new_resource.name }])
      new_resource.driver.ec2_resource.create_tags(resources: [subnet.id], tags: [{ key: "VPC", value: new_resource.vpc }])
    end
    subnet
  end
end
destroy_aws_object(subnet) click to toggle source
# File lib/chef/provider/aws_subnet.rb, line 56
def destroy_aws_object(subnet)
  if purging
    # TODO: possibly convert to http://docs.aws.amazon.com/AWSRubySDK/latest/AWS/EC2/Client.html#terminate_instances-instance_method
    p = Chef::ChefFS::Parallelizer.new(5)
    current_driver = new_resource.driver
    current_chef_server = new_resource.chef_server
    p.parallel_do(subnet.instances.to_a) do |instance|
      Cheffish.inline_resource(self, action) do
        aws_instance instance.id do
          action :purge
          driver current_driver
          chef_server current_chef_server
        end
      end
    end
    p.parallel_do(subnet.network_interfaces.to_a) do |network|
      # It is common during subnet purging for the instance to be terminated but
      # temporarily hanging around - this causes a `The network interface at device index 0 cannot be detached`
      # error to be raised when trying to detach
      retry_with_backoff(::Aws::EC2::Errors::OperationNotPermitted) do
        Cheffish.inline_resource(self, action) do
          aws_network_interface network do
            action :purge
            driver current_driver
            chef_server current_chef_server
          end
        end
      end
    end
  end
  converge_by "delete #{new_resource} in VPC #{new_resource.vpc} in #{region}" do
    # If the subnet doesn't exist we can't check state on it - state can only be :pending or :available
    begin
      subnet.delete
    rescue ::Aws::EC2::Errors::InvalidSubnetIDNotFound
    end
  end
end
update_aws_object(subnet) click to toggle source
# File lib/chef/provider/aws_subnet.rb, line 42
def update_aws_object(subnet)
  # Verify unmodifiable attributes of existing subnet
  if new_resource.cidr_block && subnet.cidr_block != new_resource.cidr_block
    raise "cidr_block for subnet #{new_resource.name} is #{new_resource.cidr_block}, but existing subnet (#{subnet.id})'s cidr_block is #{subnet.cidr_block}.  Modification of subnet cidr_block is unsupported!"
  end
  vpc = Chef::Resource::AwsVpc.get_aws_object(new_resource.vpc, resource: new_resource)
  if vpc && subnet.vpc.id != vpc.id
    raise "VPC for subnet #{new_resource.name} is #{new_resource.vpc} (#{vpc.id}), but existing subnet (#{subnet.id})'s vpc is #{subnet.vpc.id}.  Modification of subnet VPC is unsupported!"
  end
  if new_resource.availability_zone && subnet.availability_zone != new_resource.availability_zone
    raise "availability_zone for subnet #{new_resource.name} is #{new_resource.availability_zone}, but existing subnet (#{subnet.id})'s availability_zone is #{subnet.availability_zone}.  Modification of subnet availability_zone is unsupported!"
  end
end

Private Instance Methods

update_map_public_ip_on_launch(subnet) click to toggle source
# File lib/chef/provider/aws_subnet.rb, line 97
def update_map_public_ip_on_launch(subnet)
  unless new_resource.map_public_ip_on_launch.nil?
    subnet_desc = subnet.client.describe_subnets(subnet_ids: [subnet.id])[:subnets].first
    if new_resource.map_public_ip_on_launch
      unless subnet_desc[:map_public_ip_on_launch]
        converge_by "turn on automatic public IPs for subnet #{subnet.id}" do
          subnet.client.modify_subnet_attribute(subnet_id: subnet.id, map_public_ip_on_launch: { value: true })
        end
      end
    else
      if subnet_desc[:map_public_ip_on_launch]
        converge_by "turn off automatic public IPs for subnet #{subnet.id}" do
          subnet.client.modify_subnet_attribute(subnet_id: subnet.id, map_public_ip_on_launch: { value: false })
        end
      end
    end
  end
end
update_network_acl(subnet) click to toggle source
# File lib/chef/provider/aws_subnet.rb, line 161
def update_network_acl(subnet)
  if new_resource.network_acl
    network_acl_id =
      AWSResource.lookup_options({ network_acl: new_resource.network_acl }, resource: new_resource)[:network_acl]
    # Below snippet gives the entry of network_acl who is associated with current subnet by matching its subnet_id
    network_acl_association = subnet.client.describe_network_acls(filters: [{ name: "vpc-id", values: [subnet.vpc.id] }, { name: "association.subnet-id", values: [subnet.id] }]).network_acls.first.associations
    current_network_acl_association = network_acl_association.find { |r| r.subnet_id == subnet.id } unless network_acl_association.empty?

    if current_network_acl_association.network_acl_id != network_acl_id && !current_network_acl_association.nil?
      converge_by "update network ACL of subnet #{new_resource.name} to #{new_resource.network_acl}" do
        subnet.client.replace_network_acl_association(association_id: current_network_acl_association.network_acl_association_id, network_acl_id: network_acl_id)
      end
    end
  end
end
update_route_table(subnet) click to toggle source
# File lib/chef/provider/aws_subnet.rb, line 116
def update_route_table(subnet)
  current_route_table_association = subnet.client.describe_route_tables(filters: [{ name: "vpc-id", values: [subnet.vpc.id] }]).route_tables
  route_table_entry = nil
  do_break = false
  # Below snippet gives the entry of route_table who is associated with current subnet either by matching its
  # subnet_id or with a default subnet (i.e by checking association.main == true & in that case
  # association.subnet_id is nil)
  current_route_table_association.each do |route_tbl|
    next if route_tbl.associations.empty?
    route_tbl.associations.each do |r|
      if r.subnet_id == subnet.id
        route_table_entry = r
        do_break = true
        break
      elsif r.subnet_id.nil? && r.main == true
        route_table_entry = r
      end
    end
    break if do_break
  end
  if new_resource.route_table == :default_to_main
    unless route_table_entry.main
      converge_by "reset route table of subnet #{new_resource.name} to the VPC default" do
        subnet.client.disassociate_route_table(association_id: route_table_entry.route_table_association_id)
      end
    end
  else
    route_table = Chef::Resource::AwsRouteTable.get_aws_object(new_resource.route_table, resource: new_resource)
    if route_table_entry.main && route_table_entry.subnet_id.nil?
      # Even if the user sets the route table explicitly to the main route table,
      # we have work to do here: we need to make the relationship explicit so that
      # it won't be changed when the main route table of the VPC changes.
      converge_by "set route table of subnet #{new_resource.name} to #{new_resource.route_table}" do
        subnet.client.associate_route_table(route_table_id: route_table.id, subnet_id: subnet.id)
      end
    elsif route_table_entry.route_table_id != route_table.id
      # The route table is different now.  Change it.
      converge_by "change route table of subnet #{new_resource.name} to #{new_resource.route_table} (was #{route_table_entry.route_table_id})" do
        subnet.client.disassociate_route_table(association_id: route_table_entry.route_table_association_id) if route_table_entry.main == false
        subnet.client.associate_route_table(route_table_id: route_table.id, subnet_id: subnet.id)
      end
    end
  end
end