From deeabfc6e5884bfe16631b3b020d29a1d558d31c Mon Sep 17 00:00:00 2001 From: Bert Blommers Date: Fri, 15 Oct 2021 22:43:00 +0000 Subject: [PATCH] EC2 - Implement DryRun-flag on various operations (#4420) --- moto/core/responses.py | 6 +++++- moto/ec2/responses/amis.py | 1 + .../responses/availability_zones_and_regions.py | 2 ++ moto/ec2/responses/customer_gateways.py | 1 + moto/ec2/responses/elastic_ip_addresses.py | 1 + moto/ec2/responses/instances.py | 1 + moto/ec2/responses/subnets.py | 1 + moto/ec2/responses/vpcs.py | 1 + tests/test_ec2/test_amis.py | 13 +++++++++++++ .../test_availability_zones_and_regions.py | 15 +++++++++++++++ tests/test_ec2/test_customer_gateways.py | 13 +++++++++++++ tests/test_ec2/test_elastic_ip_addresses.py | 13 +++++++++++++ tests/test_ec2/test_instances.py | 13 +++++++++++++ tests/test_ec2/test_regions.py | 17 ++++++++++++++++- tests/test_ec2/test_subnets.py | 13 +++++++++++++ tests/test_ec2/test_vpcs.py | 13 +++++++++++++ 16 files changed, 122 insertions(+), 2 deletions(-) diff --git a/moto/core/responses.py b/moto/core/responses.py index b45c98964..e016b1bfb 100644 --- a/moto/core/responses.py +++ b/moto/core/responses.py @@ -802,7 +802,11 @@ class BaseResponse(_TemplateEnvironmentMixin, ActionAuthenticatorMixin): def request_json(self): return "JSON" in self.querystring.get("ContentType", []) - def is_not_dryrun(self, action): + def error_on_dryrun(self): + self.is_not_dryrun() + + def is_not_dryrun(self, action=None): + action = action or self._get_param("Action") if "true" in self.querystring.get("DryRun", ["false"]): message = ( "An error occurred (DryRunOperation) when calling the %s operation: Request would have succeeded, but DryRun flag is set" diff --git a/moto/ec2/responses/amis.py b/moto/ec2/responses/amis.py index 079e2cc06..f22ad2ccf 100644 --- a/moto/ec2/responses/amis.py +++ b/moto/ec2/responses/amis.py @@ -40,6 +40,7 @@ class AmisResponse(BaseResponse): return template.render(success=str(success).lower()) def describe_images(self): + self.error_on_dryrun() ami_ids = self._get_multi_param("ImageId") filters = filters_from_querystring(self.querystring) owners = self._get_multi_param("Owner") diff --git a/moto/ec2/responses/availability_zones_and_regions.py b/moto/ec2/responses/availability_zones_and_regions.py index 61d4eb1ae..b276d30bc 100644 --- a/moto/ec2/responses/availability_zones_and_regions.py +++ b/moto/ec2/responses/availability_zones_and_regions.py @@ -4,11 +4,13 @@ from moto.core.responses import BaseResponse class AvailabilityZonesAndRegions(BaseResponse): def describe_availability_zones(self): + self.error_on_dryrun() zones = self.ec2_backend.describe_availability_zones() template = self.response_template(DESCRIBE_ZONES_RESPONSE) return template.render(zones=zones) def describe_regions(self): + self.error_on_dryrun() region_names = self._get_multi_param("RegionName") regions = self.ec2_backend.describe_regions(region_names) template = self.response_template(DESCRIBE_REGIONS_RESPONSE) diff --git a/moto/ec2/responses/customer_gateways.py b/moto/ec2/responses/customer_gateways.py index 0192a2ccf..e5a6e6313 100644 --- a/moto/ec2/responses/customer_gateways.py +++ b/moto/ec2/responses/customer_gateways.py @@ -26,6 +26,7 @@ class CustomerGateways(BaseResponse): return template.render(delete_status=delete_status) def describe_customer_gateways(self): + self.error_on_dryrun() filters = filters_from_querystring(self.querystring) customer_gateway_ids = self._get_multi_param("CustomerGatewayId") customer_gateways = self.ec2_backend.get_all_customer_gateways( diff --git a/moto/ec2/responses/elastic_ip_addresses.py b/moto/ec2/responses/elastic_ip_addresses.py index d9471943e..223854ca6 100644 --- a/moto/ec2/responses/elastic_ip_addresses.py +++ b/moto/ec2/responses/elastic_ip_addresses.py @@ -70,6 +70,7 @@ class ElasticIPAddresses(BaseResponse): return template.render(address=eip) def describe_addresses(self): + self.error_on_dryrun() allocation_ids = self._get_multi_param("AllocationId") public_ips = self._get_multi_param("PublicIp") filters = filters_from_querystring(self.querystring) diff --git a/moto/ec2/responses/instances.py b/moto/ec2/responses/instances.py index 3fd8dd53c..319cad959 100644 --- a/moto/ec2/responses/instances.py +++ b/moto/ec2/responses/instances.py @@ -16,6 +16,7 @@ from copy import deepcopy class InstanceResponse(BaseResponse): def describe_instances(self): + self.error_on_dryrun() filter_dict = filters_from_querystring(self.querystring) instance_ids = self._get_multi_param("InstanceId") token = self._get_param("NextToken") diff --git a/moto/ec2/responses/subnets.py b/moto/ec2/responses/subnets.py index 5698e1340..ad3a5f56a 100644 --- a/moto/ec2/responses/subnets.py +++ b/moto/ec2/responses/subnets.py @@ -39,6 +39,7 @@ class Subnets(BaseResponse): return template.render(subnet=subnet) def describe_subnets(self): + self.error_on_dryrun() subnet_ids = self._get_multi_param("SubnetId") filters = filters_from_querystring(self.querystring) subnets = self.ec2_backend.get_all_subnets(subnet_ids, filters) diff --git a/moto/ec2/responses/vpcs.py b/moto/ec2/responses/vpcs.py index a0eac2cc0..230d23bea 100644 --- a/moto/ec2/responses/vpcs.py +++ b/moto/ec2/responses/vpcs.py @@ -40,6 +40,7 @@ class VPCs(BaseResponse): return template.render(vpc=vpc) def describe_vpcs(self): + self.error_on_dryrun() vpc_ids = self._get_multi_param("VpcId") filters = filters_from_querystring(self.querystring) vpcs = self.ec2_backend.describe_vpcs(vpc_ids=vpc_ids, filters=filters) diff --git a/tests/test_ec2/test_amis.py b/tests/test_ec2/test_amis.py index d66c1f5fd..520177c91 100644 --- a/tests/test_ec2/test_amis.py +++ b/tests/test_ec2/test_amis.py @@ -1761,3 +1761,16 @@ def test_ami_filter_by_unknown_ownerid(): Filters=[{"Name": "owner-alias", "Values": ["unknown",]},] )["Images"] images.should.have.length_of(0) + + +@mock_ec2 +def test_describe_images_dryrun(): + client = boto3.client("ec2", region_name="us-east-1") + + with pytest.raises(ClientError) as ex: + client.describe_images(DryRun=True) + ex.value.response["ResponseMetadata"]["HTTPStatusCode"].should.equal(412) + ex.value.response["Error"]["Code"].should.equal("DryRunOperation") + ex.value.response["Error"]["Message"].should.equal( + "An error occurred (DryRunOperation) when calling the DescribeImages operation: Request would have succeeded, but DryRun flag is set" + ) diff --git a/tests/test_ec2/test_availability_zones_and_regions.py b/tests/test_ec2/test_availability_zones_and_regions.py index 32d99098b..535a5f70d 100644 --- a/tests/test_ec2/test_availability_zones_and_regions.py +++ b/tests/test_ec2/test_availability_zones_and_regions.py @@ -2,8 +2,10 @@ from __future__ import unicode_literals import boto import boto.ec2 import boto3 +import pytest import sure # noqa +from botocore.exceptions import ClientError from moto import mock_ec2, mock_ec2_deprecated @@ -65,6 +67,19 @@ def test_boto3_availability_zones(): rec["ZoneName"].should.contain(region) +@mock_ec2 +def test_describe_availability_zones_dryrun(): + client = boto3.client("ec2", region_name="us-east-1") + + with pytest.raises(ClientError) as ex: + client.describe_availability_zones(DryRun=True) + ex.value.response["ResponseMetadata"]["HTTPStatusCode"].should.equal(412) + ex.value.response["Error"]["Code"].should.equal("DryRunOperation") + ex.value.response["Error"]["Message"].should.equal( + "An error occurred (DryRunOperation) when calling the DescribeAvailabilityZones operation: Request would have succeeded, but DryRun flag is set" + ) + + @mock_ec2 def test_boto3_zoneId_in_availability_zones(): conn = boto3.client("ec2", "us-east-1") diff --git a/tests/test_ec2/test_customer_gateways.py b/tests/test_ec2/test_customer_gateways.py index b5039bd4c..a30df3543 100644 --- a/tests/test_ec2/test_customer_gateways.py +++ b/tests/test_ec2/test_customer_gateways.py @@ -43,6 +43,19 @@ def test_describe_customer_gateways(): cgws[0].id.should.match(customer_gateway.id) +@mock_ec2 +def test_describe_customer_gateways_dryrun(): + client = boto3.client("ec2", region_name="us-east-1") + + with pytest.raises(ClientError) as ex: + client.describe_customer_gateways(DryRun=True) + ex.value.response["ResponseMetadata"]["HTTPStatusCode"].should.equal(412) + ex.value.response["Error"]["Code"].should.equal("DryRunOperation") + ex.value.response["Error"]["Message"].should.equal( + "An error occurred (DryRunOperation) when calling the DescribeCustomerGateways operation: Request would have succeeded, but DryRun flag is set" + ) + + @mock_ec2 def test_describe_customer_gateways_boto3(): ec2 = boto3.client("ec2", region_name="us-east-1") diff --git a/tests/test_ec2/test_elastic_ip_addresses.py b/tests/test_ec2/test_elastic_ip_addresses.py index 4d8bbb124..ed4238625 100644 --- a/tests/test_ec2/test_elastic_ip_addresses.py +++ b/tests/test_ec2/test_elastic_ip_addresses.py @@ -106,6 +106,19 @@ def test_eip_allocate_vpc(): vpc.release() +@mock_ec2 +def test_describe_addresses_dryrun(): + client = boto3.client("ec2", region_name="us-east-1") + + with pytest.raises(ClientError) as ex: + client.describe_addresses(DryRun=True) + ex.value.response["ResponseMetadata"]["HTTPStatusCode"].should.equal(412) + ex.value.response["Error"]["Code"].should.equal("DryRunOperation") + ex.value.response["Error"]["Message"].should.equal( + "An error occurred (DryRunOperation) when calling the DescribeAddresses operation: Request would have succeeded, but DryRun flag is set" + ) + + @mock_ec2 def test_eip_allocate_vpc_boto3(): """Allocate/release VPC EIP""" diff --git a/tests/test_ec2/test_instances.py b/tests/test_ec2/test_instances.py index 713bf4cd9..abc0d36a5 100644 --- a/tests/test_ec2/test_instances.py +++ b/tests/test_ec2/test_instances.py @@ -3096,6 +3096,19 @@ def test_run_instance_and_associate_public_ip(): addresses["Association"].should.have.key("PublicIp") +@mock_ec2 +def test_describe_instances_dryrun(): + client = boto3.client("ec2", region_name="us-east-1") + + with pytest.raises(ClientError) as ex: + client.describe_instances(DryRun=True) + ex.value.response["ResponseMetadata"]["HTTPStatusCode"].should.equal(412) + ex.value.response["Error"]["Code"].should.equal("DryRunOperation") + ex.value.response["Error"]["Message"].should.equal( + "An error occurred (DryRunOperation) when calling the DescribeInstances operation: Request would have succeeded, but DryRun flag is set" + ) + + def retrieve_all_reservations(client, filters=[]): resp = client.describe_instances(Filters=filters) all_reservations = resp["Reservations"] diff --git a/tests/test_ec2/test_regions.py b/tests/test_ec2/test_regions.py index 1633ce939..a3ea7ade7 100644 --- a/tests/test_ec2/test_regions.py +++ b/tests/test_ec2/test_regions.py @@ -3,9 +3,11 @@ import boto.ec2 import boto.ec2.autoscale import boto.ec2.elb import boto3 -import sure +import pytest +import sure # noqa from boto3 import Session +from botocore.exceptions import ClientError from moto import mock_ec2_deprecated, mock_autoscaling_deprecated, mock_elb_deprecated from moto import mock_autoscaling, mock_ec2, mock_elb @@ -283,3 +285,16 @@ def test_create_autoscaling_group_boto3(): group["LoadBalancerNames"].should.equal([lb_name]) group["PlacementGroup"].should.equal("us_test_placement") group["TerminationPolicies"].should.equal(["OldestInstance", "NewestInstance"]) + + +@mock_ec2 +def test_describe_regions_dryrun(): + client = boto3.client("ec2", region_name="us-east-1") + + with pytest.raises(ClientError) as ex: + client.describe_regions(DryRun=True) + ex.value.response["ResponseMetadata"]["HTTPStatusCode"].should.equal(412) + ex.value.response["Error"]["Code"].should.equal("DryRunOperation") + ex.value.response["Error"]["Message"].should.equal( + "An error occurred (DryRunOperation) when calling the DescribeRegions operation: Request would have succeeded, but DryRun flag is set" + ) diff --git a/tests/test_ec2/test_subnets.py b/tests/test_ec2/test_subnets.py index 1fbb0f892..a96d1f9e7 100644 --- a/tests/test_ec2/test_subnets.py +++ b/tests/test_ec2/test_subnets.py @@ -1026,3 +1026,16 @@ def test_disassociate_subnet_cidr_block(): association_set = subnets[0]["Ipv6CidrBlockAssociationSet"] association_set.should.have.length_of(1) association_set[0]["Ipv6CidrBlock"].should.equal("1080::1:200C:417A/111") + + +@mock_ec2 +def test_describe_subnets_dryrun(): + client = boto3.client("ec2", region_name="us-east-1") + + with pytest.raises(ClientError) as ex: + client.describe_subnets(DryRun=True) + ex.value.response["ResponseMetadata"]["HTTPStatusCode"].should.equal(412) + ex.value.response["Error"]["Code"].should.equal("DryRunOperation") + ex.value.response["Error"]["Message"].should.equal( + "An error occurred (DryRunOperation) when calling the DescribeSubnets operation: Request would have succeeded, but DryRun flag is set" + ) diff --git a/tests/test_ec2/test_vpcs.py b/tests/test_ec2/test_vpcs.py index d888e372d..b6f1be49b 100644 --- a/tests/test_ec2/test_vpcs.py +++ b/tests/test_ec2/test_vpcs.py @@ -1296,3 +1296,16 @@ def test_delete_vpc_end_points(): "VpcEndpoints" ][0] ep2["State"].should.equal("available") + + +@mock_ec2 +def test_describe_vpcs_dryrun(): + client = boto3.client("ec2", region_name="us-east-1") + + with pytest.raises(ClientError) as ex: + client.describe_vpcs(DryRun=True) + ex.value.response["ResponseMetadata"]["HTTPStatusCode"].should.equal(412) + ex.value.response["Error"]["Code"].should.equal("DryRunOperation") + ex.value.response["Error"]["Message"].should.equal( + "An error occurred (DryRunOperation) when calling the DescribeVpcs operation: Request would have succeeded, but DryRun flag is set" + )