DescribeInstanceTypeOfferings - script that hardcodes all offerings

This commit is contained in:
Bert Blommers 2020-12-02 14:41:24 +00:00
parent 5d208bfd04
commit 936fb19539
65 changed files with 188 additions and 2158 deletions

View File

@ -34,6 +34,7 @@ from moto.core.utils import (
) )
from moto.core import ACCOUNT_ID from moto.core import ACCOUNT_ID
from moto.kms import kms_backends from moto.kms import kms_backends
from os import listdir
from .exceptions import ( from .exceptions import (
CidrLimitExceeded, CidrLimitExceeded,
@ -175,9 +176,20 @@ INSTANCE_TYPES = _load_resource(
resource_filename(__name__, "resources/instance_types.json") resource_filename(__name__, "resources/instance_types.json")
) )
INSTANCE_TYPE_OFFERINGS = _load_resource( offerings_path = "resources/instance_type_offerings"
resource_filename(__name__, "resources/instance_type_offerings.json") INSTANCE_TYPE_OFFERINGS = {}
) for location_type in listdir(resource_filename(__name__, offerings_path)):
INSTANCE_TYPE_OFFERINGS[location_type] = {}
for region in listdir(
resource_filename(__name__, offerings_path + "/" + location_type)
):
full_path = resource_filename(
__name__, offerings_path + "/" + location_type + "/" + region
)
INSTANCE_TYPE_OFFERINGS[location_type][
region.replace(".json", "")
] = _load_resource(full_path)
AMIS = _load_resource( AMIS = _load_resource(
os.environ.get("MOTO_AMIS_PATH") os.environ.get("MOTO_AMIS_PATH")
@ -1151,25 +1163,27 @@ class InstanceTypeOfferingBackend(object):
super(InstanceTypeOfferingBackend, self).__init__() super(InstanceTypeOfferingBackend, self).__init__()
def describe_instance_type_offerings(self, location_type=None, filters=None): def describe_instance_type_offerings(self, location_type=None, filters=None):
matches = INSTANCE_TYPE_OFFERINGS
location_type = location_type or "region" location_type = location_type or "region"
matches = INSTANCE_TYPE_OFFERINGS[location_type]
matches = matches[self.region_name]
def matches_filters(offering, filters): def matches_filters(offering, filters):
for key, values in filters.items(): def matches_filter(key, values):
if key == "location": if key == "location":
if location_type in ("availability-zone", "availability-zone-id"): if location_type in ("availability-zone", "availability-zone-id"):
return offering.get("location") in values return offering.get("Location") in values
elif location_type == "region": elif location_type == "region":
return any( return any(
v for v in values if offering.get("location").startswith(v) v for v in values if offering.get("Location").startswith(v)
) )
else: else:
return False return False
elif key == "instance-type": elif key == "instance-type":
return offering.get("instance_type") in values return offering.get("InstanceType") in values
else: else:
return False return False
return True
return all([matches_filter(key, values) for key, values in filters.items()])
matches = [o for o in matches if matches_filters(o, filters)] matches = [o for o in matches if matches_filters(o, filters)]
return matches return matches

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -149,7 +149,7 @@ class InstanceResponse(BaseResponse):
return template.render(instance_types=instance_types) return template.render(instance_types=instance_types)
def describe_instance_type_offerings(self): def describe_instance_type_offerings(self):
location_type_filters = self._get_multi_param("LocationType") location_type_filters = self._get_param("LocationType")
filter_dict = filters_from_querystring(self.querystring) filter_dict = filters_from_querystring(self.querystring)
offerings = self.ec2_backend.describe_instance_type_offerings( offerings = self.ec2_backend.describe_instance_type_offerings(
location_type_filters, filter_dict location_type_filters, filter_dict
@ -856,9 +856,9 @@ EC2_DESCRIBE_INSTANCE_TYPE_OFFERINGS = """<?xml version="1.0" encoding="UTF-8"?>
<instanceTypeOfferingSet> <instanceTypeOfferingSet>
{% for offering in instance_type_offerings %} {% for offering in instance_type_offerings %}
<item> <item>
<instanceType>{{ offering.instance_type }}</instanceType> <instanceType>{{ offering.InstanceType }}</instanceType>
<location>{{ offering.location }}</location> <location>{{ offering.Location }}</location>
<locationType>{{ offering.location_type }}</locationType> <locationType>{{ offering.LocationType }}</locationType>
</item> </item>
{% endfor %} {% endfor %}
</instanceTypeOfferingSet> </instanceTypeOfferingSet>

View File

@ -0,0 +1,63 @@
"""
Get InstanceTypeOfferings from AWS
Stores result in moto/ec2/resources/instance_type_offerings/{location_type}/{region}.json
Where {location_type} is one of region/availability-zone/availability-zone-id
Note that you will get the following error if a region is not available to you:
An error occurred (AuthFailure) when calling the DescribeInstanceTypeOfferings operation:
AWS was not able to validate the provided access credentials
"""
import boto3
import json
import os
import subprocess
from boto3 import Session
from time import sleep
PATH = "moto/ec2/resources/instance_type_offerings"
TYPES = ["region", "availability-zone", "availability-zone-id"]
def main():
print("Getting InstanceTypeOfferings from all regions")
regions = []
regions.extend(Session().get_available_regions("ec2"))
regions.extend(Session().get_available_regions("ec2", partition_name="aws-us-gov"))
regions.extend(Session().get_available_regions("ec2", partition_name="aws-cn"))
print("Found " + str(len(regions)) + " regions")
root_dir = (
subprocess.check_output(["git", "rev-parse", "--show-toplevel"])
.decode()
.strip()
)
for region in regions:
for location_type in TYPES:
ec2 = boto3.client("ec2", region_name=region)
dest = os.path.join(root_dir, "{0}/{1}/{2}.json".format(PATH, location_type, region))
try:
instances = []
offerings = ec2.describe_instance_type_offerings(
LocationType=location_type
)
instances.extend(offerings["InstanceTypeOfferings"])
next_token = offerings.get("NextToken", "")
while next_token:
offerings = ec2.describe_instance_type_offerings(
LocationType=location_type,
NextToken=next_token
)
instances.extend(offerings["InstanceTypeOfferings"])
next_token = offerings.get("NextToken", None)
print("Writing data to {0}".format(dest))
with open(dest, "w+") as open_file:
json.dump(instances, open_file, sort_keys=True)
except Exception as e:
print("Unable to write data to {0}".format(dest))
print(e)
# We don't want it to look like we're DDOS'ing AWS
sleep(1)
if __name__ == "__main__":
main()

View File

@ -21,16 +21,31 @@ def test_describe_instance_type_offerings():
@mock_ec2 @mock_ec2
def test_describe_instance_type_offering_filter_by_type(): def test_describe_instance_type_offering_filter_by_type():
client = boto3.client("ec2", "us-east-1") client = boto3.client("ec2", "us-east-1")
# Verify offerings of a specific instance type
offerings = client.describe_instance_type_offerings( offerings = client.describe_instance_type_offerings(
Filters=[{"Name": "instance-type", "Values": ["t2.nano"]}] Filters=[{"Name": "instance-type", "Values": ["t2.nano"]}]
) )
offerings.should.have.key("InstanceTypeOfferings") offerings.should.have.key("InstanceTypeOfferings")
offerings["InstanceTypeOfferings"].should_not.be.empty offerings["InstanceTypeOfferings"].should_not.be.empty
offerings["InstanceTypeOfferings"].should.have.length_of(2) offerings = offerings["InstanceTypeOfferings"]
offerings["InstanceTypeOfferings"][0]["InstanceType"].should.equal("t2.nano") offerings.should.have.length_of(1)
offerings["InstanceTypeOfferings"][-1]["InstanceType"].should.equal("t2.nano") offerings[0]["InstanceType"].should.equal("t2.nano")
offerings["InstanceTypeOfferings"][0]["Location"].should.equal("us-east-1a") offerings[0]["Location"].should.equal("us-east-1")
# Verify offerings of that instance type per availibility zone
offerings = client.describe_instance_type_offerings(
LocationType="availability-zone",
Filters=[{"Name": "instance-type", "Values": ["t2.nano"]}],
)
offerings.should.have.key("InstanceTypeOfferings")
offerings = offerings["InstanceTypeOfferings"]
offerings.should.have.length_of(6)
for offering in offerings:
offering["InstanceType"].should.equal("t2.nano")
offering["LocationType"].should.equal("availability-zone")
offering["Location"].should.match("us-east-1[a-f]")
@mock_ec2 @mock_ec2
@ -42,21 +57,29 @@ def test_describe_instance_type_offering_filter_by_zone():
) )
offerings.should.have.key("InstanceTypeOfferings") offerings.should.have.key("InstanceTypeOfferings")
offerings["InstanceTypeOfferings"].should_not.be.empty offerings = offerings["InstanceTypeOfferings"]
offerings["InstanceTypeOfferings"].should.have.length_of(1) offerings.should_not.be.empty
offerings["InstanceTypeOfferings"][0]["InstanceType"].should.equal("a1.2xlarge") offerings.should.have.length_of(353)
offerings["InstanceTypeOfferings"][0]["Location"].should.equal("us-east-1c") assert all([o["LocationType"] == "availability-zone" for o in offerings])
assert all([o["Location"] == "us-east-1c" for o in offerings])
assert any([o["InstanceType"] == "a1.2xlarge" for o in offerings])
@mock_ec2 @mock_ec2
def test_describe_instance_type_offering_filter_by_region(): def test_describe_instance_type_offering_filter_by_zone_id():
client = boto3.client("ec2", "us-east-1") client = boto3.client("ec2", "ca-central-1")
offerings = client.describe_instance_type_offerings( offerings = client.describe_instance_type_offerings(
LocationType="region", Filters=[{"Name": "location", "Values": ["us-west-1"]}] LocationType="availability-zone-id",
Filters=[
{"Name": "location", "Values": ["cac1-az1"]},
{"Name": "instance-type", "Values": ["c5.9xlarge"]},
],
) )
offerings.should.have.key("InstanceTypeOfferings") offerings.should.have.key("InstanceTypeOfferings")
offerings["InstanceTypeOfferings"].should_not.be.empty offerings = offerings["InstanceTypeOfferings"]
offerings["InstanceTypeOfferings"].should.have.length_of(2) offerings.should_not.be.empty
offerings["InstanceTypeOfferings"][0]["InstanceType"].should.equal("a1.4xlarge") offerings.should.have.length_of(1)
offerings["InstanceTypeOfferings"][0]["Location"].should.equal("us-west-1a") offerings[0]["LocationType"].should.equal("availability-zone-id")
offerings[0]["InstanceType"].should.equal("c5.9xlarge")
offerings[0]["Location"].should.equal("cac1-az1")