EC2: Support all (current) instance-type filters (#5526)

This commit is contained in:
Laurie O 2022-10-04 19:48:10 +10:00 committed by GitHub
parent b8932b19c9
commit 148de0e562
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 129 additions and 17 deletions

View File

@ -427,8 +427,8 @@ class InvalidServiceName(EC2ClientError):
class InvalidFilter(EC2ClientError):
def __init__(self, filter_name):
super().__init__("InvalidFilter", f"The filter '{filter_name}' is invalid")
def __init__(self, filter_name, error_type="InvalidFilter"):
super().__init__(error_type, f"The filter '{filter_name}' is invalid")
class InvalidNextToken(EC2ClientError):

View File

@ -4,7 +4,7 @@ from os import listdir
from ..utils import generic_filter
from moto.utilities.utils import load_resource
from ..exceptions import FilterNotImplementedError, InvalidInstanceTypeError
from ..exceptions import InvalidFilter, InvalidInstanceTypeError
INSTANCE_TYPES = load_resource(__name__, "../resources/instance_types.json")
INSTANCE_FAMILIES = list(set([i.split(".")[0] for i in INSTANCE_TYPES.keys()]))
@ -23,6 +23,87 @@ for location_type in listdir(root / offerings_path):
class InstanceType(dict):
_filter_attributes = {
"auto-recovery-supported": ["AutoRecoverySupported"],
"bare-metal": ["BareMetal"],
"burstable-performance-supported": ["BurstablePerformanceSupported"],
"current-generation": ["CurrentGeneration"],
"ebs-info.ebs-optimized-info.baseline-bandwidth-in-mbps": [
"EbsInfo", "EbsOptimizedInfo", "BaselineBandwidthInMbps"
],
"ebs-info.ebs-optimized-info.baseline-iops": [
"EbsInfo", "EbsOptimizedInfo", "BaselineIops"
],
"ebs-info.ebs-optimized-info.baseline-throughput-in-mbps": [
"EbsInfo", "EbsOptimizedInfo", "BaselineThroughputInMBps"
],
"ebs-info.ebs-optimized-info.maximum-bandwidth-in-mbps": [
"EbsInfo", "EbsOptimizedInfo", "MaximumBandwidthInMbps"
],
"ebs-info.ebs-optimized-info.maximum-iops": [
"EbsInfo", "EbsOptimizedInfo", "MaximumIops"
],
"ebs-info.ebs-optimized-info.maximum-throughput-in-mbps": [
"EbsInfo", "EbsOptimizedInfo", "MaximumThroughputInMBps"
],
"ebs-info.ebs-optimized-support": ["EbsInfo", "EbsOptimizedSupport"],
"ebs-info.encryption-support": ["EbsInfo", "EncryptionSupport"],
"ebs-info.nvme-support": ["EbsInfo", "NvmeSupport"],
"free-tier-eligible": ["FreeTierEligible"],
"hibernation-supported": ["HibernationSupported"],
"hypervisor": ["Hypervisor"],
"instance-storage-info.disk.count": ["InstanceStorageInfo", "Disks", "Count"],
"instance-storage-info.disk.size-in-gb": [
"InstanceStorageInfo", "Disks", "SizeInGB"
],
"instance-storage-info.disk.type": ["InstanceStorageInfo", "Disks", "Type"],
"instance-storage-info.encryption-support": [
"InstanceStorageInfo", "EncryptionSupport"
],
"instance-storage-info.nvme-support": ["InstanceStorageInfo", "NvmeSupport"],
"instance-storage-info.total-size-in-gb": [
"InstanceStorageInfo", "TotalSizeInGB"
],
"instance-storage-supported": ["InstanceStorageSupported"],
"instance-type": ["InstanceType"],
"memory-info.size-in-mib": ["MemoryInfo", "SizeInMiB"],
"network-info.efa-info.maximum-efa-interfaces": [
"NetworkInfo", "EfaInfo", "MaximumEfaInterfaces"
],
"network-info.efa-supported": ["NetworkInfo", "EfaSupported"],
"network-info.ena-support": ["NetworkInfo", "EnaSupport"],
"network-info.encryption-in-transit-supported": [
"NetworkInfo", "EncryptionInTransitSupported"
],
"network-info.ipv4-addresses-per-interface": [
"NetworkInfo", "Ipv4AddressesPerInterface"
],
"network-info.ipv6-addresses-per-interface": [
"NetworkInfo", "Ipv6AddressesPerInterface"
],
"network-info.ipv6-supported": ["NetworkInfo", "Ipv6Supported"],
"network-info.maximum-network-cards": ["NetworkInfo", "MaximumNetworkCards"],
"network-info.maximum-network-interfaces": [
"NetworkInfo", "MaximumNetworkInterfaces"
],
"network-info.network-performance": ["NetworkInfo", "NetworkPerformance"],
"processor-info.supported-architecture": [
"ProcessorInfo", "SupportedArchitectures"
],
"processor-info.sustained-clock-speed-in-ghz": [
"ProcessorInfo", "SustainedClockSpeedInGhz"
],
"supported-boot-mode": ["SupportedBootModes"],
"supported-root-device-type": ["SupportedRootDeviceTypes"],
"supported-usage-class": ["SupportedUsageClasses"],
"supported-virtualization-type": ["SupportedVirtualizationTypes"],
"vcpu-info.default-cores": ["VCpuInfo", "DefaultCores"],
"vcpu-info.default-threads-per-core": ["VCpuInfo", "DefaultThreadsPerCore"],
"vcpu-info.default-vcpus": ["VCpuInfo", "DefaultVCpus"],
"vcpu-info.valid-cores": ["VCpuInfo", "ValidCores"],
"vcpu-info.valid-threads-per-core": ["VCpuInfo", "ValidThreadsPerCore"],
} # fmt: skip
def __init__(self, name):
self.name = name
self.update(INSTANCE_TYPES[name])
@ -37,20 +118,20 @@ class InstanceType(dict):
return "<InstanceType: %s>" % self.name
def get_filter_value(self, filter_name):
if filter_name in ("instance-type"):
return self.get("InstanceType")
elif filter_name in ("vcpu-info.default-vcpus"):
return str(self.get("VCpuInfo").get("DefaultVCpus"))
elif filter_name in ("memory-info.size-in-mib"):
return str(self.get("MemoryInfo").get("SizeInMiB"))
elif filter_name in ("bare-metal"):
return str(self.get("BareMetal")).lower()
elif filter_name in ("burstable-performance-supported"):
return str(self.get("BurstablePerformanceSupported")).lower()
elif filter_name in ("current-generation"):
return str(self.get("CurrentGeneration")).lower()
else:
return FilterNotImplementedError(filter_name, "DescribeInstanceTypes")
def stringify(v):
if isinstance(v, (bool, int)):
return str(v).lower()
elif isinstance(v, list):
return [stringify(i) for i in v]
return v
path = self._filter_attributes.get(filter_name)
if not path:
raise InvalidFilter(filter_name, error_type="InvalidParameterValue")
value = self
for key in path:
value = (value or {}).get(key)
return stringify(value)
class InstanceTypeBackend:

View File

@ -180,3 +180,34 @@ def test_describe_instance_types_filter_by_current_generation():
# not contain
types.should_not.contain("t1.micro")
@mock_ec2
def test_describe_instance_types_small_instances():
client = boto3.client("ec2", "us-east-1")
instance_types = client.describe_instance_types(Filters=[
{"Name": "bare-metal", "Values": ["false"]},
{"Name": "current-generation", "Values": ["true"]},
{"Name": "vcpu-info.default-cores", "Values": ["1"]},
{"Name": "memory-info.size-in-mib", "Values": ["512", "1024"]},
{"Name": "vcpu-info.valid-threads-per-core", "Values": ["1"]},
]) # fmt: skip
types = set(t["InstanceType"] for t in instance_types["InstanceTypes"])
types.should.equal({"t3.nano", "t3.micro", "t3a.nano", "t3a.micro"})
@mock_ec2
def test_describe_instance_types_invalid_filter():
client = boto3.client("ec2", "us-east-1")
with pytest.raises(ClientError) as exc_info:
client.describe_instance_types(
Filters=[{"Name": "spam", "Values": ["eggs"]}],
)
exc_info.value.response["Error"]["Code"].should.equal("InvalidParameterValue")
exc_info.value.response["Error"]["Message"].split(":")[0].should.equal(
"The filter 'spam' is invalid",
)
exc_info.value.response["ResponseMetadata"]["HTTPStatusCode"].should.equal(400)