Merge pull request #2369 from dkuntz2/implement-launch-templates

Add basic endpoints for EC2 Launch Templates
This commit is contained in:
Mike Grima 2019-08-21 12:54:42 -07:00 committed by GitHub
commit d5e7334e5b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 773 additions and 2 deletions

View File

@ -523,3 +523,11 @@ class OperationNotPermitted3(EC2ClientError):
pcx_id, pcx_id,
acceptor_region) acceptor_region)
) )
class InvalidLaunchTemplateNameError(EC2ClientError):
def __init__(self):
super(InvalidLaunchTemplateNameError, self).__init__(
"InvalidLaunchTemplateName.AlreadyExistsException",
"Launch template name already in use."
)

View File

@ -20,7 +20,6 @@ from boto.ec2.blockdevicemapping import BlockDeviceMapping, BlockDeviceType
from boto.ec2.spotinstancerequest import SpotInstanceRequest as BotoSpotRequest from boto.ec2.spotinstancerequest import SpotInstanceRequest as BotoSpotRequest
from boto.ec2.launchspecification import LaunchSpecification from boto.ec2.launchspecification import LaunchSpecification
from moto.compat import OrderedDict from moto.compat import OrderedDict
from moto.core import BaseBackend from moto.core import BaseBackend
from moto.core.models import Model, BaseModel from moto.core.models import Model, BaseModel
@ -49,6 +48,7 @@ from .exceptions import (
InvalidKeyPairDuplicateError, InvalidKeyPairDuplicateError,
InvalidKeyPairFormatError, InvalidKeyPairFormatError,
InvalidKeyPairNameError, InvalidKeyPairNameError,
InvalidLaunchTemplateNameError,
InvalidNetworkAclIdError, InvalidNetworkAclIdError,
InvalidNetworkAttachmentIdError, InvalidNetworkAttachmentIdError,
InvalidNetworkInterfaceIdError, InvalidNetworkInterfaceIdError,
@ -98,6 +98,7 @@ from .utils import (
random_internet_gateway_id, random_internet_gateway_id,
random_ip, random_ip,
random_ipv6_cidr, random_ipv6_cidr,
random_launch_template_id,
random_nat_gateway_id, random_nat_gateway_id,
random_key_pair, random_key_pair,
random_private_ip, random_private_ip,
@ -4113,6 +4114,92 @@ class NatGatewayBackend(object):
return self.nat_gateways.pop(nat_gateway_id) return self.nat_gateways.pop(nat_gateway_id)
class LaunchTemplateVersion(object):
def __init__(self, template, number, data, description):
self.template = template
self.number = number
self.data = data
self.description = description
self.create_time = utc_date_and_time()
class LaunchTemplate(TaggedEC2Resource):
def __init__(self, backend, name, template_data, version_description):
self.ec2_backend = backend
self.name = name
self.id = random_launch_template_id()
self.create_time = utc_date_and_time()
self.versions = []
self.create_version(template_data, version_description)
self.default_version_number = 1
def create_version(self, data, description):
num = len(self.versions) + 1
version = LaunchTemplateVersion(self, num, data, description)
self.versions.append(version)
return version
def is_default(self, version):
return self.default_version == version.number
def get_version(self, num):
return self.versions[num - 1]
def default_version(self):
return self.versions[self.default_version_number - 1]
def latest_version(self):
return self.versions[-1]
@property
def latest_version_number(self):
return self.latest_version().number
def get_filter_value(self, filter_name):
if filter_name == 'launch-template-name':
return self.name
else:
return super(LaunchTemplate, self).get_filter_value(
filter_name, "DescribeLaunchTemplates")
class LaunchTemplateBackend(object):
def __init__(self):
self.launch_template_name_to_ids = {}
self.launch_templates = OrderedDict()
self.launch_template_insert_order = []
super(LaunchTemplateBackend, self).__init__()
def create_launch_template(self, name, description, template_data):
if name in self.launch_template_name_to_ids:
raise InvalidLaunchTemplateNameError()
template = LaunchTemplate(self, name, template_data, description)
self.launch_templates[template.id] = template
self.launch_template_name_to_ids[template.name] = template.id
self.launch_template_insert_order.append(template.id)
return template
def get_launch_template(self, template_id):
return self.launch_templates[template_id]
def get_launch_template_by_name(self, name):
return self.get_launch_template(self.launch_template_name_to_ids[name])
def get_launch_templates(self, template_names=None, template_ids=None, filters=None):
if template_names and not template_ids:
template_ids = []
for name in template_names:
template_ids.append(self.launch_template_name_to_ids[name])
if template_ids:
templates = [self.launch_templates[tid] for tid in template_ids]
else:
templates = list(self.launch_templates.values())
return generic_filter(filters, templates)
class EC2Backend(BaseBackend, InstanceBackend, TagBackend, EBSBackend, class EC2Backend(BaseBackend, InstanceBackend, TagBackend, EBSBackend,
RegionsAndZonesBackend, SecurityGroupBackend, AmiBackend, RegionsAndZonesBackend, SecurityGroupBackend, AmiBackend,
VPCBackend, SubnetBackend, SubnetRouteTableAssociationBackend, VPCBackend, SubnetBackend, SubnetRouteTableAssociationBackend,
@ -4122,7 +4209,7 @@ class EC2Backend(BaseBackend, InstanceBackend, TagBackend, EBSBackend,
VPCGatewayAttachmentBackend, SpotFleetBackend, VPCGatewayAttachmentBackend, SpotFleetBackend,
SpotRequestBackend, ElasticAddressBackend, KeyPairBackend, SpotRequestBackend, ElasticAddressBackend, KeyPairBackend,
DHCPOptionsSetBackend, NetworkAclBackend, VpnGatewayBackend, DHCPOptionsSetBackend, NetworkAclBackend, VpnGatewayBackend,
CustomerGatewayBackend, NatGatewayBackend): CustomerGatewayBackend, NatGatewayBackend, LaunchTemplateBackend):
def __init__(self, region_name): def __init__(self, region_name):
self.region_name = region_name self.region_name = region_name
super(EC2Backend, self).__init__() super(EC2Backend, self).__init__()
@ -4177,6 +4264,8 @@ class EC2Backend(BaseBackend, InstanceBackend, TagBackend, EBSBackend,
elif resource_prefix == EC2_RESOURCE_TO_PREFIX['internet-gateway']: elif resource_prefix == EC2_RESOURCE_TO_PREFIX['internet-gateway']:
self.describe_internet_gateways( self.describe_internet_gateways(
internet_gateway_ids=[resource_id]) internet_gateway_ids=[resource_id])
elif resource_prefix == EC2_RESOURCE_TO_PREFIX['launch-template']:
self.get_launch_template(resource_id)
elif resource_prefix == EC2_RESOURCE_TO_PREFIX['network-acl']: elif resource_prefix == EC2_RESOURCE_TO_PREFIX['network-acl']:
self.get_all_network_acls() self.get_all_network_acls()
elif resource_prefix == EC2_RESOURCE_TO_PREFIX['network-interface']: elif resource_prefix == EC2_RESOURCE_TO_PREFIX['network-interface']:

View File

@ -14,6 +14,7 @@ from .instances import InstanceResponse
from .internet_gateways import InternetGateways from .internet_gateways import InternetGateways
from .ip_addresses import IPAddresses from .ip_addresses import IPAddresses
from .key_pairs import KeyPairs from .key_pairs import KeyPairs
from .launch_templates import LaunchTemplates
from .monitoring import Monitoring from .monitoring import Monitoring
from .network_acls import NetworkACLs from .network_acls import NetworkACLs
from .placement_groups import PlacementGroups from .placement_groups import PlacementGroups
@ -49,6 +50,7 @@ class EC2Response(
InternetGateways, InternetGateways,
IPAddresses, IPAddresses,
KeyPairs, KeyPairs,
LaunchTemplates,
Monitoring, Monitoring,
NetworkACLs, NetworkACLs,
PlacementGroups, PlacementGroups,

View File

@ -0,0 +1,252 @@
import six
import uuid
from moto.core.responses import BaseResponse
from moto.ec2.models import OWNER_ID
from moto.ec2.exceptions import FilterNotImplementedError
from moto.ec2.utils import filters_from_querystring
from xml.etree import ElementTree
from xml.dom import minidom
def xml_root(name):
root = ElementTree.Element(name, {
"xmlns": "http://ec2.amazonaws.com/doc/2016-11-15/"
})
request_id = str(uuid.uuid4()) + "example"
ElementTree.SubElement(root, "requestId").text = request_id
return root
def xml_serialize(tree, key, value):
name = key[0].lower() + key[1:]
if isinstance(value, list):
if name[-1] == 's':
name = name[:-1]
name = name + 'Set'
node = ElementTree.SubElement(tree, name)
if isinstance(value, (str, int, float, six.text_type)):
node.text = str(value)
elif isinstance(value, dict):
for dictkey, dictvalue in six.iteritems(value):
xml_serialize(node, dictkey, dictvalue)
elif isinstance(value, list):
for item in value:
xml_serialize(node, 'item', item)
elif value is None:
pass
else:
raise NotImplementedError("Don't know how to serialize \"{}\" to xml".format(value.__class__))
def pretty_xml(tree):
rough = ElementTree.tostring(tree, 'utf-8')
parsed = minidom.parseString(rough)
return parsed.toprettyxml(indent=' ')
def parse_object(raw_data):
out_data = {}
for key, value in six.iteritems(raw_data):
key_fix_splits = key.split("_")
key_len = len(key_fix_splits)
new_key = ""
for i in range(0, key_len):
new_key += key_fix_splits[i][0].upper() + key_fix_splits[i][1:]
data = out_data
splits = new_key.split(".")
for split in splits[:-1]:
if split not in data:
data[split] = {}
data = data[split]
data[splits[-1]] = value
out_data = parse_lists(out_data)
return out_data
def parse_lists(data):
for key, value in six.iteritems(data):
if isinstance(value, dict):
keys = data[key].keys()
is_list = all(map(lambda k: k.isnumeric(), keys))
if is_list:
new_value = []
keys = sorted(list(keys))
for k in keys:
lvalue = value[k]
if isinstance(lvalue, dict):
lvalue = parse_lists(lvalue)
new_value.append(lvalue)
data[key] = new_value
return data
class LaunchTemplates(BaseResponse):
def create_launch_template(self):
name = self._get_param('LaunchTemplateName')
version_description = self._get_param('VersionDescription')
tag_spec = self._parse_tag_specification("TagSpecification")
raw_template_data = self._get_dict_param('LaunchTemplateData.')
parsed_template_data = parse_object(raw_template_data)
if self.is_not_dryrun('CreateLaunchTemplate'):
if tag_spec:
if 'TagSpecifications' not in parsed_template_data:
parsed_template_data['TagSpecifications'] = []
converted_tag_spec = []
for resource_type, tags in six.iteritems(tag_spec):
converted_tag_spec.append({
"ResourceType": resource_type,
"Tags": [{"Key": key, "Value": value} for key, value in six.iteritems(tags)],
})
parsed_template_data['TagSpecifications'].extend(converted_tag_spec)
template = self.ec2_backend.create_launch_template(name, version_description, parsed_template_data)
version = template.default_version()
tree = xml_root("CreateLaunchTemplateResponse")
xml_serialize(tree, "launchTemplate", {
"createTime": version.create_time,
"createdBy": "arn:aws:iam::{OWNER_ID}:root".format(OWNER_ID=OWNER_ID),
"defaultVersionNumber": template.default_version_number,
"latestVersionNumber": version.number,
"launchTemplateId": template.id,
"launchTemplateName": template.name
})
return pretty_xml(tree)
def create_launch_template_version(self):
name = self._get_param('LaunchTemplateName')
tmpl_id = self._get_param('LaunchTemplateId')
if name:
template = self.ec2_backend.get_launch_template_by_name(name)
if tmpl_id:
template = self.ec2_backend.get_launch_template(tmpl_id)
version_description = self._get_param('VersionDescription')
raw_template_data = self._get_dict_param('LaunchTemplateData.')
template_data = parse_object(raw_template_data)
if self.is_not_dryrun('CreateLaunchTemplate'):
version = template.create_version(template_data, version_description)
tree = xml_root("CreateLaunchTemplateVersionResponse")
xml_serialize(tree, "launchTemplateVersion", {
"createTime": version.create_time,
"createdBy": "arn:aws:iam::{OWNER_ID}:root".format(OWNER_ID=OWNER_ID),
"defaultVersion": template.is_default(version),
"launchTemplateData": version.data,
"launchTemplateId": template.id,
"launchTemplateName": template.name,
"versionDescription": version.description,
"versionNumber": version.number,
})
return pretty_xml(tree)
# def delete_launch_template(self):
# pass
# def delete_launch_template_versions(self):
# pass
def describe_launch_template_versions(self):
name = self._get_param('LaunchTemplateName')
template_id = self._get_param('LaunchTemplateId')
if name:
template = self.ec2_backend.get_launch_template_by_name(name)
if template_id:
template = self.ec2_backend.get_launch_template(template_id)
max_results = self._get_int_param("MaxResults", 15)
versions = self._get_multi_param("LaunchTemplateVersion")
min_version = self._get_int_param("MinVersion")
max_version = self._get_int_param("MaxVersion")
filters = filters_from_querystring(self.querystring)
if filters:
raise FilterNotImplementedError("all filters", "DescribeLaunchTemplateVersions")
if self.is_not_dryrun('DescribeLaunchTemplateVersions'):
tree = ElementTree.Element("DescribeLaunchTemplateVersionsResponse", {
"xmlns": "http://ec2.amazonaws.com/doc/2016-11-15/",
})
request_id = ElementTree.SubElement(tree, "requestId")
request_id.text = "65cadec1-b364-4354-8ca8-4176dexample"
versions_node = ElementTree.SubElement(tree, "launchTemplateVersionSet")
ret_versions = []
if versions:
for v in versions:
ret_versions.append(template.get_version(int(v)))
elif min_version:
if max_version:
vMax = max_version
else:
vMax = min_version + max_results
vMin = min_version - 1
ret_versions = template.versions[vMin:vMax]
elif max_version:
vMax = max_version
ret_versions = template.versions[:vMax]
else:
ret_versions = template.versions
ret_versions = ret_versions[:max_results]
for version in ret_versions:
xml_serialize(versions_node, "item", {
"createTime": version.create_time,
"createdBy": "arn:aws:iam::{OWNER_ID}:root".format(OWNER_ID=OWNER_ID),
"defaultVersion": True,
"launchTemplateData": version.data,
"launchTemplateId": template.id,
"launchTemplateName": template.name,
"versionDescription": version.description,
"versionNumber": version.number,
})
return pretty_xml(tree)
def describe_launch_templates(self):
max_results = self._get_int_param("MaxResults", 15)
template_names = self._get_multi_param("LaunchTemplateName")
template_ids = self._get_multi_param("LaunchTemplateId")
filters = filters_from_querystring(self.querystring)
if self.is_not_dryrun("DescribeLaunchTemplates"):
tree = ElementTree.Element("DescribeLaunchTemplatesResponse")
templates_node = ElementTree.SubElement(tree, "launchTemplates")
templates = self.ec2_backend.get_launch_templates(template_names=template_names, template_ids=template_ids, filters=filters)
templates = templates[:max_results]
for template in templates:
xml_serialize(templates_node, "item", {
"createTime": template.create_time,
"createdBy": "arn:aws:iam::{OWNER_ID}:root".format(OWNER_ID=OWNER_ID),
"defaultVersionNumber": template.default_version_number,
"latestVersionNumber": template.latest_version_number,
"launchTemplateId": template.id,
"launchTemplateName": template.name,
})
return pretty_xml(tree)
# def modify_launch_template(self):
# pass

View File

@ -20,6 +20,7 @@ EC2_RESOURCE_TO_PREFIX = {
'image': 'ami', 'image': 'ami',
'instance': 'i', 'instance': 'i',
'internet-gateway': 'igw', 'internet-gateway': 'igw',
'launch-template': 'lt',
'nat-gateway': 'nat', 'nat-gateway': 'nat',
'network-acl': 'acl', 'network-acl': 'acl',
'network-acl-subnet-assoc': 'aclassoc', 'network-acl-subnet-assoc': 'aclassoc',
@ -161,6 +162,10 @@ def random_nat_gateway_id():
return random_id(prefix=EC2_RESOURCE_TO_PREFIX['nat-gateway'], size=17) return random_id(prefix=EC2_RESOURCE_TO_PREFIX['nat-gateway'], size=17)
def random_launch_template_id():
return random_id(prefix=EC2_RESOURCE_TO_PREFIX['launch-template'], size=17)
def random_public_ip(): def random_public_ip():
return '54.214.{0}.{1}'.format(random.choice(range(255)), return '54.214.{0}.{1}'.format(random.choice(range(255)),
random.choice(range(255))) random.choice(range(255)))

View File

@ -0,0 +1,415 @@
import boto3
import sure # noqa
from nose.tools import assert_raises
from botocore.client import ClientError
from moto import mock_ec2
@mock_ec2
def test_launch_template_create():
cli = boto3.client("ec2", region_name="us-east-1")
resp = cli.create_launch_template(
LaunchTemplateName="test-template",
# the absolute minimum needed to create a template without other resources
LaunchTemplateData={
"TagSpecifications": [{
"ResourceType": "instance",
"Tags": [{
"Key": "test",
"Value": "value",
}],
}],
},
)
resp.should.have.key("LaunchTemplate")
lt = resp["LaunchTemplate"]
lt["LaunchTemplateName"].should.equal("test-template")
lt["DefaultVersionNumber"].should.equal(1)
lt["LatestVersionNumber"].should.equal(1)
with assert_raises(ClientError) as ex:
cli.create_launch_template(
LaunchTemplateName="test-template",
LaunchTemplateData={
"TagSpecifications": [{
"ResourceType": "instance",
"Tags": [{
"Key": "test",
"Value": "value",
}],
}],
},
)
str(ex.exception).should.equal(
'An error occurred (InvalidLaunchTemplateName.AlreadyExistsException) when calling the CreateLaunchTemplate operation: Launch template name already in use.')
@mock_ec2
def test_describe_launch_template_versions():
template_data = {
"ImageId": "ami-abc123",
"DisableApiTermination": False,
"TagSpecifications": [{
"ResourceType": "instance",
"Tags": [{
"Key": "test",
"Value": "value",
}],
}],
"SecurityGroupIds": [
"sg-1234",
"sg-ab5678",
],
}
cli = boto3.client("ec2", region_name="us-east-1")
create_resp = cli.create_launch_template(
LaunchTemplateName="test-template",
LaunchTemplateData=template_data)
# test using name
resp = cli.describe_launch_template_versions(
LaunchTemplateName="test-template",
Versions=['1'])
templ = resp["LaunchTemplateVersions"][0]["LaunchTemplateData"]
templ.should.equal(template_data)
# test using id
resp = cli.describe_launch_template_versions(
LaunchTemplateId=create_resp["LaunchTemplate"]["LaunchTemplateId"],
Versions=['1'])
templ = resp["LaunchTemplateVersions"][0]["LaunchTemplateData"]
templ.should.equal(template_data)
@mock_ec2
def test_create_launch_template_version():
cli = boto3.client("ec2", region_name="us-east-1")
create_resp = cli.create_launch_template(
LaunchTemplateName="test-template",
LaunchTemplateData={
"ImageId": "ami-abc123"
})
version_resp = cli.create_launch_template_version(
LaunchTemplateName="test-template",
LaunchTemplateData={
"ImageId": "ami-def456"
},
VersionDescription="new ami")
version_resp.should.have.key("LaunchTemplateVersion")
version = version_resp["LaunchTemplateVersion"]
version["DefaultVersion"].should.equal(False)
version["LaunchTemplateId"].should.equal(create_resp["LaunchTemplate"]["LaunchTemplateId"])
version["VersionDescription"].should.equal("new ami")
version["VersionNumber"].should.equal(2)
@mock_ec2
def test_create_launch_template_version_by_id():
cli = boto3.client("ec2", region_name="us-east-1")
create_resp = cli.create_launch_template(
LaunchTemplateName="test-template",
LaunchTemplateData={
"ImageId": "ami-abc123"
})
version_resp = cli.create_launch_template_version(
LaunchTemplateId=create_resp["LaunchTemplate"]["LaunchTemplateId"],
LaunchTemplateData={
"ImageId": "ami-def456"
},
VersionDescription="new ami")
version_resp.should.have.key("LaunchTemplateVersion")
version = version_resp["LaunchTemplateVersion"]
version["DefaultVersion"].should.equal(False)
version["LaunchTemplateId"].should.equal(create_resp["LaunchTemplate"]["LaunchTemplateId"])
version["VersionDescription"].should.equal("new ami")
version["VersionNumber"].should.equal(2)
@mock_ec2
def test_describe_launch_template_versions_with_multiple_versions():
cli = boto3.client("ec2", region_name="us-east-1")
cli.create_launch_template(
LaunchTemplateName="test-template",
LaunchTemplateData={
"ImageId": "ami-abc123"
})
cli.create_launch_template_version(
LaunchTemplateName="test-template",
LaunchTemplateData={
"ImageId": "ami-def456"
},
VersionDescription="new ami")
resp = cli.describe_launch_template_versions(
LaunchTemplateName="test-template")
resp["LaunchTemplateVersions"].should.have.length_of(2)
resp["LaunchTemplateVersions"][0]["LaunchTemplateData"]["ImageId"].should.equal("ami-abc123")
resp["LaunchTemplateVersions"][1]["LaunchTemplateData"]["ImageId"].should.equal("ami-def456")
@mock_ec2
def test_describe_launch_template_versions_with_versions_option():
cli = boto3.client("ec2", region_name="us-east-1")
cli.create_launch_template(
LaunchTemplateName="test-template",
LaunchTemplateData={
"ImageId": "ami-abc123"
})
cli.create_launch_template_version(
LaunchTemplateName="test-template",
LaunchTemplateData={
"ImageId": "ami-def456"
},
VersionDescription="new ami")
cli.create_launch_template_version(
LaunchTemplateName="test-template",
LaunchTemplateData={
"ImageId": "ami-hij789"
},
VersionDescription="new ami, again")
resp = cli.describe_launch_template_versions(
LaunchTemplateName="test-template",
Versions=["2", "3"])
resp["LaunchTemplateVersions"].should.have.length_of(2)
resp["LaunchTemplateVersions"][0]["LaunchTemplateData"]["ImageId"].should.equal("ami-def456")
resp["LaunchTemplateVersions"][1]["LaunchTemplateData"]["ImageId"].should.equal("ami-hij789")
@mock_ec2
def test_describe_launch_template_versions_with_min():
cli = boto3.client("ec2", region_name="us-east-1")
cli.create_launch_template(
LaunchTemplateName="test-template",
LaunchTemplateData={
"ImageId": "ami-abc123"
})
cli.create_launch_template_version(
LaunchTemplateName="test-template",
LaunchTemplateData={
"ImageId": "ami-def456"
},
VersionDescription="new ami")
cli.create_launch_template_version(
LaunchTemplateName="test-template",
LaunchTemplateData={
"ImageId": "ami-hij789"
},
VersionDescription="new ami, again")
resp = cli.describe_launch_template_versions(
LaunchTemplateName="test-template",
MinVersion="2")
resp["LaunchTemplateVersions"].should.have.length_of(2)
resp["LaunchTemplateVersions"][0]["LaunchTemplateData"]["ImageId"].should.equal("ami-def456")
resp["LaunchTemplateVersions"][1]["LaunchTemplateData"]["ImageId"].should.equal("ami-hij789")
@mock_ec2
def test_describe_launch_template_versions_with_max():
cli = boto3.client("ec2", region_name="us-east-1")
cli.create_launch_template(
LaunchTemplateName="test-template",
LaunchTemplateData={
"ImageId": "ami-abc123"
})
cli.create_launch_template_version(
LaunchTemplateName="test-template",
LaunchTemplateData={
"ImageId": "ami-def456"
},
VersionDescription="new ami")
cli.create_launch_template_version(
LaunchTemplateName="test-template",
LaunchTemplateData={
"ImageId": "ami-hij789"
},
VersionDescription="new ami, again")
resp = cli.describe_launch_template_versions(
LaunchTemplateName="test-template",
MaxVersion="2")
resp["LaunchTemplateVersions"].should.have.length_of(2)
resp["LaunchTemplateVersions"][0]["LaunchTemplateData"]["ImageId"].should.equal("ami-abc123")
resp["LaunchTemplateVersions"][1]["LaunchTemplateData"]["ImageId"].should.equal("ami-def456")
@mock_ec2
def test_describe_launch_template_versions_with_min_and_max():
cli = boto3.client("ec2", region_name="us-east-1")
cli.create_launch_template(
LaunchTemplateName="test-template",
LaunchTemplateData={
"ImageId": "ami-abc123"
})
cli.create_launch_template_version(
LaunchTemplateName="test-template",
LaunchTemplateData={
"ImageId": "ami-def456"
},
VersionDescription="new ami")
cli.create_launch_template_version(
LaunchTemplateName="test-template",
LaunchTemplateData={
"ImageId": "ami-hij789"
},
VersionDescription="new ami, again")
cli.create_launch_template_version(
LaunchTemplateName="test-template",
LaunchTemplateData={
"ImageId": "ami-345abc"
},
VersionDescription="new ami, because why not")
resp = cli.describe_launch_template_versions(
LaunchTemplateName="test-template",
MinVersion="2",
MaxVersion="3")
resp["LaunchTemplateVersions"].should.have.length_of(2)
resp["LaunchTemplateVersions"][0]["LaunchTemplateData"]["ImageId"].should.equal("ami-def456")
resp["LaunchTemplateVersions"][1]["LaunchTemplateData"]["ImageId"].should.equal("ami-hij789")
@mock_ec2
def test_describe_launch_templates():
cli = boto3.client("ec2", region_name="us-east-1")
lt_ids = []
r = cli.create_launch_template(
LaunchTemplateName="test-template",
LaunchTemplateData={
"ImageId": "ami-abc123"
})
lt_ids.append(r["LaunchTemplate"]["LaunchTemplateId"])
r = cli.create_launch_template(
LaunchTemplateName="test-template2",
LaunchTemplateData={
"ImageId": "ami-abc123"
})
lt_ids.append(r["LaunchTemplate"]["LaunchTemplateId"])
# general call, all templates
resp = cli.describe_launch_templates()
resp.should.have.key("LaunchTemplates")
resp["LaunchTemplates"].should.have.length_of(2)
resp["LaunchTemplates"][0]["LaunchTemplateName"].should.equal("test-template")
resp["LaunchTemplates"][1]["LaunchTemplateName"].should.equal("test-template2")
# filter by names
resp = cli.describe_launch_templates(
LaunchTemplateNames=["test-template2", "test-template"])
resp.should.have.key("LaunchTemplates")
resp["LaunchTemplates"].should.have.length_of(2)
resp["LaunchTemplates"][0]["LaunchTemplateName"].should.equal("test-template2")
resp["LaunchTemplates"][1]["LaunchTemplateName"].should.equal("test-template")
# filter by ids
resp = cli.describe_launch_templates(LaunchTemplateIds=lt_ids)
resp.should.have.key("LaunchTemplates")
resp["LaunchTemplates"].should.have.length_of(2)
resp["LaunchTemplates"][0]["LaunchTemplateName"].should.equal("test-template")
resp["LaunchTemplates"][1]["LaunchTemplateName"].should.equal("test-template2")
@mock_ec2
def test_describe_launch_templates_with_filters():
cli = boto3.client("ec2", region_name="us-east-1")
r = cli.create_launch_template(
LaunchTemplateName="test-template",
LaunchTemplateData={
"ImageId": "ami-abc123"
})
cli.create_tags(
Resources=[r["LaunchTemplate"]["LaunchTemplateId"]],
Tags=[
{"Key": "tag1", "Value": "a value"},
{"Key": "another-key", "Value": "this value"},
])
cli.create_launch_template(
LaunchTemplateName="no-tags",
LaunchTemplateData={
"ImageId": "ami-abc123"
})
resp = cli.describe_launch_templates(Filters=[{
"Name": "tag:tag1", "Values": ["a value"]
}])
resp["LaunchTemplates"].should.have.length_of(1)
resp["LaunchTemplates"][0]["LaunchTemplateName"].should.equal("test-template")
resp = cli.describe_launch_templates(Filters=[{
"Name": "launch-template-name", "Values": ["no-tags"]
}])
resp["LaunchTemplates"].should.have.length_of(1)
resp["LaunchTemplates"][0]["LaunchTemplateName"].should.equal("no-tags")
@mock_ec2
def test_create_launch_template_with_tag_spec():
cli = boto3.client("ec2", region_name="us-east-1")
cli.create_launch_template(
LaunchTemplateName="test-template",
LaunchTemplateData={"ImageId": "ami-abc123"},
TagSpecifications=[{
"ResourceType": "instance",
"Tags": [
{"Key": "key", "Value": "value"}
]
}],
)
resp = cli.describe_launch_template_versions(
LaunchTemplateName="test-template",
Versions=["1"])
version = resp["LaunchTemplateVersions"][0]
version["LaunchTemplateData"].should.have.key("TagSpecifications")
version["LaunchTemplateData"]["TagSpecifications"].should.have.length_of(1)
version["LaunchTemplateData"]["TagSpecifications"][0].should.equal({
"ResourceType": "instance",
"Tags": [
{"Key": "key", "Value": "value"}
]
})