From 79d2acd6e44e8b9727d53bbe7f29a276ee3c4d8e Mon Sep 17 00:00:00 2001 From: Eric Herot Date: Fri, 31 Mar 2017 18:33:45 -0400 Subject: [PATCH] feat: Add new resource: network_interfaces_eni Uses the aws-sdk gem, added as a dependency to metadata --- CHANGELOG.md | 1 + metadata.rb | 2 + resources/eni.rb | 121 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 124 insertions(+) create mode 100644 resources/eni.rb diff --git a/CHANGELOG.md b/CHANGELOG.md index db9b9d6..bdb08af 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ network_interface - resolved cookstyle error: recipes/default.rb:8:1 refactor: `ChefStyle/CommentFormat` - resolved cookstyle error: recipes/default.rb:9:1 refactor: `ChefStyle/CommentFormat` - resolved cookstyle error: recipes/default.rb:25:6 warning: `ChefDeprecations/NodeSet` +- Add `network_interfaces_eni` resource (#32) # Unreleased: diff --git a/metadata.rb b/metadata.rb index b804d6a..d64c021 100644 --- a/metadata.rb +++ b/metadata.rb @@ -12,5 +12,7 @@ supports 'ubuntu', '>= 14.04' supports 'debian', '>= 8.0' +gem 'aws-sdk', '~> 3.0' + depends 'modules', '>= 0.1.2' depends 'line', '~> 0.6.1' diff --git a/resources/eni.rb b/resources/eni.rb new file mode 100644 index 0000000..79d56dc --- /dev/null +++ b/resources/eni.rb @@ -0,0 +1,121 @@ +require 'aws-sdk' +require 'net/http' + +default_action :create_and_attach + +property :description, String +property :subnet_id, String, required: true +property :private_ip_address, String +property :security_groups, Array +property :network_interface_id, String + +action :create_and_attach do + nic_id_to_attach = network_interface_id || create + + if attached_nic_ids.include? nic_id_to_attach + Chef::Log.debug "NIC with ID #{nic_id_to_attach} is already attached to #{instance_id}" + else + converge_by "Attach NIC #{nic_id_to_attach} to #{instance_id} at index #{next_device_index}" do + ec2.attach_network_interface( + network_interface_id: nic_id_to_attach, + instance_id: instance_id, + device_index: next_device_index + ) + end + end +end + +action :create do + create +end + +action :delete do + if existing_nic + converge_by "Delete the NIC with ID #{existing_nic.network_interface_id}" do + ec2.delete_network_interface network_interface_id: existing_nic.network_interface_id + end + elsif network_interface_id + Chef::Log.debug "NIC with ID #{network_interface_id} does not exist" + else + Chef::Log.debug "NIC with description \"#{description}\" does not exist" + end +end + +private + +def create + if existing_nic_id + Chef::Log.debug("NIC already exists: #{existing_nic.network_interface_id}/#{description}") + return existing_nic.network_interface_id + end + + converge_by "Create new NIC in description: #{description}, subnet_id: #{subnet_id}" do + options = {} + + %w(subnet_id private_ip_address security_groups).each do |prop| + next unless send prop + options[prop.to_sym] = send(prop) + end + + ec2.create_network_interface(options).network_interface.network_interface_id + end +end + +def existing_nic + @existing_nic ||= begin + # Use the ID to look up the adapter if we have it + return ec2.describe_network_interfaces( + network_interface_id: network_interface_id + ).network_interfaces.first if network_interface_id + + # Otherwise use the description + results = ec2.describe_network_interfaces( + filters: [{ name: 'description', values: [description] }] + ).network_interfaces + + # Multiple NICs with the same description == problems + if results.count > 1 + fail "More than one NIC matches the description \"#{description}\": " \ + "#{results.map(&:network_interface_id).join ', '}" + end + + results.first + end +end + +def ec2 + @ec2 ||= AWS::EC2::Client.new +end + +def instance_id + @instance_id ||= Net::HTTP.get URI 'http://169.254.169.254/2016-09-02/meta-data/instance-id' +end + +def next_device_index + @next_device_index ||= begin + device_numbers = macs.map do |mac| + Net::HTTP.get( + URI "http://169.254.169.254/2016-09-02/meta-data/network/interfaces/macs/#{mac}/device-number" + ).to_i + end + + device_numbers.sort.last + 1 + end +end + +def macs + @macs ||= Net::HTTP.get( + URI 'http://169.254.169.254/2016-09-02/meta-data/network/interfaces/macs/' + ).delete('/').split("\n") +end + +def attached_nic_ids + @attached_nic_ids ||= begin + macs.map do |mac| + Net::HTTP.get( + URI "http://169.254.169.254/2016-09-02/meta-data/network/interfaces/macs/#{mac}" \ + '/interface-id' + ) + end + end +end