diff --git a/CHANGELOG.md b/CHANGELOG.md index 099cf063b56f..6008ceaf5f13 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,51 @@ Moto Changelog ============== +3.1.14 +----- +Docker Digest for 3.1.14: + + New Methods: + * Greengrass: + * create_function_definition() + * create_resource_definition() + * create_function_definition_version() + * create_resource_definition_version() + * create_subscription_definition() + * create_subscription_definition_version() + * delete_function_definition() + * delete_resource_definition() + * delete_subscription_definition() + * get_function_definition() + * get_function_definition_version() + * get_resource_definition() + * get_resource_definition_version() + * get_subscription_definition() + * get_subscription_definition_version() + * list_function_definitions() + * list_function_definition_versions() + * list_resource_definitions() + * list_resource_definition_versions() + * list_subscription_definitions() + * list_subscription_definition_versions() + * update_function_definition() + * update_resource_definition() + * update_subscription_definition() + * EKS: + * list_tags_for_resources() + * tag_resource() + * untag_resource() + * Route53: + * associate_vpc() + * disassociate_vpc_from_hosted_zone() + * update_health_check() + * update_hosted_zone_comment() + + Miscellaneous: + * APIGateway:put_integration() now supports the requestParameters-parameter + * EC2:create_route() now validates whether a route already exists + + 3.1.13 ----- Docker Digest for 3.1.13: _sha256:d7f6c779c79f03b686747ae26b52bdca26fd81a50c6a41a8a6cba50c96982abf_ diff --git a/IMPLEMENTATION_COVERAGE.md b/IMPLEMENTATION_COVERAGE.md index 5c2a07ca2d2e..b1d02ff9bbcf 100644 --- a/IMPLEMENTATION_COVERAGE.md +++ b/IMPLEMENTATION_COVERAGE.md @@ -2114,7 +2114,7 @@ ## eks
-35% implemented +44% implemented - [ ] associate_encryption_config - [ ] associate_identity_provider_config @@ -2140,11 +2140,11 @@ - [X] list_fargate_profiles - [ ] list_identity_provider_configs - [X] list_nodegroups -- [ ] list_tags_for_resource +- [X] list_tags_for_resource - [ ] list_updates - [ ] register_cluster -- [ ] tag_resource -- [ ] untag_resource +- [X] tag_resource +- [X] untag_resource - [ ] update_addon - [ ] update_cluster_config - [ ] update_cluster_version @@ -2867,7 +2867,7 @@ ## greengrass
-17% implemented +43% implemented - [ ] associate_role_to_group - [ ] associate_service_role_to_account @@ -2878,26 +2878,26 @@ - [ ] create_deployment - [X] create_device_definition - [X] create_device_definition_version -- [ ] create_function_definition -- [ ] create_function_definition_version +- [X] create_function_definition +- [X] create_function_definition_version - [ ] create_group - [ ] create_group_certificate_authority - [ ] create_group_version - [ ] create_logger_definition - [ ] create_logger_definition_version -- [ ] create_resource_definition -- [ ] create_resource_definition_version +- [X] create_resource_definition +- [X] create_resource_definition_version - [ ] create_software_update_job -- [ ] create_subscription_definition -- [ ] create_subscription_definition_version +- [X] create_subscription_definition +- [X] create_subscription_definition_version - [ ] delete_connector_definition - [X] delete_core_definition - [X] delete_device_definition -- [ ] delete_function_definition +- [X] delete_function_definition - [ ] delete_group - [ ] delete_logger_definition -- [ ] delete_resource_definition -- [ ] delete_subscription_definition +- [X] delete_resource_definition +- [X] delete_subscription_definition - [ ] disassociate_role_from_group - [ ] disassociate_service_role_from_account - [ ] get_associated_role @@ -2910,19 +2910,19 @@ - [ ] get_deployment_status - [X] get_device_definition - [X] get_device_definition_version -- [ ] get_function_definition -- [ ] get_function_definition_version +- [X] get_function_definition +- [X] get_function_definition_version - [ ] get_group - [ ] get_group_certificate_authority - [ ] get_group_certificate_configuration - [ ] get_group_version - [ ] get_logger_definition - [ ] get_logger_definition_version -- [ ] get_resource_definition -- [ ] get_resource_definition_version +- [X] get_resource_definition +- [X] get_resource_definition_version - [ ] get_service_role_for_account -- [ ] get_subscription_definition -- [ ] get_subscription_definition_version +- [X] get_subscription_definition +- [X] get_subscription_definition_version - [ ] get_thing_runtime_configuration - [ ] list_bulk_deployment_detailed_reports - [ ] list_bulk_deployments @@ -2933,17 +2933,17 @@ - [ ] list_deployments - [X] list_device_definition_versions - [X] list_device_definitions -- [ ] list_function_definition_versions -- [ ] list_function_definitions +- [X] list_function_definition_versions +- [X] list_function_definitions - [ ] list_group_certificate_authorities - [ ] list_group_versions - [ ] list_groups - [ ] list_logger_definition_versions - [ ] list_logger_definitions -- [ ] list_resource_definition_versions -- [ ] list_resource_definitions -- [ ] list_subscription_definition_versions -- [ ] list_subscription_definitions +- [X] list_resource_definition_versions +- [X] list_resource_definitions +- [X] list_subscription_definition_versions +- [X] list_subscription_definitions - [ ] list_tags_for_resource - [ ] reset_deployments - [ ] start_bulk_deployment @@ -2954,19 +2954,20 @@ - [ ] update_connector_definition - [X] update_core_definition - [X] update_device_definition -- [ ] update_function_definition +- [X] update_function_definition - [ ] update_group - [ ] update_group_certificate_configuration - [ ] update_logger_definition -- [ ] update_resource_definition -- [ ] update_subscription_definition +- [X] update_resource_definition +- [X] update_subscription_definition - [ ] update_thing_runtime_configuration
## guardduty
-18% implemented +17% implemented +- [ ] accept_administrator_invitation - [ ] accept_invitation - [ ] archive_findings - [X] create_detector @@ -2987,9 +2988,11 @@ - [ ] describe_organization_configuration - [ ] describe_publishing_destination - [ ] disable_organization_admin_account +- [ ] disassociate_from_administrator_account - [ ] disassociate_from_master_account - [ ] disassociate_members - [X] enable_organization_admin_account +- [ ] get_administrator_account - [X] get_detector - [X] get_filter - [ ] get_findings @@ -2999,6 +3002,7 @@ - [ ] get_master_account - [ ] get_member_detectors - [ ] get_members +- [ ] get_remaining_free_trial_days - [ ] get_threat_intel_set - [ ] get_usage_statistics - [ ] invite_members @@ -4733,10 +4737,10 @@ ## route53
-34% implemented +40% implemented - [ ] activate_key_signing_key -- [ ] associate_vpc_with_hosted_zone +- [X] associate_vpc_with_hosted_zone - [ ] change_cidr_collection - [X] change_resource_record_sets - [X] change_tags_for_resource @@ -4761,7 +4765,7 @@ - [ ] delete_traffic_policy_instance - [ ] delete_vpc_association_authorization - [ ] disable_hosted_zone_dnssec -- [ ] disassociate_vpc_from_hosted_zone +- [X] disassociate_vpc_from_hosted_zone - [ ] enable_hosted_zone_dnssec - [ ] get_account_limit - [ ] get_change @@ -4801,8 +4805,8 @@ - [ ] list_traffic_policy_versions - [ ] list_vpc_association_authorizations - [ ] test_dns_answer -- [ ] update_health_check -- [ ] update_hosted_zone_comment +- [X] update_health_check +- [X] update_hosted_zone_comment - [ ] update_traffic_policy_comment - [ ] update_traffic_policy_instance
@@ -5998,6 +6002,7 @@ - compute-optimizer - connect - connect-contact-lens +- connectcampaigns - connectparticipant - cur - customer-profiles @@ -6110,6 +6115,7 @@ - qldb-session - rbin - rds-data +- redshiftserverless - resiliencehub - robomaker - route53-recovery-cluster diff --git a/docs/docs/services/cognito-idp.rst b/docs/docs/services/cognito-idp.rst index 3065ea664186..6e63a2af81a7 100644 --- a/docs/docs/services/cognito-idp.rst +++ b/docs/docs/services/cognito-idp.rst @@ -12,6 +12,8 @@ cognito-idp =========== +.. autoclass:: moto.cognitoidp.models.CognitoIdpBackend + |start-h3| Example usage |end-h3| .. sourcecode:: python @@ -145,10 +147,3 @@ cognito-idp - [ ] verify_user_attribute - -|start-h3| Stable Cognito User Pool Id |end-h3| - -In some cases, you need to have reproducible IDs for the user pool. -For example, a single initialization before the start of integration tests. - -This behavior can be enabled by passing the environment variable: MOTO_COGNITO_IDP_USER_POOL_ID_STRATEGY=HASH. diff --git a/docs/docs/services/eks.rst b/docs/docs/services/eks.rst index 00fad92b5ee3..1a4a993209e3 100644 --- a/docs/docs/services/eks.rst +++ b/docs/docs/services/eks.rst @@ -50,10 +50,22 @@ eks - [ ] list_identity_provider_configs - [X] list_nodegroups - [X] list_tags_for_resource + + This function currently will list tags on an EKS cluster only. It does not list tags from a managed node group + + - [ ] list_updates - [ ] register_cluster - [X] tag_resource + + This function currently will tag an EKS cluster only. It does not tag a managed node group + + - [X] untag_resource + + This function currently will remove tags on an EKS cluster only. It does not remove tags from a managed node group + + - [ ] update_addon - [ ] update_cluster_config - [ ] update_cluster_version diff --git a/docs/docs/services/greengrass.rst b/docs/docs/services/greengrass.rst index 315fdc543d54..d27ddab3b4a6 100644 --- a/docs/docs/services/greengrass.rst +++ b/docs/docs/services/greengrass.rst @@ -34,26 +34,26 @@ greengrass - [ ] create_deployment - [X] create_device_definition - [X] create_device_definition_version -- [ ] create_function_definition -- [ ] create_function_definition_version +- [X] create_function_definition +- [X] create_function_definition_version - [ ] create_group - [ ] create_group_certificate_authority - [ ] create_group_version - [ ] create_logger_definition - [ ] create_logger_definition_version -- [ ] create_resource_definition -- [ ] create_resource_definition_version +- [X] create_resource_definition +- [X] create_resource_definition_version - [ ] create_software_update_job -- [ ] create_subscription_definition -- [ ] create_subscription_definition_version +- [X] create_subscription_definition +- [X] create_subscription_definition_version - [ ] delete_connector_definition - [X] delete_core_definition - [X] delete_device_definition -- [ ] delete_function_definition +- [X] delete_function_definition - [ ] delete_group - [ ] delete_logger_definition -- [ ] delete_resource_definition -- [ ] delete_subscription_definition +- [X] delete_resource_definition +- [X] delete_subscription_definition - [ ] disassociate_role_from_group - [ ] disassociate_service_role_from_account - [ ] get_associated_role @@ -66,19 +66,19 @@ greengrass - [ ] get_deployment_status - [X] get_device_definition - [X] get_device_definition_version -- [ ] get_function_definition -- [ ] get_function_definition_version +- [X] get_function_definition +- [X] get_function_definition_version - [ ] get_group - [ ] get_group_certificate_authority - [ ] get_group_certificate_configuration - [ ] get_group_version - [ ] get_logger_definition - [ ] get_logger_definition_version -- [ ] get_resource_definition -- [ ] get_resource_definition_version +- [X] get_resource_definition +- [X] get_resource_definition_version - [ ] get_service_role_for_account -- [ ] get_subscription_definition -- [ ] get_subscription_definition_version +- [X] get_subscription_definition +- [X] get_subscription_definition_version - [ ] get_thing_runtime_configuration - [ ] list_bulk_deployment_detailed_reports - [ ] list_bulk_deployments @@ -89,17 +89,17 @@ greengrass - [ ] list_deployments - [X] list_device_definition_versions - [X] list_device_definitions -- [ ] list_function_definition_versions -- [ ] list_function_definitions +- [X] list_function_definition_versions +- [X] list_function_definitions - [ ] list_group_certificate_authorities - [ ] list_group_versions - [ ] list_groups - [ ] list_logger_definition_versions - [ ] list_logger_definitions -- [ ] list_resource_definition_versions -- [ ] list_resource_definitions -- [ ] list_subscription_definition_versions -- [ ] list_subscription_definitions +- [X] list_resource_definition_versions +- [X] list_resource_definitions +- [X] list_subscription_definition_versions +- [X] list_subscription_definitions - [ ] list_tags_for_resource - [ ] reset_deployments - [ ] start_bulk_deployment @@ -110,11 +110,11 @@ greengrass - [ ] update_connector_definition - [X] update_core_definition - [X] update_device_definition -- [ ] update_function_definition +- [X] update_function_definition - [ ] update_group - [ ] update_group_certificate_configuration - [ ] update_logger_definition -- [ ] update_resource_definition -- [ ] update_subscription_definition +- [X] update_resource_definition +- [X] update_subscription_definition - [ ] update_thing_runtime_configuration diff --git a/docs/docs/services/guardduty.rst b/docs/docs/services/guardduty.rst index 143acf6a054f..3bb95e38aa52 100644 --- a/docs/docs/services/guardduty.rst +++ b/docs/docs/services/guardduty.rst @@ -25,6 +25,7 @@ guardduty |start-h3| Implemented features for this service |end-h3| +- [ ] accept_administrator_invitation - [ ] accept_invitation - [ ] archive_findings - [X] create_detector @@ -45,9 +46,11 @@ guardduty - [ ] describe_organization_configuration - [ ] describe_publishing_destination - [ ] disable_organization_admin_account +- [ ] disassociate_from_administrator_account - [ ] disassociate_from_master_account - [ ] disassociate_members - [X] enable_organization_admin_account +- [ ] get_administrator_account - [X] get_detector - [X] get_filter - [ ] get_findings @@ -57,6 +60,7 @@ guardduty - [ ] get_master_account - [ ] get_member_detectors - [ ] get_members +- [ ] get_remaining_free_trial_days - [ ] get_threat_intel_set - [ ] get_usage_statistics - [ ] invite_members diff --git a/docs/docs/services/route53.rst b/docs/docs/services/route53.rst index d00b7bf9762f..148621fd2dd6 100644 --- a/docs/docs/services/route53.rst +++ b/docs/docs/services/route53.rst @@ -26,7 +26,7 @@ route53 |start-h3| Implemented features for this service |end-h3| - [ ] activate_key_signing_key -- [ ] associate_vpc_with_hosted_zone +- [X] associate_vpc_with_hosted_zone - [ ] change_cidr_collection - [X] change_resource_record_sets - [X] change_tags_for_resource @@ -55,7 +55,7 @@ route53 - [ ] delete_traffic_policy_instance - [ ] delete_vpc_association_authorization - [ ] disable_hosted_zone_dnssec -- [ ] disassociate_vpc_from_hosted_zone +- [X] disassociate_vpc_from_hosted_zone - [ ] enable_hosted_zone_dnssec - [ ] get_account_limit - [ ] get_change @@ -111,8 +111,8 @@ route53 - [ ] list_traffic_policy_versions - [ ] list_vpc_association_authorizations - [ ] test_dns_answer -- [ ] update_health_check -- [ ] update_hosted_zone_comment +- [X] update_health_check +- [X] update_hosted_zone_comment - [ ] update_traffic_policy_comment - [ ] update_traffic_policy_instance diff --git a/moto/cognitoidp/models.py b/moto/cognitoidp/models.py index 276effb5d24e..87f8239705ab 100644 --- a/moto/cognitoidp/models.py +++ b/moto/cognitoidp/models.py @@ -831,6 +831,13 @@ def to_json(self): class CognitoIdpBackend(BaseBackend): + """ + In some cases, you need to have reproducible IDs for the user pool. + For example, a single initialization before the start of integration tests. + + This behavior can be enabled by passing the environment variable: MOTO_COGNITO_IDP_USER_POOL_ID_STRATEGY=HASH. + """ + def __init__(self, region_name, account_id): super().__init__(region_name, account_id) self.user_pools = OrderedDict() diff --git a/moto/route53/models.py b/moto/route53/models.py index 8e6be240987d..db713cbe985f 100644 --- a/moto/route53/models.py +++ b/moto/route53/models.py @@ -470,14 +470,14 @@ def get_dnssec(self, zone_id): # check if hosted zone exists self.get_hosted_zone(zone_id) - def associate_vpc(self, zone_id, vpcid, vpcregion): + def associate_vpc_with_hosted_zone(self, zone_id, vpcid, vpcregion): zone = self.get_hosted_zone(zone_id) if not zone.private_zone: raise PublicZoneVPCAssociation() zone.add_vpc(vpcid, vpcregion) return zone - def disassociate_vpc(self, zone_id, vpcid): + def disassociate_vpc_from_hosted_zone(self, zone_id, vpcid): zone = self.get_hosted_zone(zone_id) if len(zone.vpcs) <= 1: raise LastVPCAssociation() diff --git a/moto/route53/responses.py b/moto/route53/responses.py index 86ea8302c617..01ca96431d17 100644 --- a/moto/route53/responses.py +++ b/moto/route53/responses.py @@ -151,7 +151,7 @@ def associate_vpc_response(self, request, full_url, headers): vpcid = vpc.get("VPCId", None) vpcregion = vpc.get("VPCRegion", None) - route53_backend.associate_vpc(zoneid, vpcid, vpcregion) + route53_backend.associate_vpc_with_hosted_zone(zoneid, vpcid, vpcregion) template = Template(ASSOCIATE_VPC_RESPONSE) return 200, headers, template.render(comment=comment) @@ -169,7 +169,7 @@ def disassociate_vpc_response(self, request, full_url, headers): vpc = elements.get("DisassociateVPCFromHostedZoneRequest", {}).get("VPC", {}) vpcid = vpc.get("VPCId", None) - route53_backend.disassociate_vpc(zoneid, vpcid) + route53_backend.disassociate_vpc_from_hosted_zone(zoneid, vpcid) template = Template(DISASSOCIATE_VPC_RESPONSE) return 200, headers, template.render(comment=comment) diff --git a/scripts/implementation_coverage.py b/scripts/implementation_coverage.py index 389972cb8bdb..2a3080d9bca9 100755 --- a/scripts/implementation_coverage.py +++ b/scripts/implementation_coverage.py @@ -261,7 +261,7 @@ def test_{coverage[service_name]['name'][5:]}_behaviour: file.write(" ...\n") file.write("\n\n") file.write(".. sourcecode:: python\n\n") - file.write(" @mock_all\n") + file.write(" @mock_all()\n") file.write(" def test_all_supported_services_at_the_same_time():\n") file.write(" ...\n") file.write("\n") diff --git a/tests/test_route53/test_route53.py b/tests/test_route53/test_route53.py index 6a17b9ed0573..749264be63d8 100644 --- a/tests/test_route53/test_route53.py +++ b/tests/test_route53/test_route53.py @@ -161,6 +161,20 @@ def test_get_unknown_hosted_zone(): err["Message"].should.equal("No hosted zone found with ID: unknown") +@mock_route53 +def test_update_hosted_zone_comment(): + conn = boto3.client("route53", region_name="us-east-1") + response = conn.create_hosted_zone( + Name="testdns.aws.com.", CallerReference=str(hash("foo")) + ) + zone_id = response["HostedZone"]["Id"].split("/")[-1] + + conn.update_hosted_zone_comment(Id=zone_id, Comment="yolo") + + resp = conn.get_hosted_zone(Id=zone_id)["HostedZone"] + resp["Config"].should.have.key("Comment").equals("yolo") + + @mock_route53 def test_list_resource_record_set_unknown_zone(): conn = boto3.client("route53", region_name="us-east-1") @@ -188,90 +202,6 @@ def test_list_resource_record_set_unknown_type(): err["Message"].should.equal("Bad Request") -@mock_route53 -def test_create_health_check(): - conn = boto3.client("route53", region_name="us-east-1") - - check = conn.create_health_check( - CallerReference="?", - HealthCheckConfig={ - "IPAddress": "10.0.0.25", - "Port": 80, - "Type": "HTTP", - "ResourcePath": "/", - "FullyQualifiedDomainName": "example.com", - "SearchString": "a good response", - "RequestInterval": 10, - "FailureThreshold": 2, - }, - )["HealthCheck"] - check.should.have.key("Id").match("[a-z0-9-]+") - check.should.have.key("CallerReference") - check.should.have.key("HealthCheckConfig") - check["HealthCheckConfig"].should.have.key("IPAddress").equal("10.0.0.25") - check["HealthCheckConfig"].should.have.key("Port").equal(80) - check["HealthCheckConfig"].should.have.key("Type").equal("HTTP") - check["HealthCheckConfig"].should.have.key("ResourcePath").equal("/") - check["HealthCheckConfig"].should.have.key("FullyQualifiedDomainName").equal( - "example.com" - ) - check["HealthCheckConfig"].should.have.key("SearchString").equal("a good response") - check["HealthCheckConfig"].should.have.key("RequestInterval").equal(10) - check["HealthCheckConfig"].should.have.key("FailureThreshold").equal(2) - check.should.have.key("HealthCheckVersion").equal(1) - - -@mock_route53 -def test_list_health_checks(): - conn = boto3.client("route53", region_name="us-east-1") - - conn.list_health_checks()["HealthChecks"].should.have.length_of(0) - - check = conn.create_health_check( - CallerReference="?", - HealthCheckConfig={ - "IPAddress": "10.0.0.25", - "Port": 80, - "Type": "HTTP", - "ResourcePath": "/", - "FullyQualifiedDomainName": "example.com", - "SearchString": "a good response", - "RequestInterval": 10, - "FailureThreshold": 2, - }, - )["HealthCheck"] - - checks = conn.list_health_checks()["HealthChecks"] - checks.should.have.length_of(1) - checks.should.contain(check) - - -@mock_route53 -def test_delete_health_checks(): - conn = boto3.client("route53", region_name="us-east-1") - - conn.list_health_checks()["HealthChecks"].should.have.length_of(0) - - check = conn.create_health_check( - CallerReference="?", - HealthCheckConfig={ - "IPAddress": "10.0.0.25", - "Port": 80, - "Type": "HTTP", - "ResourcePath": "/", - "FullyQualifiedDomainName": "example.com", - "SearchString": "a good response", - "RequestInterval": 10, - "FailureThreshold": 2, - }, - )["HealthCheck"] - - conn.delete_health_check(HealthCheckId=check["Id"]) - - checks = conn.list_health_checks()["HealthChecks"] - checks.should.have.length_of(0) - - @mock_route53 def test_use_health_check_in_resource_record_set(): conn = boto3.client("route53", region_name="us-east-1") @@ -457,70 +387,6 @@ def test_deleting_latency_route(): cnames[1]["Region"].should.equal("us-west-1") -@mock_ec2 -@mock_route53 -def test_hosted_zone_private_zone_preserved(): - # Create mock VPC so we can get a VPC ID - region = "us-east-1" - ec2c = boto3.client("ec2", region_name=region) - vpc_id = ec2c.create_vpc(CidrBlock="10.1.0.0/16").get("Vpc").get("VpcId") - - # Create hosted_zone as a Private VPC Hosted Zone - conn = boto3.client("route53", region_name=region) - new_zone = conn.create_hosted_zone( - Name="testdns.aws.com.", - CallerReference=str(hash("foo")), - HostedZoneConfig=dict(PrivateZone=True, Comment="Test"), - VPC={"VPCRegion": region, "VPCId": vpc_id}, - ) - - zone_id = new_zone["HostedZone"]["Id"].split("/")[-1] - hosted_zone = conn.get_hosted_zone(Id=zone_id) - hosted_zone["HostedZone"]["Config"]["PrivateZone"].should.equal(True) - hosted_zone.should.have.key("VPCs") - hosted_zone["VPCs"].should.have.length_of(1) - hosted_zone["VPCs"][0].should.have.key("VPCId") - hosted_zone["VPCs"][0].should.have.key("VPCRegion") - hosted_zone["VPCs"][0]["VPCId"].should_not.equal(None) - hosted_zone["VPCs"][0]["VPCRegion"].should_not.equal(None) - hosted_zone["VPCs"][0]["VPCId"].should.be.equal(vpc_id) - hosted_zone["VPCs"][0]["VPCRegion"].should.be.equal(region) - - hosted_zones = conn.list_hosted_zones() - hosted_zones["HostedZones"][0]["Config"]["PrivateZone"].should.equal(True) - - hosted_zones = conn.list_hosted_zones_by_name(DNSName="testdns.aws.com.") - hosted_zones["HostedZones"].should.have.length_of(1) - hosted_zones["HostedZones"][0]["Config"]["PrivateZone"].should.equal(True) - - # create_hosted_zone statements with PrivateZone=True, - # but without a _valid_ vpc-id should NOT fail. - zone2_name = "testdns2.aws.com." - no_vpc_zone = conn.create_hosted_zone( - Name=zone2_name, - CallerReference=str(hash("foo")), - HostedZoneConfig=dict(PrivateZone=True, Comment="Test without VPC"), - ) - - zone_id = no_vpc_zone["HostedZone"]["Id"].split("/")[-1] - hosted_zone = conn.get_hosted_zone(Id=zone_id) - hosted_zone["HostedZone"]["Config"]["PrivateZone"].should.equal(True) - hosted_zone.should.have.key("VPCs") - hosted_zone["VPCs"].should.have.length_of(0) - - hosted_zones = conn.list_hosted_zones() - hosted_zones["HostedZones"].should.have.length_of(2) - hosted_zones["HostedZones"][0]["Config"]["PrivateZone"].should.equal(True) - hosted_zones["HostedZones"][1]["Config"]["PrivateZone"].should.equal(True) - - hosted_zones = conn.list_hosted_zones_by_name(DNSName=zone2_name) - hosted_zones["HostedZones"].should.have.length_of(1) - hosted_zones["HostedZones"][0]["Config"]["PrivateZone"].should.equal(True) - hosted_zones["HostedZones"][0]["Name"].should.equal(zone2_name) - - return - - @mock_route53 def test_list_or_change_tags_for_resource_request(): conn = boto3.client("route53", region_name="us-east-1") @@ -737,69 +603,6 @@ def test_list_hosted_zones_by_dns_name(): zones["HostedZones"][3]["Name"].should.equal("test.a.org.") -@mock_ec2 -@mock_route53 -def test_list_hosted_zones_by_vpc(): - # Create mock VPC so we can get a VPC ID - ec2c = boto3.client("ec2", region_name="us-east-1") - vpc_id = ec2c.create_vpc(CidrBlock="10.1.0.0/16").get("Vpc").get("VpcId") - region = "us-east-1" - - conn = boto3.client("route53", region_name=region) - zone_b = conn.create_hosted_zone( - Name="test.b.com.", - CallerReference=str(hash("foo")), - HostedZoneConfig=dict(PrivateZone=True, Comment="test com"), - VPC={"VPCRegion": region, "VPCId": vpc_id}, - ) - zone_id = zone_b["HostedZone"]["Id"].split("/")[2] - response = conn.list_hosted_zones_by_vpc(VPCId=vpc_id, VPCRegion=region) - response.should.have.key("ResponseMetadata") - response.should.have.key("HostedZoneSummaries") - response["HostedZoneSummaries"].should.have.length_of(1) - response["HostedZoneSummaries"][0].should.have.key("HostedZoneId") - retured_zone = response["HostedZoneSummaries"][0] - retured_zone["HostedZoneId"].should.equal(zone_id) - retured_zone["Name"].should.equal(zone_b["HostedZone"]["Name"]) - - -@mock_ec2 -@mock_route53 -def test_list_hosted_zones_by_vpc_with_multiple_vpcs(): - # Create mock VPC so we can get a VPC ID - ec2c = boto3.client("ec2", region_name="us-east-1") - vpc_id = ec2c.create_vpc(CidrBlock="10.1.0.0/16").get("Vpc").get("VpcId") - region = "us-east-1" - - # Create 3 Zones associate with the VPC. - zones = {} - conn = boto3.client("route53", region_name=region) - for zone in ["a", "b", "c"]: - zone_name = f"test.{zone}.com." - zones[zone] = conn.create_hosted_zone( - Name=zone_name, - CallerReference=str(hash("foo")), - HostedZoneConfig=dict(PrivateZone=True, Comment=f"test {zone} com"), - VPC={"VPCRegion": region, "VPCId": vpc_id}, - ) - - # List the zones associated with this vpc - response = conn.list_hosted_zones_by_vpc(VPCId=vpc_id, VPCRegion=region) - response.should.have.key("ResponseMetadata") - response.should.have.key("HostedZoneSummaries") - response["HostedZoneSummaries"].should.have.length_of(3) - - # Loop through all zone summaries and verify they match what was created - for summary in response["HostedZoneSummaries"]: - # use the zone name as the index - index = summary["Name"].split(".")[1] - zone_id = zones[index]["HostedZone"]["Id"].split("/")[2] - summary.should.have.key("HostedZoneId") - summary["HostedZoneId"].should.equal(zone_id) - summary.should.have.key("Name") - summary["Name"].should.equal(zones[index]["HostedZone"]["Name"]) - - @mock_route53 def test_change_resource_record_sets_crud_valid(): conn = boto3.client("route53", region_name="us-east-1") @@ -1568,3 +1371,14 @@ def test_list_resource_recordset_pagination(): response.should.have.key("MaxItems").equals("300") response.shouldnt.have.key("NextRecordName") response.shouldnt.have.key("NextRecordType") + + +@mock_route53 +def test_get_dns_sec(): + client = boto3.client("route53", region_name="us-east-1") + + hosted_zone_id = client.create_hosted_zone( + Name="testdns.aws.com.", CallerReference=str(hash("foo")) + )["HostedZone"]["Id"] + dns_sec = client.get_dnssec(HostedZoneId=hosted_zone_id) + dns_sec.should.have.key("Status").equals({"ServeSignature": "NOT_SIGNING"}) diff --git a/tests/test_route53/test_route53_boto3.py b/tests/test_route53/test_route53_healthchecks.py similarity index 69% rename from tests/test_route53/test_route53_boto3.py rename to tests/test_route53/test_route53_healthchecks.py index 5db1618d12ef..c6e3f6cde4c6 100644 --- a/tests/test_route53/test_route53_boto3.py +++ b/tests/test_route53/test_route53_healthchecks.py @@ -59,6 +59,7 @@ def test_create_health_check_with_additional_options(): "Type": "HTTP", "ResourcePath": "/", "FullyQualifiedDomainName": "example.com", + "SearchString": "a good response", "RequestInterval": 10, "FailureThreshold": 2, "MeasureLatency": True, @@ -73,9 +74,11 @@ def test_create_health_check_with_additional_options(): check.should.have.key("CallerReference").being.equal( "test-route53-health-HealthCheck-asdf" ) + check.should.have.key("HealthCheckVersion").equal(1) check.should.have.key("HealthCheckConfig") # config = check["HealthCheckConfig"] + check["HealthCheckConfig"].should.have.key("SearchString").equal("a good response") config.should.have.key("MeasureLatency").being.equal(True) config.should.have.key("Inverted").being.equal(True) config.should.have.key("Disabled").being.equal(True) @@ -203,11 +206,93 @@ def test_get_unknown_health_check(): @mock_route53 -def test_get_dns_sec(): +def test_list_health_checks(): + conn = boto3.client("route53", region_name="us-east-1") + + conn.list_health_checks()["HealthChecks"].should.have.length_of(0) + + check = conn.create_health_check( + CallerReference="?", + HealthCheckConfig={ + "IPAddress": "10.0.0.25", + "Port": 80, + "Type": "HTTP", + "ResourcePath": "/", + "FullyQualifiedDomainName": "example.com", + "SearchString": "a good response", + "RequestInterval": 10, + "FailureThreshold": 2, + }, + )["HealthCheck"] + + checks = conn.list_health_checks()["HealthChecks"] + checks.should.have.length_of(1) + checks.should.contain(check) + + +@mock_route53 +def test_delete_health_checks(): + conn = boto3.client("route53", region_name="us-east-1") + + conn.list_health_checks()["HealthChecks"].should.have.length_of(0) + + check = conn.create_health_check( + CallerReference="?", + HealthCheckConfig={ + "IPAddress": "10.0.0.25", + "Port": 80, + "Type": "HTTP", + "ResourcePath": "/", + "FullyQualifiedDomainName": "example.com", + "SearchString": "a good response", + "RequestInterval": 10, + "FailureThreshold": 2, + }, + )["HealthCheck"] + + conn.delete_health_check(HealthCheckId=check["Id"]) + + checks = conn.list_health_checks()["HealthChecks"] + checks.should.have.length_of(0) + + +@mock_route53 +def test_update_health_check(): client = boto3.client("route53", region_name="us-east-1") - hosted_zone_id = client.create_hosted_zone( - Name="testdns.aws.com.", CallerReference=str(hash("foo")) - )["HostedZone"]["Id"] - dns_sec = client.get_dnssec(HostedZoneId=hosted_zone_id) - dns_sec.should.have.key("Status").equals({"ServeSignature": "NOT_SIGNING"}) + hc_id = client.create_health_check( + CallerReference="callref", + HealthCheckConfig={ + "Type": "CALCULATED", + "Inverted": False, + "Disabled": False, + "HealthThreshold": 1, + }, + )["HealthCheck"]["Id"] + + client.update_health_check( + HealthCheckId=hc_id, + IPAddress="0.0.0.0", + Port=80, + ResourcePath="rp", + FullyQualifiedDomainName="example.com", + SearchString="search", + FailureThreshold=123, + Inverted=False, + Disabled=False, + HealthThreshold=13, + ChildHealthChecks=["child"], + Regions=["us-east-1", "us-east-2", "us-west-1"], + ) + + config = client.get_health_check(HealthCheckId=hc_id)["HealthCheck"][ + "HealthCheckConfig" + ] + config.should.have.key("Type").equals("CALCULATED") + config.should.have.key("ResourcePath").equals("rp") + config.should.have.key("FullyQualifiedDomainName").equals("example.com") + config.should.have.key("SearchString").equals("search") + config.should.have.key("Inverted").equals(False) + config.should.have.key("Disabled").equals(False) + config.should.have.key("ChildHealthChecks").equals(["child"]) + config.should.have.key("Regions").equals(["us-east-1", "us-east-2", "us-west-1"]) diff --git a/tests/test_route53/test_route53_vpcs.py b/tests/test_route53/test_route53_vpcs.py new file mode 100644 index 000000000000..e892888c454b --- /dev/null +++ b/tests/test_route53/test_route53_vpcs.py @@ -0,0 +1,236 @@ +import boto3 +import sure # noqa # pylint: disable=unused-import +import pytest + +from botocore.exceptions import ClientError +from moto import mock_ec2, mock_route53 + + +@mock_ec2 +@mock_route53 +def test_hosted_zone_private_zone_preserved(): + # Create mock VPC so we can get a VPC ID + region = "us-east-1" + ec2c = boto3.client("ec2", region_name=region) + vpc_id = ec2c.create_vpc(CidrBlock="10.1.0.0/16").get("Vpc").get("VpcId") + + # Create hosted_zone as a Private VPC Hosted Zone + conn = boto3.client("route53", region_name=region) + new_zone = conn.create_hosted_zone( + Name="testdns.aws.com.", + CallerReference=str(hash("foo")), + HostedZoneConfig=dict(PrivateZone=True, Comment="Test"), + VPC={"VPCRegion": region, "VPCId": vpc_id}, + ) + + zone_id = new_zone["HostedZone"]["Id"].split("/")[-1] + hosted_zone = conn.get_hosted_zone(Id=zone_id) + hosted_zone["HostedZone"]["Config"]["PrivateZone"].should.equal(True) + hosted_zone.should.have.key("VPCs") + hosted_zone["VPCs"].should.have.length_of(1) + hosted_zone["VPCs"][0].should.have.key("VPCId") + hosted_zone["VPCs"][0].should.have.key("VPCRegion") + hosted_zone["VPCs"][0]["VPCId"].should.be.equal(vpc_id) + hosted_zone["VPCs"][0]["VPCRegion"].should.be.equal(region) + + hosted_zones = conn.list_hosted_zones() + hosted_zones["HostedZones"][0]["Config"]["PrivateZone"].should.equal(True) + + hosted_zones = conn.list_hosted_zones_by_name(DNSName="testdns.aws.com.") + hosted_zones["HostedZones"].should.have.length_of(1) + hosted_zones["HostedZones"][0]["Config"]["PrivateZone"].should.equal(True) + + # create_hosted_zone statements with PrivateZone=True, + # but without a _valid_ vpc-id should NOT fail. + zone2_name = "testdns2.aws.com." + no_vpc_zone = conn.create_hosted_zone( + Name=zone2_name, + CallerReference=str(hash("foo")), + HostedZoneConfig=dict(PrivateZone=True, Comment="Test without VPC"), + ) + + zone_id = no_vpc_zone["HostedZone"]["Id"].split("/")[-1] + hosted_zone = conn.get_hosted_zone(Id=zone_id) + hosted_zone["HostedZone"]["Config"]["PrivateZone"].should.equal(True) + hosted_zone.should.have.key("VPCs") + hosted_zone["VPCs"].should.have.length_of(0) + + hosted_zones = conn.list_hosted_zones() + hosted_zones["HostedZones"].should.have.length_of(2) + hosted_zones["HostedZones"][0]["Config"]["PrivateZone"].should.equal(True) + hosted_zones["HostedZones"][1]["Config"]["PrivateZone"].should.equal(True) + + hosted_zones = conn.list_hosted_zones_by_name(DNSName=zone2_name) + hosted_zones["HostedZones"].should.have.length_of(1) + hosted_zones["HostedZones"][0]["Config"]["PrivateZone"].should.equal(True) + hosted_zones["HostedZones"][0]["Name"].should.equal(zone2_name) + + +@mock_ec2 +@mock_route53 +def test_list_hosted_zones_by_vpc_with_multiple_vpcs(): + # Create mock VPC so we can get a VPC ID + ec2c = boto3.client("ec2", region_name="us-east-1") + vpc_id = ec2c.create_vpc(CidrBlock="10.1.0.0/16").get("Vpc").get("VpcId") + region = "us-east-1" + + # Create 3 Zones associate with the VPC. + zones = {} + conn = boto3.client("route53", region_name=region) + for zone in ["a", "b", "c"]: + zone_name = f"test.{zone}.com." + zones[zone] = conn.create_hosted_zone( + Name=zone_name, + CallerReference=str(hash("foo")), + HostedZoneConfig=dict(PrivateZone=True, Comment=f"test {zone} com"), + VPC={"VPCRegion": region, "VPCId": vpc_id}, + ) + + # List the zones associated with this vpc + response = conn.list_hosted_zones_by_vpc(VPCId=vpc_id, VPCRegion=region) + response.should.have.key("ResponseMetadata") + response.should.have.key("HostedZoneSummaries") + response["HostedZoneSummaries"].should.have.length_of(3) + + # Loop through all zone summaries and verify they match what was created + for summary in response["HostedZoneSummaries"]: + # use the zone name as the index + index = summary["Name"].split(".")[1] + zone_id = zones[index]["HostedZone"]["Id"].split("/")[2] + summary.should.have.key("HostedZoneId") + summary["HostedZoneId"].should.equal(zone_id) + summary.should.have.key("Name") + summary["Name"].should.equal(zones[index]["HostedZone"]["Name"]) + + +@mock_ec2 +@mock_route53 +def test_list_hosted_zones_by_vpc(): + # Create mock VPC so we can get a VPC ID + ec2c = boto3.client("ec2", region_name="us-east-1") + vpc_id = ec2c.create_vpc(CidrBlock="10.1.0.0/16").get("Vpc").get("VpcId") + region = "us-east-1" + + conn = boto3.client("route53", region_name=region) + zone_b = conn.create_hosted_zone( + Name="test.b.com.", + CallerReference=str(hash("foo")), + HostedZoneConfig=dict(PrivateZone=True, Comment="test com"), + VPC={"VPCRegion": region, "VPCId": vpc_id}, + ) + zone_id = zone_b["HostedZone"]["Id"].split("/")[2] + + response = conn.list_hosted_zones_by_vpc(VPCId=vpc_id, VPCRegion=region) + response.should.have.key("ResponseMetadata") + response.should.have.key("HostedZoneSummaries") + response["HostedZoneSummaries"].should.have.length_of(1) + response["HostedZoneSummaries"][0].should.have.key("HostedZoneId") + retured_zone = response["HostedZoneSummaries"][0] + retured_zone["HostedZoneId"].should.equal(zone_id) + retured_zone["Name"].should.equal(zone_b["HostedZone"]["Name"]) + + +@mock_ec2 +@mock_route53 +def test_route53_associate_vpc(): + ec2c = boto3.client("ec2", region_name="us-east-1") + vpc_id = ec2c.create_vpc(CidrBlock="10.1.0.0/16")["Vpc"]["VpcId"] + conn = boto3.client("route53", region_name="us-east-1") + zone = conn.create_hosted_zone( + Name="test.b.com.", + CallerReference=str(hash("foo")), + HostedZoneConfig=dict(PrivateZone=True, Comment=""), + ) + zone_id = zone["HostedZone"]["Id"].split("/")[2] + + resp = conn.associate_vpc_with_hosted_zone( + HostedZoneId=zone_id, + VPC={"VPCId": vpc_id, "VPCRegion": "us-east-1"}, + Comment="yolo", + ) + resp.should.have.key("ChangeInfo") + resp["ChangeInfo"].should.have.key("Comment").equals("yolo") + + +@mock_ec2 +@mock_route53 +def test_route53_associate_vpc_with_public_Zone(): + ec2c = boto3.client("ec2", region_name="us-east-1") + vpc_id = ec2c.create_vpc(CidrBlock="10.1.0.0/16")["Vpc"]["VpcId"] + conn = boto3.client("route53", region_name="us-east-1") + zone = conn.create_hosted_zone( + Name="test.b.com.", + CallerReference=str(hash("foo")), + ) + zone_id = zone["HostedZone"]["Id"].split("/")[2] + + with pytest.raises(ClientError) as exc: + conn.associate_vpc_with_hosted_zone( + HostedZoneId=zone_id, + VPC={"VPCId": vpc_id, "VPCRegion": "us-east-1"}, + Comment="yolo", + ) + err = exc.value.response["Error"] + err["Code"].should.equal("PublicZoneVPCAssociation") + err["Message"].should.equal( + "You're trying to associate a VPC with a public hosted zone. Amazon Route 53 doesn't support associating a VPC with a public hosted zone." + ) + + +@mock_ec2 +@mock_route53 +def test_route53_associate_and_disassociate_vpc(): + ec2c = boto3.client("ec2", region_name="us-east-1") + vpc_id1 = ec2c.create_vpc(CidrBlock="10.1.0.0/16").get("Vpc").get("VpcId") + vpc_id2 = ec2c.create_vpc(CidrBlock="10.1.0.1/16").get("Vpc").get("VpcId") + region = "us-east-1" + + conn = boto3.client("route53", region_name=region) + zone = conn.create_hosted_zone( + Name="test.b.com.", + CallerReference=str(hash("foo")), + HostedZoneConfig=dict(PrivateZone=True, Comment="test com"), + VPC={"VPCRegion": region, "VPCId": vpc_id1}, + ) + zone_id = zone["HostedZone"]["Id"].split("/")[2] + + conn.associate_vpc_with_hosted_zone( + HostedZoneId=zone_id, + VPC={"VPCId": vpc_id2, "VPCRegion": region}, + ) + + zone_vpcs = conn.get_hosted_zone(Id=zone_id)["VPCs"] + zone_vpcs.should.have.length_of(2) + zone_vpcs.should.contain({"VPCRegion": region, "VPCId": vpc_id1}) + zone_vpcs.should.contain({"VPCRegion": region, "VPCId": vpc_id2}) + + conn.disassociate_vpc_from_hosted_zone(HostedZoneId=zone_id, VPC={"VPCId": vpc_id1}) + + zone_vpcs = conn.get_hosted_zone(Id=zone_id)["VPCs"] + zone_vpcs.should.have.length_of(1) + zone_vpcs.should.contain({"VPCRegion": region, "VPCId": vpc_id2}) + + +@mock_ec2 +@mock_route53 +def test_route53_disassociate_last_vpc(): + ec2c = boto3.client("ec2", region_name="us-east-1") + vpc_id = ec2c.create_vpc(CidrBlock="10.1.0.0/16")["Vpc"]["VpcId"] + conn = boto3.client("route53", region_name="us-east-1") + zone = conn.create_hosted_zone( + Name="test.b.com.", + CallerReference=str(hash("foo")), + HostedZoneConfig=dict(PrivateZone=True, Comment="test com"), + VPC={"VPCRegion": "us-east-1", "VPCId": vpc_id}, + ) + zone_id = zone["HostedZone"]["Id"].split("/")[2] + + with pytest.raises(ClientError) as exc: + conn.disassociate_vpc_from_hosted_zone( + HostedZoneId=zone_id, VPC={"VPCId": vpc_id} + ) + err = exc.value.response["Error"] + err["Code"].should.equal("LastVPCAssociation") + err["Message"].should.equal( + "The VPC that you're trying to disassociate from the private hosted zone is the last VPC that is associated with the hosted zone. Amazon Route 53 doesn't support disassociating the last VPC from a hosted zone." + )