Feature: ServiceQuotas (#5557)

This commit is contained in:
Bert Blommers 2022-10-11 22:09:17 +00:00 committed by GitHub
parent 9e2cf62d80
commit 1be71f9036
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 568 additions and 4 deletions

View File

@ -5723,6 +5723,31 @@
- [ ] validate_resource_policy
</details>
## service-quotas
<details>
<summary>10% implemented</summary>
- [ ] associate_service_quota_template
- [ ] delete_service_quota_increase_request_from_template
- [ ] disassociate_service_quota_template
- [ ] get_association_for_service_quota_template
- [ ] get_aws_default_service_quota
- [ ] get_requested_service_quota_change
- [X] get_service_quota
- [ ] get_service_quota_increase_request_from_template
- [X] list_aws_default_service_quotas
- [ ] list_requested_service_quota_change_history
- [ ] list_requested_service_quota_change_history_by_quota
- [ ] list_service_quota_increase_requests_in_template
- [ ] list_service_quotas
- [ ] list_services
- [ ] list_tags_for_resource
- [ ] put_service_quota_increase_request_into_template
- [ ] request_service_quota_increase
- [ ] tag_resource
- [ ] untag_resource
</details>
## servicediscovery
<details>
<summary>61% implemented</summary>
@ -6537,7 +6562,6 @@
- schemas
- securityhub
- serverlessrepo
- service-quotas
- servicecatalog
- servicecatalog-appregistry
- sesv2

View File

@ -9,6 +9,7 @@ include moto/ec2/resources/amis.json
include moto/cognitoidp/resources/*.json
include moto/dynamodb/parsing/reserved_keywords.txt
include moto/moto_api/_internal/*
include moto/servicequotas/resources/*/*.json
include moto/ssm/resources/*.json
include moto/ssm/resources/ami-amazon-linux-latest/*.json
include moto/support/resources/*.json

View File

@ -0,0 +1,54 @@
.. _implementedservice_service-quotas:
.. |start-h3| raw:: html
<h3>
.. |end-h3| raw:: html
</h3>
==============
service-quotas
==============
.. autoclass:: moto.servicequotas.models.ServiceQuotasBackend
|start-h3| Example usage |end-h3|
.. sourcecode:: python
@mock_servicequotas
def test_servicequotas_behaviour:
boto3.client("service-quotas")
...
|start-h3| Implemented features for this service |end-h3|
- [ ] associate_service_quota_template
- [ ] delete_service_quota_increase_request_from_template
- [ ] disassociate_service_quota_template
- [ ] get_association_for_service_quota_template
- [ ] get_aws_default_service_quota
- [ ] get_requested_service_quota_change
- [X] get_service_quota
- [ ] get_service_quota_increase_request_from_template
- [X] list_aws_default_service_quotas
The ServiceCodes that are currently implemented are: vpc
Pagination is not yet implemented.
- [ ] list_requested_service_quota_change_history
- [ ] list_requested_service_quota_change_history_by_quota
- [ ] list_service_quota_increase_requests_in_template
- [ ] list_service_quotas
- [ ] list_services
- [ ] list_tags_for_resource
- [ ] put_service_quota_increase_request_into_template
- [ ] request_service_quota_increase
- [ ] tag_resource
- [ ] untag_resource

View File

@ -138,6 +138,9 @@ mock_s3control = lazy_load(".s3control", "mock_s3control")
mock_sagemaker = lazy_load(".sagemaker", "mock_sagemaker")
mock_sdb = lazy_load(".sdb", "mock_sdb")
mock_secretsmanager = lazy_load(".secretsmanager", "mock_secretsmanager")
mock_servicequotas = lazy_load(
".servicequotas", "mock_servicequotas", boto3_name="service-quotas"
)
mock_ses = lazy_load(".ses", "mock_ses")
mock_servicediscovery = lazy_load(".servicediscovery", "mock_servicediscovery")
mock_signer = lazy_load(".signer", "mock_signer", boto3_name="signer")

View File

@ -148,6 +148,7 @@ backend_url_patterns = [
"servicediscovery",
re.compile("https?://servicediscovery\\.(.+)\\.amazonaws\\.com"),
),
("service-quotas", re.compile("https?://servicequotas\\.(.+)\\.amazonaws\\.com")),
("ses", re.compile("https?://email\\.(.+)\\.amazonaws\\.com")),
("ses", re.compile("https?://ses\\.(.+)\\.amazonaws\\.com")),
("signer", re.compile("https?://signer\\.(.+)\\.amazonaws\\.com")),

View File

@ -0,0 +1,5 @@
"""servicequotas module initialization; sets value for base decorator."""
from .models import servicequotas_backends
from ..core.models import base_decorator
mock_servicequotas = base_decorator(servicequotas_backends)

View File

@ -0,0 +1,10 @@
"""Exceptions raised by the servicequotas service."""
from moto.core.exceptions import JsonRESTError
class NoSuchResource(JsonRESTError):
def __init__(self) -> None:
super().__init__(
"NoSuchResourceException",
"This service is not available in the current Region. Choose a different Region or a different service.",
)

View File

@ -0,0 +1,35 @@
"""ServiceQuotasBackend class with methods for supported APIs."""
from moto.core import BaseBackend
from moto.core.utils import BackendDict
from typing import Any, Dict, List
from .exceptions import NoSuchResource
from .resources.default_quotas.vpc import VPC_DEFAULT_QUOTAS
class ServiceQuotasBackend(BaseBackend):
"""Implementation of ServiceQuotas APIs."""
def __init__(self, region_name: str, account_id: str):
super().__init__(region_name, account_id)
def list_aws_default_service_quotas(
self, service_code: str
) -> List[Dict[str, Any]]:
"""
The ServiceCodes that are currently implemented are: vpc
Pagination is not yet implemented.
"""
if service_code == "vpc":
return VPC_DEFAULT_QUOTAS
raise NoSuchResource
def get_service_quota(self, service_code: str, quota_code: str) -> Dict[str, Any]:
if service_code == "vpc":
for quota in VPC_DEFAULT_QUOTAS:
if quota["QuotaCode"] == quota_code:
return quota
raise NoSuchResource
servicequotas_backends = BackendDict(ServiceQuotasBackend, "service-quotas")

View File

View File

@ -0,0 +1,277 @@
VPC_DEFAULT_QUOTAS = [
{
"Adjustable": True,
"GlobalQuota": False,
"QuotaArn": "arn:aws:servicequotas:eu-west-1::vpc/L-7E9ECCDB",
"QuotaCode": "L-7E9ECCDB",
"QuotaName": "Active VPC peering connections per VPC",
"ServiceCode": "vpc",
"ServiceName": "Amazon Virtual Private Cloud (Amazon VPC)",
"Unit": "None",
"Value": 50.0,
},
{
"Adjustable": False,
"GlobalQuota": False,
"QuotaArn": "arn:aws:servicequotas:eu-west-1::vpc/L-3248932A",
"QuotaCode": "L-3248932A",
"QuotaName": "Characters per VPC endpoint policy",
"ServiceCode": "vpc",
"ServiceName": "Amazon Virtual Private Cloud (Amazon VPC)",
"Unit": "None",
"Value": 20480.0,
},
{
"Adjustable": True,
"GlobalQuota": False,
"QuotaArn": "arn:aws:servicequotas:eu-west-1::vpc/L-45FE3B85",
"QuotaCode": "L-45FE3B85",
"QuotaName": "Egress-only internet gateways per Region",
"ServiceCode": "vpc",
"ServiceName": "Amazon Virtual Private Cloud (Amazon VPC)",
"Unit": "None",
"Value": 5.0,
},
{
"Adjustable": True,
"GlobalQuota": False,
"QuotaArn": "arn:aws:servicequotas:eu-west-1::vpc/L-1B52E74A",
"QuotaCode": "L-1B52E74A",
"QuotaName": "Gateway VPC endpoints per Region",
"ServiceCode": "vpc",
"ServiceName": "Amazon Virtual Private Cloud (Amazon VPC)",
"Unit": "None",
"Value": 20.0,
},
{
"Adjustable": True,
"GlobalQuota": False,
"QuotaArn": "arn:aws:servicequotas:eu-west-1::vpc/L-83CA0A9D",
"QuotaCode": "L-83CA0A9D",
"QuotaName": "IPv4 CIDR blocks per VPC",
"ServiceCode": "vpc",
"ServiceName": "Amazon Virtual Private Cloud (Amazon VPC)",
"Unit": "None",
"Value": 5.0,
},
{
"Adjustable": True,
"GlobalQuota": False,
"QuotaArn": "arn:aws:servicequotas:eu-west-1::vpc/L-085A6257",
"QuotaCode": "L-085A6257",
"QuotaName": "IPv6 CIDR blocks per VPC",
"ServiceCode": "vpc",
"ServiceName": "Amazon Virtual Private Cloud (Amazon VPC)",
"Unit": "None",
"Value": 5.0,
},
{
"Adjustable": True,
"GlobalQuota": False,
"QuotaArn": "arn:aws:servicequotas:eu-west-1::vpc/L-0EA8095F",
"QuotaCode": "L-0EA8095F",
"QuotaName": "Inbound or outbound rules per security group",
"ServiceCode": "vpc",
"ServiceName": "Amazon Virtual Private Cloud (Amazon VPC)",
"Unit": "None",
"Value": 60.0,
},
{
"Adjustable": True,
"GlobalQuota": False,
"QuotaArn": "arn:aws:servicequotas:eu-west-1::vpc/L-29B6F2EB",
"QuotaCode": "L-29B6F2EB",
"QuotaName": "Interface VPC endpoints per VPC",
"ServiceCode": "vpc",
"ServiceName": "Amazon Virtual Private Cloud (Amazon VPC)",
"Unit": "None",
"Value": 50.0,
},
{
"Adjustable": True,
"GlobalQuota": False,
"QuotaArn": "arn:aws:servicequotas:eu-west-1::vpc/L-A4707A72",
"QuotaCode": "L-A4707A72",
"QuotaName": "Internet gateways per Region",
"ServiceCode": "vpc",
"ServiceName": "Amazon Virtual Private Cloud (Amazon VPC)",
"Unit": "None",
"Value": 5.0,
},
{
"Adjustable": True,
"GlobalQuota": False,
"QuotaArn": "arn:aws:servicequotas:eu-west-1::vpc/L-FE5A380F",
"QuotaCode": "L-FE5A380F",
"QuotaName": "NAT gateways per Availability Zone",
"ServiceCode": "vpc",
"ServiceName": "Amazon Virtual Private Cloud (Amazon VPC)",
"Unit": "None",
"Value": 5.0,
},
{
"Adjustable": True,
"GlobalQuota": False,
"QuotaArn": "arn:aws:servicequotas:eu-west-1::vpc/L-B4A6D682",
"QuotaCode": "L-B4A6D682",
"QuotaName": "Network ACLs per VPC",
"ServiceCode": "vpc",
"ServiceName": "Amazon Virtual Private Cloud (Amazon VPC)",
"Unit": "None",
"Value": 200.0,
},
{
"Adjustable": True,
"GlobalQuota": False,
"QuotaArn": "arn:aws:servicequotas:eu-west-1::vpc/L-BB24F6E5",
"QuotaCode": "L-BB24F6E5",
"QuotaName": "Network Address Usage",
"ServiceCode": "vpc",
"ServiceName": "Amazon Virtual Private Cloud (Amazon VPC)",
"Unit": "None",
"Value": 64000.0,
},
{
"Adjustable": True,
"GlobalQuota": False,
"QuotaArn": "arn:aws:servicequotas:eu-west-1::vpc/L-DF5E4CA3",
"QuotaCode": "L-DF5E4CA3",
"QuotaName": "Network interfaces per Region",
"ServiceCode": "vpc",
"ServiceName": "Amazon Virtual Private Cloud (Amazon VPC)",
"Unit": "None",
"Value": 5000.0,
},
{
"Adjustable": True,
"GlobalQuota": False,
"QuotaArn": "arn:aws:servicequotas:eu-west-1::vpc/L-DC9F7029",
"QuotaCode": "L-DC9F7029",
"QuotaName": "Outstanding VPC peering connection requests",
"ServiceCode": "vpc",
"ServiceName": "Amazon Virtual Private Cloud (Amazon VPC)",
"Unit": "None",
"Value": 25.0,
},
{
"Adjustable": True,
"GlobalQuota": False,
"QuotaArn": "arn:aws:servicequotas:eu-west-1::vpc/L-2C462E13",
"QuotaCode": "L-2C462E13",
"QuotaName": "Participant accounts per VPC",
"ServiceCode": "vpc",
"ServiceName": "Amazon Virtual Private Cloud (Amazon VPC)",
"Unit": "None",
"Value": 100.0,
},
{
"Adjustable": True,
"GlobalQuota": False,
"QuotaArn": "arn:aws:servicequotas:eu-west-1::vpc/L-CD17FD4B",
"QuotaCode": "L-CD17FD4B",
"QuotaName": "Peered Network Address Usage",
"ServiceCode": "vpc",
"ServiceName": "Amazon Virtual Private Cloud (Amazon VPC)",
"Unit": "None",
"Value": 128000.0,
},
{
"Adjustable": True,
"GlobalQuota": False,
"QuotaArn": "arn:aws:servicequotas:eu-west-1::vpc/L-589F43AA",
"QuotaCode": "L-589F43AA",
"QuotaName": "Route tables per VPC",
"ServiceCode": "vpc",
"ServiceName": "Amazon Virtual Private Cloud (Amazon VPC)",
"Unit": "None",
"Value": 200.0,
},
{
"Adjustable": True,
"GlobalQuota": False,
"QuotaArn": "arn:aws:servicequotas:eu-west-1::vpc/L-93826ACB",
"QuotaCode": "L-93826ACB",
"QuotaName": "Routes per route table",
"ServiceCode": "vpc",
"ServiceName": "Amazon Virtual Private Cloud (Amazon VPC)",
"Unit": "None",
"Value": 50.0,
},
{
"Adjustable": True,
"GlobalQuota": False,
"QuotaArn": "arn:aws:servicequotas:eu-west-1::vpc/L-2AEEBF1A",
"QuotaCode": "L-2AEEBF1A",
"QuotaName": "Rules per network ACL",
"ServiceCode": "vpc",
"ServiceName": "Amazon Virtual Private Cloud (Amazon VPC)",
"Unit": "None",
"Value": 20.0,
},
{
"Adjustable": True,
"GlobalQuota": False,
"QuotaArn": "arn:aws:servicequotas:eu-west-1::vpc/L-2AFB9258",
"QuotaCode": "L-2AFB9258",
"QuotaName": "Security groups per network interface",
"ServiceCode": "vpc",
"ServiceName": "Amazon Virtual Private Cloud (Amazon VPC)",
"Unit": "None",
"Value": 5.0,
},
{
"Adjustable": True,
"GlobalQuota": False,
"QuotaArn": "arn:aws:servicequotas:eu-west-1::vpc/L-407747CB",
"QuotaCode": "L-407747CB",
"QuotaName": "Subnets per VPC",
"ServiceCode": "vpc",
"ServiceName": "Amazon Virtual Private Cloud (Amazon VPC)",
"Unit": "None",
"Value": 200.0,
},
{
"Adjustable": True,
"GlobalQuota": False,
"QuotaArn": "arn:aws:servicequotas:eu-west-1::vpc/L-44499CD2",
"QuotaCode": "L-44499CD2",
"QuotaName": "Subnets that can be shared with an account",
"ServiceCode": "vpc",
"ServiceName": "Amazon Virtual Private Cloud (Amazon VPC)",
"Unit": "None",
"Value": 100.0,
},
{
"Adjustable": False,
"GlobalQuota": False,
"QuotaArn": "arn:aws:servicequotas:eu-west-1::vpc/L-8312C5BB",
"QuotaCode": "L-8312C5BB",
"QuotaName": "VPC peering connection request expiry hours",
"ServiceCode": "vpc",
"ServiceName": "Amazon Virtual Private Cloud (Amazon VPC)",
"Unit": "None",
"Value": 168.0,
},
{
"Adjustable": True,
"GlobalQuota": False,
"QuotaArn": "arn:aws:servicequotas:eu-west-1::vpc/L-E79EC296",
"QuotaCode": "L-E79EC296",
"QuotaName": "VPC security groups per Region",
"ServiceCode": "vpc",
"ServiceName": "Amazon Virtual Private Cloud (Amazon VPC)",
"Unit": "None",
"Value": 2500.0,
},
{
"Adjustable": True,
"GlobalQuota": False,
"QuotaArn": "arn:aws:servicequotas:eu-west-1::vpc/L-F678F1CE",
"QuotaCode": "L-F678F1CE",
"QuotaName": "VPCs per Region",
"ServiceCode": "vpc",
"ServiceName": "Amazon Virtual Private Cloud (Amazon VPC)",
"Unit": "None",
"Value": 5.0,
},
]

View File

@ -0,0 +1,33 @@
"""Handles incoming servicequotas requests, invokes methods, returns responses."""
import json
from moto.core.responses import BaseResponse
from .models import servicequotas_backends, ServiceQuotasBackend
class ServiceQuotasResponse(BaseResponse):
"""Handler for ServiceQuotas requests and responses."""
def __init__(self) -> None:
super().__init__(service_name="service-quotas")
@property
def backend(self) -> ServiceQuotasBackend:
"""Return backend instance specific for this region."""
return servicequotas_backends[self.current_account][self.region]
def list_aws_default_service_quotas(self) -> str:
params = json.loads(self.body)
service_code = str(params.get("ServiceCode"))
quotas = self.backend.list_aws_default_service_quotas(service_code)
return json.dumps(dict(Quotas=quotas))
def get_service_quota(self) -> str:
params = json.loads(self.body)
service_code = str(params.get("ServiceCode"))
quota_code = str(params.get("QuotaCode"))
quota = self.backend.get_service_quota(
service_code=service_code,
quota_code=quota_code,
)
return json.dumps(dict(Quota=quota))

View File

@ -0,0 +1,11 @@
"""servicequotas base URL and path."""
from .responses import ServiceQuotasResponse
url_bases = [
r"https?://servicequotas\.(.+)\.amazonaws\.com",
]
url_paths = {
"{0}/$": ServiceQuotasResponse.dispatch,
}

View File

@ -87,10 +87,7 @@ ec2:
- TestAccEC2CarrierGateway_
- TestAccEC2InstanceTypeOfferingDataSource_
- TestAccEC2InstanceTypeOfferingsDataSource_
- TestAccEC2InternetGateway_
- TestAccEC2NATGateway_
- TestAccEC2RouteTableAssociation_
- TestAccEC2SecurityGroups
- TestAccEC2SpotInstanceRequest_disappears
- TestAccEC2SpotInstanceRequest_interruptUpdate
- TestAccEC2VPCEndpointService_
@ -99,6 +96,42 @@ ec2:
- TestAccEC2VPNGateway_
- TestAccEC2VPNGatewayAttachment_
- TestAccVPC_
- TestAccVPCEgressOnlyInternetGateway_
- TestAccVPCInternetGateway
- TestAccVPCNATGateway_
- TestAccVPCSecurityGroupDataSource_basic
- TestAccVPCSecurityGroupRule_
- TestAccVPCSecurityGroup_allowAll
- TestAccVPCSecurityGroup_basic
- TestAccVPCSecurityGroup_change
- TestAccVPCSecurityGroup_cidrAndGroups
- TestAccVPCSecurityGroup_defaultEgressVPC
- TestAccVPCSecurityGroup_disappears
- TestAccVPCSecurityGroup_driftComplex
- TestAccVPCSecurityGroup_egressMode
- TestAccVPCSecurityGroup_egressWithPrefixList
- TestAccVPCSecurityGroup_failWithDiffMismatch
- TestAccVPCSecurityGroup_ingressMode
- TestAccVPCSecurityGroup_ingressWithCIDRAndSGsVPC
- TestAccVPCSecurityGroup_ingressWithPrefixList
- TestAccVPCSecurityGroup_invalidCIDRBlock
- TestAccVPCSecurityGroup_ipRangeAndSecurityGroupWithSameRules
- TestAccVPCSecurityGroup_ipRangesWithSameRules
- TestAccVPCSecurityGroup_ipv4AndIPv6Egress
- TestAccVPCSecurityGroup_ipv6
- TestAccVPCSecurityGroup_multiIngress
- TestAccVPCSecurityGroup_nameGenerated
- TestAccVPCSecurityGroup_namePrefix
- TestAccVPCSecurityGroup_namePrefixTerraform
- TestAccVPCSecurityGroup_nameTerraformPrefix
- TestAccVPCSecurityGroup_noVPC
- TestAccVPCSecurityGroup_ruleDescription
- TestAccVPCSecurityGroup_ruleGathering
- TestAccVPCSecurityGroup_self
- TestAccVPCSecurityGroup_sourceSecurityGroup
- TestAccVPCSecurityGroup_tags
- TestAccVPCSecurityGroup_vpc
- TestAccVPCSecurityGroups
ecr:
- TestAccECRLifecyclePolicy
- TestAccECRRegistryPolicy

View File

View File

@ -0,0 +1,77 @@
"""Unit tests for servicequotas-supported APIs."""
import boto3
import pytest
import sure # noqa # pylint: disable=unused-import
from moto import mock_servicequotas
from botocore.exceptions import ClientError
# See our Development Tips on writing tests for hints on how to write good tests:
# http://docs.getmoto.org/en/latest/docs/contributing/development_tips/tests.html
@mock_servicequotas
def test_list_aws_default_service_quotas():
client = boto3.client("service-quotas", region_name="eu-west-1")
resp = client.list_aws_default_service_quotas(ServiceCode="vpc")
resp.should.have.key("Quotas").length_of(25)
resp["Quotas"].should.contain(
{
"Adjustable": True,
"GlobalQuota": False,
"QuotaArn": "arn:aws:servicequotas:eu-west-1::vpc/L-2AFB9258",
"QuotaCode": "L-2AFB9258",
"QuotaName": "Security groups per network interface",
"ServiceCode": "vpc",
"ServiceName": "Amazon Virtual Private Cloud (Amazon VPC)",
"Unit": "None",
"Value": 5.0,
}
)
resp["Quotas"].should.contain(
{
"Adjustable": True,
"GlobalQuota": False,
"QuotaArn": "arn:aws:servicequotas:eu-west-1::vpc/L-F678F1CE",
"QuotaCode": "L-F678F1CE",
"QuotaName": "VPCs per Region",
"ServiceCode": "vpc",
"ServiceName": "Amazon Virtual Private Cloud (Amazon VPC)",
"Unit": "None",
"Value": 5.0,
}
)
@mock_servicequotas
def test_list_defaults_for_unknown_service():
client = boto3.client("service-quotas", "us-east-1")
with pytest.raises(ClientError) as exc:
client.list_aws_default_service_quotas(ServiceCode="unknown")
err = exc.value.response["Error"]
err["Code"].should.equal("NoSuchResourceException")
err["Message"].should.equal(
"This service is not available in the current Region. Choose a different Region or a different service."
)
@mock_servicequotas
def test_get_service_quota():
client = boto3.client("service-quotas", region_name="us-east-2")
quotas = client.list_aws_default_service_quotas(ServiceCode="vpc")["Quotas"]
for quota in quotas:
resp = client.get_service_quota(ServiceCode="vpc", QuotaCode=quota["QuotaCode"])
quota.should.equal(resp["Quota"])
@mock_servicequotas
def test_get_unknown_service_quota():
client = boto3.client("service-quotas", region_name="us-east-2")
with pytest.raises(ClientError) as exc:
client.get_service_quota(ServiceCode="vpc", QuotaCode="unknown")
err = exc.value.response["Error"]
err["Code"].should.equal("NoSuchResourceException")