From 9fc03381dc1c8a45bbe3540143a3919b9bf77ff4 Mon Sep 17 00:00:00 2001 From: Alex Coomans Date: Wed, 18 Dec 2013 22:57:32 -0700 Subject: [PATCH] Add support for AWS VPC Network ACLs --- lib/fog/aws.rb | 6 + lib/fog/aws/compute.rb | 10 ++ lib/fog/aws/models/compute/network_acl.rb | 170 ++++++++++++++++++ lib/fog/aws/models/compute/network_acls.rb | 138 ++++++++++++++ .../aws/parsers/compute/create_network_acl.rb | 28 +++ .../parsers/compute/describe_network_acls.rb | 42 +++++ .../aws/parsers/compute/network_acl_parser.rb | 105 +++++++++++ .../replace_network_acl_association.rb | 20 +++ .../requests/compute/create_network_acl.rb | 105 +++++++++++ .../compute/create_network_acl_entry.rb | 80 +++++++++ lib/fog/aws/requests/compute/create_subnet.rb | 11 ++ lib/fog/aws/requests/compute/create_vpc.rb | 6 + .../requests/compute/delete_network_acl.rb | 52 ++++++ .../compute/delete_network_acl_entry.rb | 55 ++++++ lib/fog/aws/requests/compute/delete_vpc.rb | 4 + .../requests/compute/describe_network_acls.rb | 104 +++++++++++ .../replace_network_acl_association.rb | 66 +++++++ .../compute/replace_network_acl_entry.rb | 81 +++++++++ tests/aws/models/compute/network_acl_tests.rb | 109 +++++++++++ .../aws/models/compute/network_acls_tests.rb | 7 + .../aws/requests/compute/network_acl_tests.rb | 86 +++++++++ 21 files changed, 1285 insertions(+) create mode 100644 lib/fog/aws/models/compute/network_acl.rb create mode 100644 lib/fog/aws/models/compute/network_acls.rb create mode 100644 lib/fog/aws/parsers/compute/create_network_acl.rb create mode 100644 lib/fog/aws/parsers/compute/describe_network_acls.rb create mode 100644 lib/fog/aws/parsers/compute/network_acl_parser.rb create mode 100644 lib/fog/aws/parsers/compute/replace_network_acl_association.rb create mode 100644 lib/fog/aws/requests/compute/create_network_acl.rb create mode 100644 lib/fog/aws/requests/compute/create_network_acl_entry.rb create mode 100644 lib/fog/aws/requests/compute/delete_network_acl.rb create mode 100644 lib/fog/aws/requests/compute/delete_network_acl_entry.rb create mode 100644 lib/fog/aws/requests/compute/describe_network_acls.rb create mode 100644 lib/fog/aws/requests/compute/replace_network_acl_association.rb create mode 100644 lib/fog/aws/requests/compute/replace_network_acl_entry.rb create mode 100644 tests/aws/models/compute/network_acl_tests.rb create mode 100644 tests/aws/models/compute/network_acls_tests.rb create mode 100644 tests/aws/requests/compute/network_acl_tests.rb diff --git a/lib/fog/aws.rb b/lib/fog/aws.rb index 077fb96e6..f95f831b1 100644 --- a/lib/fog/aws.rb +++ b/lib/fog/aws.rb @@ -251,6 +251,12 @@ module Fog "sg-#{Fog::Mock.random_hex(8)}" end + def self.network_acl_id + "acl-#{Fog::Mock.random_hex(8)}" + end + def self.network_acl_association_id + "aclassoc-#{Fog::Mock.random_hex(8)}" + end def self.network_interface_id "eni-#{Fog::Mock.random_hex(8)}" end diff --git a/lib/fog/aws/compute.rb b/lib/fog/aws/compute.rb index 74de1ee14..86817236d 100644 --- a/lib/fog/aws/compute.rb +++ b/lib/fog/aws/compute.rb @@ -24,6 +24,8 @@ module Fog collection :internet_gateways model :key_pair collection :key_pairs + model :network_acl + collection :network_acls model :network_interface collection :network_interfaces model :route_table @@ -60,6 +62,8 @@ module Fog request :create_internet_gateway request :create_image request :create_key_pair + request :create_network_acl + request :create_network_acl_entry request :create_network_interface request :create_placement_group request :create_route @@ -76,6 +80,8 @@ module Fog request :delete_dhcp_options request :delete_internet_gateway request :delete_key_pair + request :delete_network_acl + request :delete_network_acl_entry request :delete_network_interface request :delete_security_group request :delete_placement_group @@ -98,6 +104,7 @@ module Fog request :describe_reserved_instances request :describe_instance_status request :describe_key_pairs + request :describe_network_acls request :describe_network_interface_attribute request :describe_network_interfaces request :describe_route_tables @@ -131,6 +138,8 @@ module Fog request :purchase_reserved_instances_offering request :reboot_instances request :release_address + request :replace_network_acl_association + request :replace_network_acl_entry request :register_image request :request_spot_instances request :reset_network_interface_attribute @@ -214,6 +223,7 @@ module Fog 'ipPermissions' => [], }, }, + :network_acls => {}, :network_interfaces => {}, :snapshots => {}, :volumes => {}, diff --git a/lib/fog/aws/models/compute/network_acl.rb b/lib/fog/aws/models/compute/network_acl.rb new file mode 100644 index 000000000..16cf735fc --- /dev/null +++ b/lib/fog/aws/models/compute/network_acl.rb @@ -0,0 +1,170 @@ +require 'fog/core/model' + +module Fog + module Compute + class AWS + + class NetworkAcl < Fog::Model + ICMP = 1 + TCP = 6 + UDP = 17 + + identity :network_acl_id, :aliases => 'networkAclId' + attribute :vpc_id, :aliases => 'vpcId' + attribute :default + attribute :entries, :aliases => 'entrySet' + attribute :associations, :aliases => 'associationSet' + attribute :tags, :aliases => 'tagSet' + + # Add an inbound rule, shortcut method for #add_rule + def add_inbound_rule(rule_number, protocol, rule_action, cidr_block, options = {}) + add_rule(rule_number, protocol, rule_action, cidr_block, false, options) + end + + # Add an outbound rule, shortcut method for #add_rule + def add_outbound_rule(rule_number, protocol, rule_action, cidr_block, options = {}) + add_rule(rule_number, protocol, rule_action, cidr_block, true, options) + end + + # Add a new rule + # + # network_acl.add_rule(100, Fog::Compute::AWS::NetworkAcl::TCP, 'allow', '0.0.0.0/0', true, 'PortRange.From' => 22, 'PortRange.To' => 22) + # + # ==== Parameters + # * rule_number<~Integer> - The rule number for the entry, between 100 and 32766 + # * protocol<~Integer> - The IP protocol to which the rule applies. You can use -1 to mean all protocols. + # * rule_action<~String> - Allows or denies traffic that matches the rule. (either allow or deny) + # * cidr_block<~String> - The CIDR range to allow or deny + # * egress<~Boolean> - Indicates whether this rule applies to egress traffic from the subnet (true) or ingress traffic to the subnet (false). + # * options<~Hash>: + # * 'Icmp.Code' - ICMP code, required if protocol is 1 + # * 'Icmp.Type' - ICMP type, required if protocol is 1 + # * 'PortRange.From' - The first port in the range, required if protocol is 6 (TCP) or 17 (UDP) + # * 'PortRange.To' - The last port in the range, required if protocol is 6 (TCP) or 17 (UDP) + # + # ==== Returns + # + # True or false depending on the result + # + def add_rule(rule_number, protocol, rule_action, cidr_block, egress, options = {}) + requires :network_acl_id + + service.create_network_acl_entry(network_acl_id, rule_number, protocol, rule_action, cidr_block, egress, options) + true + end + + # Remove an inbound rule, shortcut method for #remove_rule + def remove_inbound_rule(rule_number) + remove_rule(rule_number, false) + end + + # Remove an outbound rule, shortcut method for #remove_rule + def remove_outbound_rule(rule_number) + remove_rule(rule_number, true) + end + + # Update a specific rule number + # + # network_acl.remove_rule(100, true) + # + # ==== Parameters + # * rule_number<~Integer> - The rule number for the entry, between 100 and 32766 + # * egress<~Boolean> - Indicates whether this rule applies to egress traffic from the subnet (true) or ingress traffic to the subnet (false). + # + # ==== Returns + # + # True or false depending on the result + # + def remove_rule(rule_number, egress) + requires :network_acl_id + + service.delete_network_acl_entry(network_acl_id, rule_number, egress) + true + end + + # Update an inbound rule, shortcut method for #update_rule + def update_inbound_rule(rule_number, protocol, rule_action, cidr_block, options = {}) + update_rule(rule_number, protocol, rule_action, cidr_block, false, options) + end + + # Update an outbound rule, shortcut method for #update_rule + def update_outbound_rule(rule_number, protocol, rule_action, cidr_block, options = {}) + update_rule(rule_number, protocol, rule_action, cidr_block, true, options) + end + + # Update a specific rule number + # + # network_acl.update_rule(100, Fog::Compute::AWS::NetworkAcl::TCP, 'allow', '0.0.0.0/0', true, 'PortRange.From' => 22, 'PortRange.To' => 22) + # + # ==== Parameters + # * rule_number<~Integer> - The rule number for the entry, between 100 and 32766 + # * protocol<~Integer> - The IP protocol to which the rule applies. You can use -1 to mean all protocols. + # * rule_action<~String> - Allows or denies traffic that matches the rule. (either allow or deny) + # * cidr_block<~String> - The CIDR range to allow or deny + # * egress<~Boolean> - Indicates whether this rule applies to egress traffic from the subnet (true) or ingress traffic to the subnet (false). + # * options<~Hash>: + # * 'Icmp.Code' - ICMP code, required if protocol is 1 + # * 'Icmp.Type' - ICMP type, required if protocol is 1 + # * 'PortRange.From' - The first port in the range, required if protocol is 6 (TCP) or 17 (UDP) + # * 'PortRange.To' - The last port in the range, required if protocol is 6 (TCP) or 17 (UDP) + # + # ==== Returns + # + # True or false depending on the result + # + def update_rule(rule_number, protocol, rule_action, cidr_block, egress, options = {}) + requires :network_acl_id + + service.replace_network_acl_entry(network_acl_id, rule_number, protocol, rule_action, cidr_block, egress, options) + true + end + + # Associate a subnet with this network ACL + # + # network_acl.associate_with(subnet) + # + # ==== Parameters + # * subnet<~Subnet> - Subnet object to associate with this network ACL + # + # ==== Returns + # + # True or false depending on the result + # + def associate_with(subnet) + requires :network_acl_id + + # We have to manually find out the network ACL the subnet is currently associated with + old_id = service.network_acls.all('association.subnet-id' => subnet.subnet_id).first.associations.detect { |a| a['subnetId'] == subnet.subnet_id }['networkAclAssociationId'] + service.replace_network_acl_association(old_id, network_acl_id) + true + end + + # Removes an existing network ACL + # + # network_acl.destroy + # + # ==== Returns + # + # True or false depending on the result + # + def destroy + requires :network_acl_id + + service.delete_network_acl(network_acl_id) + true + end + + # Create a network ACL + # + # >> g = AWS.network_acls.new(:vpc_id => 'vpc-abcdefgh') + # >> g.save + def save + requires :vpc_id + data = service.create_network_acl(vpc_id).body['networkAcl'] + merge_attributes(data) + true + end + end + end + end +end diff --git a/lib/fog/aws/models/compute/network_acls.rb b/lib/fog/aws/models/compute/network_acls.rb new file mode 100644 index 000000000..8d743a510 --- /dev/null +++ b/lib/fog/aws/models/compute/network_acls.rb @@ -0,0 +1,138 @@ +require 'fog/core/collection' +require 'fog/aws/models/compute/network_acl' + +module Fog + module Compute + class AWS + + class NetworkAcls < Fog::Collection + + attribute :filters + + model Fog::Compute::AWS::NetworkAcl + + # Creates a new network ACL + # + # AWS.network_acls.new + # + # ==== Returns + # + # Returns the details of the new network ACL + # + #>> + # + def initialize(attributes) + self.filters ||= {} + super + end + + # Returns an array of all network ACLs that have been created + # + # AWS.network_acls.all + # + # ==== Returns + # + # Returns an array of all network ACLs + # + #>> AWS.network_acls.all + # {}, + # "portRange" => {}, + # "ruleNumber" => 32767, + # "protocol" => -1, + # "ruleAction" => "deny", + # "egress" => false, + # "cidrBlock" => "0.0.0.0/0" + # }, + # { + # "icmpTypeCode" => {}, + # "portRange" => {}, + # "ruleNumber" => 32767, + # "protocol" => -1, + # "ruleAction" => "deny", + # "egress" => true, + # "cidrBlock" => "0.0.0.0/0" + # } + # ], + # associations=[ + # { + # "networkAclAssociationId" => "aclassoc-abcdefgh", + # "networkAclId" => "acl-abcdefgh", + # "subnetId" => "subnet-abcdefgh" + # } + # ], + # tags={} + # > + # ] + # > + # + def all(filters = filters) + self.filters = filters + data = service.describe_network_acls(filters).body + load(data['networkAclSet']) + end + + # Used to retrieve a network interface + # network interface id is required to get any information + # + # You can run the following command to get the details: + # AWS.network_interfaces.get("eni-11223344") + # + # ==== Returns + # + #>> AWS.network_acls.get("acl-abcdefgh") + # {}, + # "portRange" => {}, + # "ruleNumber" => 32767, + # "protocol" => -1, + # "ruleAction" => "deny", + # "egress" => false, + # "cidrBlock" => "0.0.0.0/0" + # }, + # { + # "icmpTypeCode" => {}, + # "portRange" => {}, + # "ruleNumber" => 32767, + # "protocol" => -1, + # "ruleAction" => "deny", + # "egress" => true, + # "cidrBlock" => "0.0.0.0/0" + # } + # ], + # associations=[ + # { + # "networkAclAssociationId" => "aclassoc-abcdefgh", + # "networkAclId" => "acl-abcdefgh", + # "subnetId" => "subnet-abcdefgh" + # } + # ], + # tags={} + # > + def get(nacl_id) + self.class.new(:service => service).all('network-acl-id' => nacl_id).first if nacl_id + end + end + end + end +end diff --git a/lib/fog/aws/parsers/compute/create_network_acl.rb b/lib/fog/aws/parsers/compute/create_network_acl.rb new file mode 100644 index 000000000..4ebfa0c10 --- /dev/null +++ b/lib/fog/aws/parsers/compute/create_network_acl.rb @@ -0,0 +1,28 @@ +module Fog + module Parsers + module Compute + module AWS + require 'fog/aws/parsers/compute/network_acl_parser' + + class CreateNetworkAcl < NetworkAclParser + def reset + super + @response = { 'networkAcl' => {} } + end + + def end_element(name) + case name + when 'requestId' + @response[name] = value + when 'networkAcl' + @response['networkAcl'] = @network_acl + reset_nacl + else + super + end + end + end + end + end + end +end diff --git a/lib/fog/aws/parsers/compute/describe_network_acls.rb b/lib/fog/aws/parsers/compute/describe_network_acls.rb new file mode 100644 index 000000000..c02061468 --- /dev/null +++ b/lib/fog/aws/parsers/compute/describe_network_acls.rb @@ -0,0 +1,42 @@ +module Fog + module Parsers + module Compute + module AWS + require 'fog/aws/parsers/compute/network_acl_parser' + + class DescribeNetworkAcls < NetworkAclParser + def reset + super + @response = { 'networkAclSet' => [] } + @item_level = 0 + end + + def start_element(name, attrs = []) + super + case name + when 'item' + @item_level += 1 + end + end + + def end_element(name) + case name + when 'requestId' + @response[name] = value + when 'item' + @item_level -= 1 + if @item_level == 0 + @response['networkAclSet'] << @network_acl + reset_nacl + else + super + end + else + super + end + end + end + end + end + end +end diff --git a/lib/fog/aws/parsers/compute/network_acl_parser.rb b/lib/fog/aws/parsers/compute/network_acl_parser.rb new file mode 100644 index 000000000..fda3ea845 --- /dev/null +++ b/lib/fog/aws/parsers/compute/network_acl_parser.rb @@ -0,0 +1,105 @@ +module Fog + module Parsers + module Compute + module AWS + + class NetworkAclParser < Fog::Parsers::Base + def reset_nacl + @network_acl = { 'associationSet' => [], 'entrySet' => [], 'tagSet' => {} } + @association = {} + @entry = { 'icmpTypeCode' => {}, 'portRange' => {} } + @tag = {} + + @in_entry_set = false + @in_association_set = false + @in_tag_set = false + @in_port_range = false + @in_icmp_type_code = false + end + + def reset + reset_nacl + end + + def start_element(name, attrs = []) + super + case name + when 'entrySet' + @in_entry_set = true + when 'associationSet' + @in_association_set = true + when 'tagSet' + @in_tag_set = true + when 'portRange' + @in_port_range = true + when 'icmpTypeCode' + @in_icmp_type_code = true + end + end + + def end_element(name) + if @in_entry_set + if @in_port_range + case name + when 'portRange' + @in_port_range = false + when 'from', 'to' + @entry['portRange'][name] = value.to_i + end + elsif @in_icmp_type_code + case name + when 'icmpTypeCode' + @in_icmp_type_code = false + when 'code', 'type' + @entry['icmpTypeCode'][name] = value.to_i + end + else + case name + when 'entrySet' + @in_entry_set = false + when 'item' + @network_acl['entrySet'] << @entry + @entry = { 'icmpTypeCode' => {}, 'portRange' => {} } + when 'ruleNumber', 'protocol' + @entry[name] = value.to_i + when 'ruleAction', 'cidrBlock' + @entry[name] = value + when 'egress' + @entry[name] = value == 'true' + end + end + elsif @in_association_set + case name + when 'associationSet' + @in_association_set = false + when 'item' + @network_acl['associationSet'] << @association + @association = {} + when 'networkAclAssociationId', 'networkAclId', 'subnetId' + @association[name] = value + end + elsif @in_tag_set + case name + when 'tagSet' + @in_tag_set = false + when 'item' + @network_acl['tagSet'][@tag['key']] = @tag['value'] + @tag = {} + when 'key', 'value' + @tag[name] = value + end + else + case name + when 'networkAclId', 'vpcId' + @network_acl[name] = value + when 'default' + @network_acl[name] = value == 'true' + end + end + end + + end + end + end + end +end diff --git a/lib/fog/aws/parsers/compute/replace_network_acl_association.rb b/lib/fog/aws/parsers/compute/replace_network_acl_association.rb new file mode 100644 index 000000000..6656c7567 --- /dev/null +++ b/lib/fog/aws/parsers/compute/replace_network_acl_association.rb @@ -0,0 +1,20 @@ +module Fog + module Parsers + module Compute + module AWS + class ReplaceNetworkAclAssociation < Fog::Parsers::Base + def reset + @response = {} + end + + def end_element(name) + case name + when 'requestId', 'newAssociationId' + @response[name] = value + end + end + end + end + end + end +end diff --git a/lib/fog/aws/requests/compute/create_network_acl.rb b/lib/fog/aws/requests/compute/create_network_acl.rb new file mode 100644 index 000000000..8c5c26065 --- /dev/null +++ b/lib/fog/aws/requests/compute/create_network_acl.rb @@ -0,0 +1,105 @@ +module Fog + module Compute + class AWS + class Real + require 'fog/aws/parsers/compute/create_network_acl' + + # Creates a network ACL + # + # ==== Parameters + # * vpcId<~String> - The ID of the VPC to create this network ACL under + # + # === Returns + # * response<~Excon::Response>: + # * body<~Hash>: + # * 'requestId'<~String> - Id of request + # * 'networkAcl'<~Array>: - The network ACL + # * 'networkAclId'<~String> - The ID of the network ACL + # * 'vpcId'<~String> - The ID of the VPC for the network ACL + # * 'default'<~Boolean> - Indicates whether this is the default network ACL for the VPC + # * 'entrySet'<~Array>: - A list of entries (rules) in the network ACL + # * 'ruleNumber'<~Integer> - The rule number for the entry. ACL entries are processed in ascending order by rule number + # * 'protocol'<~Integer> - The protocol. A value of -1 means all protocols + # * 'ruleAction'<~String> - Indicates whether to allow or deny the traffic that matches the rule + # * 'egress'<~Boolean> - Indicates whether the rule is an egress rule (applied to traffic leaving the subnet) + # * 'cidrBlock'<~String> - The network range to allow or deny, in CIDR notation + # * 'icmpTypeCode'<~Hash> - ICMP protocol: The ICMP type and code + # * 'code'<~Integer> - The ICMP code. A value of -1 means all codes for the specified ICMP type + # * 'type'<~Integer> - The ICMP type. A value of -1 means all types + # * 'portRange'<~Hash> - TCP or UDP protocols: The range of ports the rule applies to + # * 'from'<~Integer> - The first port in the range + # * 'to'<~Integer> - The last port in the range + # * 'associationSet'<~Array>: - A list of associations between the network ACL and subnets + # * 'networkAclAssociationId'<~String> - The ID of the association + # * 'networkAclId'<~String> - The ID of the network ACL + # * 'subnetId'<~String> - The ID of the subnet + # * 'tagSet'<~Array>: - Tags assigned to the resource. + # * 'key'<~String> - Tag's key + # * 'value'<~String> - Tag's value + # + # {Amazon API Reference}[http://docs.aws.amazon.com/AWSEC2/latest/APIReference/ApiReference-query-CreateNetworkAcl.html] + def create_network_acl(vpcId, options = {}) + request({ + 'Action' => 'CreateNetworkAcl', + 'VpcId' => vpcId, + :parser => Fog::Parsers::Compute::AWS::CreateNetworkAcl.new + }.merge!(options)) + end + end + + class Mock + def create_network_acl(vpcId, options = {}) + response = Excon::Response.new + if vpcId + id = Fog::AWS::Mock.network_acl_id + + unless self.data[:vpcs].detect { |s| s['vpcId'] == vpcId } + raise Fog::Compute::AWS::Error.new("Unknown VPC '#{vpcId}' specified") + end + + data = { + 'networkAclId' => id, + 'vpcId' => vpcId, + 'default' => false, + 'entrySet' => [ + { + 'icmpTypeCode' => {}, + 'portRange' => {}, + 'ruleNumber' => 32767, + 'protocol' => -1, + 'ruleAction' => "deny", + 'egress' => true, + 'cidrBlock' => "0.0.0.0/0", + }, + { + 'icmpTypeCode' => {}, + 'portRange' => {}, + 'ruleNumber' => 32767, + 'protocol' => -1, + 'ruleAction' => "deny", + 'egress' => false, + 'cidrBlock' => "0.0.0.0/0", + }, + ], + 'associationSet' => [], + 'tagSet' => {}, + } + + self.data[:network_acls][id] = data + response.body = { + 'requestId' => Fog::AWS::Mock.request_id, + 'networkAcl' => data + } + else + response.status = 400 + response.body = { + 'Code' => 'InvalidParameterValue', + 'Message' => "Invalid value '' for subnetId" + } + end + response + end + end + end + end +end diff --git a/lib/fog/aws/requests/compute/create_network_acl_entry.rb b/lib/fog/aws/requests/compute/create_network_acl_entry.rb new file mode 100644 index 000000000..2e0f90703 --- /dev/null +++ b/lib/fog/aws/requests/compute/create_network_acl_entry.rb @@ -0,0 +1,80 @@ +module Fog + module Compute + class AWS + class Real + require 'fog/aws/parsers/compute/basic' + + # Creates a Network ACL entry + # + # ==== Parameters + # * network_acl_id<~String> - The ID of the ACL to add this entry to + # * rule_number<~Integer> - The rule number for the entry, between 100 and 32766 + # * protocol<~Integer> - The IP protocol to which the rule applies. You can use -1 to mean all protocols. + # * rule_action<~String> - Allows or denies traffic that matches the rule. (either allow or deny) + # * cidr_block<~String> - The CIDR range to allow or deny + # * egress<~Boolean> - Indicates whether this rule applies to egress traffic from the subnet (true) or ingress traffic to the subnet (false). + # * options<~Hash>: + # * 'Icmp.Code' - ICMP code, required if protocol is 1 + # * 'Icmp.Type' - ICMP type, required if protocol is 1 + # * 'PortRange.From' - The first port in the range, required if protocol is 6 (TCP) or 17 (UDP) + # * 'PortRange.To' - The last port in the range, required if protocol is 6 (TCP) or 17 (UDP) + # + # === Returns + # * response<~Excon::Response>: + # * body<~Hash>: + # * 'requestId'<~String> - Id of request + # * 'return'<~Boolean> - Returns true if the request succeeds. + # + # {Amazon API Reference}[http://docs.aws.amazon.com/AWSEC2/latest/APIReference/ApiReference-query-CreateNetworkAclEntry.html] + def create_network_acl_entry(network_acl_id, rule_number, protocol, rule_action, cidr_block, egress, options = {}) + request({ + 'Action' => 'CreateNetworkAclEntry', + 'NetworkAclId' => network_acl_id, + 'RuleNumber' => rule_number, + 'Protocol' => protocol, + 'RuleAction' => rule_action, + 'Egress' => egress, + 'CidrBlock' => cidr_block, + :parser => Fog::Parsers::Compute::AWS::Basic.new + }.merge!(options)) + end + end + + class Mock + def create_network_acl_entry(network_acl_id, rule_number, protocol, rule_action, cidr_block, egress, options = {}) + response = Excon::Response.new + if self.data[:network_acls][network_acl_id] + + if self.data[:network_acls][network_acl_id]['entrySet'].detect { |r| r['ruleNumber'] == rule_number && r['egress'] == egress } + raise Fog::Compute::AWS::Error.new("Already a rule with that number") + end + + data = { + 'ruleNumber' => rule_number, + 'protocol' => protocol, + 'ruleAction' => rule_action, + 'egress' => egress, + 'cidrBlock' => cidr_block, + 'icmpTypeCode' => {}, + 'portRange' => {} + } + data['icmpTypeCode']['code'] = options['Icmp.Code'] if options['Icmp.Code'] + data['icmpTypeCode']['type'] = options['Icmp.Type'] if options['Icmp.Type'] + data['portRange']['from'] = options['PortRange.From'] if options['PortRange.From'] + data['portRange']['to'] = options['PortRange.To'] if options['PortRange.To'] + self.data[:network_acls][network_acl_id]['entrySet'] << data + + response.status = 200 + response.body = { + 'requestId' => Fog::AWS::Mock.request_id, + 'return' => true + } + response + else + raise Fog::Compute::AWS::NotFound.new("The network ACL '#{network_acl_id}' does not exist") + end + end + end + end + end +end diff --git a/lib/fog/aws/requests/compute/create_subnet.rb b/lib/fog/aws/requests/compute/create_subnet.rb index bb2211785..2a1c01abf 100644 --- a/lib/fog/aws/requests/compute/create_subnet.rb +++ b/lib/fog/aws/requests/compute/create_subnet.rb @@ -56,6 +56,17 @@ module Fog 'tagSet' => {} }) + # Add this subnet to the default network ACL + accid = Fog::AWS::Mock.network_acl_association_id + default_nacl = self.data[:network_acls].values.detect { |nacl| nacl['vpcId'] == vpcId && nacl['default'] } + if default_nacl + default_nacl['associationSet'] << { + 'networkAclAssociationId' => accid, + 'networkAclId' => default_nacl['networkAclId'], + 'subnetId' => self.data[:subnets].last['subnetId'], + } + end + response.body = { 'requestId' => Fog::AWS::Mock.request_id, 'subnetSet' => self.data[:subnets] diff --git a/lib/fog/aws/requests/compute/create_vpc.rb b/lib/fog/aws/requests/compute/create_vpc.rb index 6d8bb24e9..ffa675aa0 100644 --- a/lib/fog/aws/requests/compute/create_vpc.rb +++ b/lib/fog/aws/requests/compute/create_vpc.rb @@ -66,6 +66,12 @@ module Fog assoc = add_route_association(default_route.id, nil, true) route_table["associationSet"].push(assoc) + # Create a default network ACL + default_nacl = self.network_acls.new(:vpc_id => vpc_id) + default_nacl.save + # Manually override since Amazon doesn't let you create a default one + self.data[:network_acls][default_nacl.network_acl_id]['default'] = true + response.body = { 'requestId' => Fog::AWS::Mock.request_id, 'vpcSet' => self.data[:vpcs] diff --git a/lib/fog/aws/requests/compute/delete_network_acl.rb b/lib/fog/aws/requests/compute/delete_network_acl.rb new file mode 100644 index 000000000..5e53ccaff --- /dev/null +++ b/lib/fog/aws/requests/compute/delete_network_acl.rb @@ -0,0 +1,52 @@ +module Fog + module Compute + class AWS + class Real + require 'fog/aws/parsers/compute/basic' + + # Deletes a network ACL. + # + # ==== Parameters + # * network_acl_id<~String> - The ID of the network ACL you want to delete. + # + # === Returns + # * response<~Excon::Response>: + # * body<~Hash>: + # * 'requestId'<~String> - Id of request + # * 'return'<~Boolean> - Returns true if the request succeeds. + # + # {Amazon API Reference}[http://docs.aws.amazon.com/AWSEC2/latest/APIReference/ApiReference-query-DeleteNetworkAcl.html] + def delete_network_acl(network_acl_id) + request( + 'Action' => 'DeleteNetworkAcl', + 'NetworkAclId' => network_acl_id, + :parser => Fog::Parsers::Compute::AWS::Basic.new + ) + end + end + + class Mock + def delete_network_acl(network_acl_id) + response = Excon::Response.new + if self.data[:network_acls][network_acl_id] + + if self.data[:network_acls][network_acl_id]['associationSet'].any? + raise Fog::Compute::AWS::Error.new("ACL is in use") + end + + self.data[:network_acls].delete(network_acl_id) + + response.status = 200 + response.body = { + 'requestId' => Fog::AWS::Mock.request_id, + 'return' => true + } + response + else + raise Fog::Compute::AWS::NotFound.new("The network ACL '#{network_acl_id}' does not exist") + end + end + end + end + end +end diff --git a/lib/fog/aws/requests/compute/delete_network_acl_entry.rb b/lib/fog/aws/requests/compute/delete_network_acl_entry.rb new file mode 100644 index 000000000..7a4cad8e2 --- /dev/null +++ b/lib/fog/aws/requests/compute/delete_network_acl_entry.rb @@ -0,0 +1,55 @@ +module Fog + module Compute + class AWS + class Real + require 'fog/aws/parsers/compute/basic' + + # Deletes a network ACL entry + # + # ==== Parameters + # * network_acl_id<~String> - The ID of the network ACL + # * rule_number<~Integer> - The rule number of the entry to delete. + # * egress<~Boolean> - Indicates whether the rule is an egress rule (true) or ingress rule (false) + # + # === Returns + # * response<~Excon::Response>: + # * body<~Hash>: + # * 'requestId'<~String> - Id of request + # * 'return'<~Boolean> - Returns true if the request succeeds. + # + # {Amazon API Reference}[http://docs.aws.amazon.com/AWSEC2/latest/APIReference/ApiReference-query-DeleteNetworkAclEntry.html] + def delete_network_acl_entry(network_acl_id, rule_number, egress) + request( + 'Action' => 'DeleteNetworkAclEntry', + 'NetworkAclId' => network_acl_id, + 'RuleNumber' => rule_number, + 'Egress' => egress, + :parser => Fog::Parsers::Compute::AWS::Basic.new + ) + end + end + + class Mock + def delete_network_acl_entry(network_acl_id, rule_number, egress) + response = Excon::Response.new + if self.data[:network_acls][network_acl_id] + if self.data[:network_acls][network_acl_id]['entrySet'].detect { |r| r['ruleNumber'] == rule_number && r['egress'] == egress } + self.data[:network_acls][network_acl_id]['entrySet'].delete_if { |r| r['ruleNumber'] == rule_number && r['egress'] == egress } + else + raise Fog::Compute::AWS::Error.new("No rule with that number and egress value") + end + + response.status = 200 + response.body = { + 'requestId' => Fog::AWS::Mock.request_id, + 'return' => true + } + response + else + raise Fog::Compute::AWS::NotFound.new("The network ACL '#{network_acl_id}' does not exist") + end + end + end + end + end +end diff --git a/lib/fog/aws/requests/compute/delete_vpc.rb b/lib/fog/aws/requests/compute/delete_vpc.rb index c44b73d72..1d09cf32c 100644 --- a/lib/fog/aws/requests/compute/delete_vpc.rb +++ b/lib/fog/aws/requests/compute/delete_vpc.rb @@ -36,6 +36,10 @@ module Fog response.status = 200 self.data[:vpcs].reject! { |v| v['vpcId'] == vpc_id } + # Delete the default network ACL + network_acl_id = self.network_acls.all('vpc-id' => vpc_id, 'default' => true).first.network_acl_id + self.data[:network_acls].delete(network_acl_id) + response.body = { 'requestId' => Fog::AWS::Mock.request_id, 'return' => true diff --git a/lib/fog/aws/requests/compute/describe_network_acls.rb b/lib/fog/aws/requests/compute/describe_network_acls.rb new file mode 100644 index 000000000..0f887513d --- /dev/null +++ b/lib/fog/aws/requests/compute/describe_network_acls.rb @@ -0,0 +1,104 @@ +module Fog + module Compute + class AWS + class Real + + require 'fog/aws/parsers/compute/describe_network_acls' + + # Describe all or specified network ACLs + # + # ==== Parameters + # * filters<~Hash> - List of filters to limit results with + # + # === Returns + # * response<~Excon::Response>: + # * body<~Hash>: + # * 'requestId'<~String> - Id of request + # * 'networkAclSet'<~Array>: - A list of network ACLs + # * 'networkAclId'<~String> - The ID of the network ACL + # * 'vpcId'<~String> - The ID of the VPC for the network ACL + # * 'default'<~Boolean> - Indicates whether this is the default network ACL for the VPC + # * 'entrySet'<~Array>: - A list of entries (rules) in the network ACL + # * 'ruleNumber'<~Integer> - The rule number for the entry. ACL entries are processed in ascending order by rule number + # * 'protocol'<~Integer> - The protocol. A value of -1 means all protocols + # * 'ruleAction'<~String> - Indicates whether to allow or deny the traffic that matches the rule + # * 'egress'<~Boolean> - Indicates whether the rule is an egress rule (applied to traffic leaving the subnet) + # * 'cidrBlock'<~String> - The network range to allow or deny, in CIDR notation + # * 'icmpTypeCode'<~Hash> - ICMP protocol: The ICMP type and code + # * 'code'<~Integer> - The ICMP code. A value of -1 means all codes for the specified ICMP type + # * 'type'<~Integer> - The ICMP type. A value of -1 means all types + # * 'portRange'<~Hash> - TCP or UDP protocols: The range of ports the rule applies to + # * 'from'<~Integer> - The first port in the range + # * 'to'<~Integer> - The last port in the range + # * 'associationSet'<~Array>: - A list of associations between the network ACL and subnets + # * 'networkAclAssociationId'<~String> - The ID of the association + # * 'networkAclId'<~String> - The ID of the network ACL + # * 'subnetId'<~String> - The ID of the subnet + # * 'tagSet'<~Array>: - Tags assigned to the resource. + # * 'key'<~String> - Tag's key + # * 'value'<~String> - Tag's value + # + # {Amazon API Reference}[http://docs.aws.amazon.com/AWSEC2/latest/APIReference/ApiReference-query-DescribeNetworkAcls.html] + def describe_network_acls(filters = {}) + params = Fog::AWS.indexed_filters(filters) + request({ + 'Action' => 'DescribeNetworkAcls', + :idempotent => true, + :parser => Fog::Parsers::Compute::AWS::DescribeNetworkAcls.new + }.merge!(params)) + end + end + + class Mock + def describe_network_acls(filters = {}) + response = Excon::Response.new + + network_acls = self.data[:network_acls].values + + aliases = { + 'vpc-id' => 'vpcId', + 'network-acl-id' => 'networkAclId', + 'default' => 'default', + } + association_aliases = { + 'association-id' => 'networkAclAssociationId', + 'network-acl-id' => 'networkAclId', + 'subnet-id' => 'subnetId', + } + entry_aliases = { + 'cidr' => 'cidrBlock', + 'egress' => 'egress', + 'rule-action' => 'ruleAction', + 'rule-number' => 'ruleNumber', + 'protocol' => 'protocol', + 'egress' => 'egress', + } + for filter_key, filter_value in filters + filter_key = filter_key.to_s + if association_key = filter_key.split('association.')[1] + aliased_key = association_aliases[association_key] + network_acls = network_acls.reject{|nacl| !nacl['associationSet'].detect {|association| [*filter_value].include?(association[aliased_key])}} + elsif entry_key = filter_key.split('entry.icmp.')[1] + network_acls = network_acls.reject{|nacl| !nacl['entrySet'].detect {|association| [*filter_value].include?(association['icmpTypeCode'][entry_key])}} + elsif entry_key = filter_key.split('entry.port-range.')[1] + network_acls = network_acls.reject{|nacl| !nacl['entrySet'].detect {|association| [*filter_value].include?(association['portRange'][entry_key])}} + elsif entry_key = filter_key.split('entry.')[1] + aliased_key = entry_aliases[entry_key] + network_acls = network_acls.reject{|nacl| !nacl['entrySet'].detect {|association| [*filter_value].include?(association[aliased_key])}} + else + aliased_key = aliases[filter_key] + network_acls = network_acls.reject{|nacl| ![*filter_value].include?(nacl[aliased_key])} + end + end + + response.status = 200 + response.body = { + 'requestId' => Fog::AWS::Mock.request_id, + 'networkAclSet' => network_acls + } + response + end + end + end + end +end diff --git a/lib/fog/aws/requests/compute/replace_network_acl_association.rb b/lib/fog/aws/requests/compute/replace_network_acl_association.rb new file mode 100644 index 000000000..3d7f9e710 --- /dev/null +++ b/lib/fog/aws/requests/compute/replace_network_acl_association.rb @@ -0,0 +1,66 @@ +module Fog + module Compute + class AWS + class Real + require 'fog/aws/parsers/compute/replace_network_acl_association' + + # Replace the network ACL for a subnet with a + # + # ==== Parameters + # * association_id<~String> - The ID of the current association between the original network ACL and the subnet + # * network_acl_id<~String> - The ID of the network ACL + # + # === Returns + # * response<~Excon::Response>: + # * body<~Hash>: + # * 'requestId'<~String> - Id of request + # * 'return'<~Boolean> - Returns true if the request succeeds. + # + # {Amazon API Reference}[http://docs.aws.amazon.com/AWSEC2/latest/APIReference/ApiReference-query-ReplaceNetworkAclAssociation.html] + def replace_network_acl_association(association_id, network_acl_id) + request({ + 'Action' => 'ReplaceNetworkAclAssociation', + 'AssociationId' => association_id, + 'NetworkAclId' => network_acl_id, + :parser => Fog::Parsers::Compute::AWS::ReplaceNetworkAclAssociation.new + }) + end + end + + class Mock + def replace_network_acl_association(association_id, network_acl_id) + response = Excon::Response.new + if self.data[:network_acls][network_acl_id] + # find the old assoc + old_nacl = self.data[:network_acls].values.detect do |n| + n['associationSet'].detect { |assoc| assoc['networkAclAssociationId'] == association_id } + end + + unless old_nacl + raise Fog::Compute::AWS::Error.new("Invalid association_id #{association_id}") + end + + subnet_id = old_nacl['associationSet'].detect { |assoc| assoc['networkAclAssociationId'] == association_id }['subnetId'] + old_nacl['associationSet'].delete_if { |assoc| assoc['networkAclAssociationId'] == association_id } + + id = Fog::AWS::Mock.network_acl_association_id + self.data[:network_acls][network_acl_id]['associationSet'] << { + 'networkAclAssociationId' => id, + 'networkAclId' => network_acl_id, + 'subnetId' => subnet_id, + } + + response.status = 200 + response.body = { + 'requestId' => Fog::AWS::Mock.request_id, + 'newAssociationId' => id + } + response + else + raise Fog::Compute::AWS::NotFound.new("The network ACL '#{network_acl_id}' does not exist") + end + end + end + end + end +end diff --git a/lib/fog/aws/requests/compute/replace_network_acl_entry.rb b/lib/fog/aws/requests/compute/replace_network_acl_entry.rb new file mode 100644 index 000000000..61e26ffdb --- /dev/null +++ b/lib/fog/aws/requests/compute/replace_network_acl_entry.rb @@ -0,0 +1,81 @@ +module Fog + module Compute + class AWS + class Real + require 'fog/aws/parsers/compute/basic' + + # Replaces a Network ACL entry with the same rule number + # + # ==== Parameters + # * network_acl_id<~String> - The ID of the ACL to add this entry to + # * rule_number<~Integer> - The rule number for the entry, between 100 and 32766 + # * protocol<~Integer> - The IP protocol to which the rule applies. You can use -1 to mean all protocols. + # * rule_action<~String> - Allows or denies traffic that matches the rule. (either allow or deny) + # * cidr_block<~String> - The CIDR range to allow or deny + # * egress<~Boolean> - Indicates whether this rule applies to egress traffic from the subnet (true) or ingress traffic to the subnet (false). + # * options<~Hash>: + # * 'Icmp.Code' - ICMP code, required if protocol is 1 + # * 'Icmp.Type' - ICMP type, required if protocol is 1 + # * 'PortRange.From' - The first port in the range, required if protocol is 6 (TCP) or 17 (UDP) + # * 'PortRange.To' - The last port in the range, required if protocol is 6 (TCP) or 17 (UDP) + # + # === Returns + # * response<~Excon::Response>: + # * body<~Hash>: + # * 'requestId'<~String> - Id of request + # * 'return'<~Boolean> - Returns true if the request succeeds. + # + # {Amazon API Reference}[http://docs.aws.amazon.com/AWSEC2/latest/APIReference/ApiReference-query-ReplaceNetworkAclEntry.html] + def replace_network_acl_entry(network_acl_id, rule_number, protocol, rule_action, cidr_block, egress, options = {}) + request({ + 'Action' => 'ReplaceNetworkAclEntry', + 'NetworkAclId' => network_acl_id, + 'RuleNumber' => rule_number, + 'Protocol' => protocol, + 'RuleAction' => rule_action, + 'Egress' => egress, + 'CidrBlock' => cidr_block, + :parser => Fog::Parsers::Compute::AWS::Basic.new + }.merge!(options)) + end + end + + class Mock + def replace_network_acl_entry(network_acl_id, rule_number, protocol, rule_action, cidr_block, egress, options = {}) + response = Excon::Response.new + if self.data[:network_acls][network_acl_id] + + unless self.data[:network_acls][network_acl_id]['entrySet'].detect { |r| r['ruleNumber'] == rule_number && r['egress'] == egress } + raise Fog::Compute::AWS::Error.new("No rule with that number") + end + self.data[:network_acls][network_acl_id]['entrySet'].delete_if { |r| r['ruleNumber'] == rule_number && r['egress'] == egress } + + data = { + 'ruleNumber' => rule_number, + 'protocol' => protocol, + 'ruleAction' => rule_action, + 'egress' => egress, + 'cidrBlock' => cidr_block, + 'icmpTypeCode' => {}, + 'portRange' => {} + } + data['icmpTypeCode']['code'] = options['Icmp.Code'] if options['Icmp.Code'] + data['icmpTypeCode']['type'] = options['Icmp.Type'] if options['Icmp.Type'] + data['portRange']['from'] = options['PortRange.From'] if options['PortRange.From'] + data['portRange']['to'] = options['PortRange.To'] if options['PortRange.To'] + self.data[:network_acls][network_acl_id]['entrySet'] << data + + response.status = 200 + response.body = { + 'requestId' => Fog::AWS::Mock.request_id, + 'return' => true + } + response + else + raise Fog::Compute::AWS::NotFound.new("The network ACL '#{network_acl_id}' does not exist") + end + end + end + end + end +end diff --git a/tests/aws/models/compute/network_acl_tests.rb b/tests/aws/models/compute/network_acl_tests.rb new file mode 100644 index 000000000..209b12429 --- /dev/null +++ b/tests/aws/models/compute/network_acl_tests.rb @@ -0,0 +1,109 @@ +Shindo.tests("Fog::Compute[:aws] | network_acl", ['aws']) do + @vpc = Fog::Compute[:aws].vpcs.create('cidr_block' => '10.0.10.0/24') + @subnet = Fog::Compute[:aws].subnets.create('vpc_id' => @vpc.id, 'cidr_block' => '10.0.10.16/28') + + model_tests(Fog::Compute[:aws].network_acls, { :vpc_id => @vpc.id }, true) + + tests("associate_with") do + @new_nacl = Fog::Compute[:aws].network_acls.create(:vpc_id => @vpc.id) + @default_nacl = Fog::Compute[:aws].network_acls.all('vpc-id' => @vpc.id, 'default' => true).first + + test("associate_with new_nacl") do + @new_nacl.associate_with(@subnet) + end + + @new_nacl.reload + + test("associate_with correctly updates new_nacl") do + @new_nacl.associations.collect { |a| a['subnetId'] } == [@subnet.subnet_id] + end + + @default_nacl.associate_with(@subnet) + @new_nacl.reload + @default_nacl.reload + + test("associate_with correctly updates new_nacl after removal") do + @new_nacl.associations.collect { |a| a['subnetId'] } == [] + end + + test("associate_with correctly updates default_nacl after removal") do + @default_nacl.associations.collect { |a| a['subnetId'] } == [@subnet.subnet_id] + end + + @new_nacl.destroy + end + + tests("add_rule and remove_rule") do + @new_nacl = Fog::Compute[:aws].network_acls.create(:vpc_id => @vpc.id) + default_rules = @new_nacl.entries.dup + + test("add a new inbound rule") do + @new_nacl.add_inbound_rule(100, Fog::Compute::AWS::NetworkAcl::TCP, 'allow', '0.0.0.0/0', 'PortRange.From' => 22, 'PortRange.To' => 22) + @new_nacl.reload + (@new_nacl.entries - default_rules) == [{ + "icmpTypeCode" => {}, + "portRange" => { + "from" => 22, + "to" => 22 + }, + "ruleNumber" => 100, + "protocol" => 6, + "ruleAction" => "allow", + "egress" => false, + "cidrBlock" => "0.0.0.0/0" + }] + end + + test("remove inbound rule") do + @new_nacl.remove_inbound_rule(100) + @new_nacl.reload + @new_nacl.entries == default_rules + end + + test("add a new outbound rule") do + @new_nacl.add_outbound_rule(100, Fog::Compute::AWS::NetworkAcl::TCP, 'allow', '0.0.0.0/0', 'PortRange.From' => 22, 'PortRange.To' => 22) + @new_nacl.reload + (@new_nacl.entries - default_rules) == [{ + "icmpTypeCode" => {}, + "portRange" => { + "from" => 22, + "to" => 22 + }, + "ruleNumber" => 100, + "protocol" => 6, + "ruleAction" => "allow", + "egress" => true, + "cidrBlock" => "0.0.0.0/0" + }] + end + + test("remove outbound rule") do + @new_nacl.remove_outbound_rule(100) + @new_nacl.reload + @new_nacl.entries == default_rules + end + + test("update rule") do + @new_nacl.add_inbound_rule(100, Fog::Compute::AWS::NetworkAcl::TCP, 'allow', '0.0.0.0/0', 'PortRange.From' => 22, 'PortRange.To' => 22) + @new_nacl.update_inbound_rule(100, Fog::Compute::AWS::NetworkAcl::TCP, 'allow', '10.0.0.0/8', 'PortRange.From' => 22, 'PortRange.To' => 22) + @new_nacl.reload + (@new_nacl.entries - default_rules) == [{ + "icmpTypeCode" => {}, + "portRange" => { + "from" => 22, + "to" => 22 + }, + "ruleNumber" => 100, + "protocol" => 6, + "ruleAction" => "allow", + "egress" => false, + "cidrBlock" => "10.0.0.0/8" + }] + end + + @new_nacl.destroy + end + + @subnet.destroy + @vpc.destroy +end diff --git a/tests/aws/models/compute/network_acls_tests.rb b/tests/aws/models/compute/network_acls_tests.rb new file mode 100644 index 000000000..ffad2f5db --- /dev/null +++ b/tests/aws/models/compute/network_acls_tests.rb @@ -0,0 +1,7 @@ +Shindo.tests("Fog::Compute[:aws] | network_acls", ['aws']) do + @vpc = Fog::Compute[:aws].vpcs.create('cidr_block' => '10.0.10.0/24') + + collection_tests(Fog::Compute[:aws].network_acls, { :vpc_id => @vpc.id }, true) + + @vpc.destroy +end diff --git a/tests/aws/requests/compute/network_acl_tests.rb b/tests/aws/requests/compute/network_acl_tests.rb new file mode 100644 index 000000000..59fedcf61 --- /dev/null +++ b/tests/aws/requests/compute/network_acl_tests.rb @@ -0,0 +1,86 @@ +Shindo.tests('Fog::Compute[:aws] | network acl requests', ['aws']) do + + @network_acl_format = { + 'networkAclId' => String, + 'vpcId' => String, + 'default' => Fog::Boolean, + 'entrySet' => [{ + 'ruleNumber' => Integer, + 'protocol' => Integer, + 'ruleAction' => String, + 'egress' => Fog::Boolean, + 'cidrBlock' => String, + 'icmpTypeCode' => { + 'code' => Fog::Nullable::Integer, + 'type' => Fog::Nullable::Integer + }, + 'portRange' => { + 'from' => Fog::Nullable::Integer, + 'to' => Fog::Nullable::Integer + } + }], + 'associationSet' => Array, + 'tagSet' => Hash + } + + @network_acls_format = { + 'requestId' => String, + 'networkAclSet' => [ @network_acl_format ] + } + + @network_acl_replace_association = { + 'requestId' => String, + 'newAssociationId' => String + } + + tests('success') do + @vpc = Fog::Compute[:aws].vpcs.create('cidr_block' => '10.0.10.0/24') + @subnet = Fog::Compute[:aws].subnets.create('vpc_id' => @vpc.id, 'cidr_block' => '10.0.10.16/28') + @network_acl = nil + + # Describe network interfaces + tests('#describe_network_acls').formats(@network_acls_format) do + Fog::Compute[:aws].describe_network_acls.body + end + + tests('#create_network_acl').formats(@network_acl_format) do + data = Fog::Compute[:aws].create_network_acl(@vpc.id).body + + @network_acl = data['networkAcl'] + data['networkAcl'] + end + + tests("#create_network_acl_entry").formats(AWS::Compute::Formats::BASIC) do + Fog::Compute[:aws].create_network_acl_entry(@network_acl['networkAclId'], 100, 6, 'allow', '0.0.0.0/8', false, 'PortRange.From' => 22, 'PortRange.To' => 22).body + end + + tests("#replace_network_acl_entry").formats(AWS::Compute::Formats::BASIC) do + Fog::Compute[:aws].replace_network_acl_entry(@network_acl['networkAclId'], 100, 6, 'deny', '0.0.0.0/8', false, 'PortRange.From' => 22, 'PortRange.To' => 22).body + end + + tests("#delete_network_acl_entry").formats(AWS::Compute::Formats::BASIC) do + Fog::Compute[:aws].delete_network_acl_entry(@network_acl['networkAclId'], 100, false).body + end + + default_acl = Fog::Compute[:aws].describe_network_acls('vpc-id' => @vpc.id, 'default' => true).body['networkAclSet'].first + @assoc_id = default_acl['associationSet'].first['networkAclAssociationId'] + + tests("#replace_network_acl_association").formats(@network_acl_replace_association) do + data = Fog::Compute[:aws].replace_network_acl_association(@assoc_id, @network_acl['networkAclId']).body + @assoc_id = data['newAssociationId'] + data + end + + tests("#replace_network_acl_association").formats(@network_acl_replace_association) do + Fog::Compute[:aws].replace_network_acl_association(@assoc_id, default_acl['networkAclId']).body + end + + tests('#delete_network_acl').formats(AWS::Compute::Formats::BASIC) do + Fog::Compute[:aws].delete_network_acl(@network_acl['networkAclId']).body + end + + # Clean up + @subnet.destroy + @vpc.destroy + end +end