Add TagSpecification support to ec2:CreateImage (#3604)
* Bump `botocore` to minimum version that supports TagSpecifications for this action. * Add test coverage. Closes #3602
This commit is contained in:
parent
2bdba88389
commit
cb03223c9b
@ -615,3 +615,13 @@ class InvalidVpcEndPointIdError(EC2ClientError):
|
|||||||
"InvalidVpcEndPointId.NotFound",
|
"InvalidVpcEndPointId.NotFound",
|
||||||
"The VpcEndPoint ID '{0}' does not exist".format(vpc_end_point_id),
|
"The VpcEndPoint ID '{0}' does not exist".format(vpc_end_point_id),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class InvalidTaggableResourceType(EC2ClientError):
|
||||||
|
def __init__(self, resource_type):
|
||||||
|
super(InvalidTaggableResourceType, self).__init__(
|
||||||
|
"InvalidParameterValue",
|
||||||
|
"'{}' is not a valid taggable resource type for this operation.".format(
|
||||||
|
resource_type
|
||||||
|
),
|
||||||
|
)
|
||||||
|
@ -109,6 +109,7 @@ from .exceptions import (
|
|||||||
IncorrectStateIamProfileAssociationError,
|
IncorrectStateIamProfileAssociationError,
|
||||||
InvalidAssociationIDIamProfileAssociationError,
|
InvalidAssociationIDIamProfileAssociationError,
|
||||||
InvalidVpcEndPointIdError,
|
InvalidVpcEndPointIdError,
|
||||||
|
InvalidTaggableResourceType,
|
||||||
)
|
)
|
||||||
from .utils import (
|
from .utils import (
|
||||||
EC2_RESOURCE_TO_PREFIX,
|
EC2_RESOURCE_TO_PREFIX,
|
||||||
@ -1522,10 +1523,26 @@ class AmiBackend(object):
|
|||||||
ami_id = ami["ami_id"]
|
ami_id = ami["ami_id"]
|
||||||
self.amis[ami_id] = Ami(self, **ami)
|
self.amis[ami_id] = Ami(self, **ami)
|
||||||
|
|
||||||
def create_image(self, instance_id, name=None, description=None, context=None):
|
def create_image(
|
||||||
|
self,
|
||||||
|
instance_id,
|
||||||
|
name=None,
|
||||||
|
description=None,
|
||||||
|
context=None,
|
||||||
|
tag_specifications=None,
|
||||||
|
):
|
||||||
# TODO: check that instance exists and pull info from it.
|
# TODO: check that instance exists and pull info from it.
|
||||||
ami_id = random_ami_id()
|
ami_id = random_ami_id()
|
||||||
instance = self.get_instance(instance_id)
|
instance = self.get_instance(instance_id)
|
||||||
|
tags = []
|
||||||
|
for tag_specification in tag_specifications:
|
||||||
|
resource_type = tag_specification["ResourceType"]
|
||||||
|
if resource_type == "image":
|
||||||
|
tags += tag_specification["Tag"]
|
||||||
|
elif resource_type == "snapshot":
|
||||||
|
raise NotImplementedError()
|
||||||
|
else:
|
||||||
|
raise InvalidTaggableResourceType(resource_type)
|
||||||
|
|
||||||
ami = Ami(
|
ami = Ami(
|
||||||
self,
|
self,
|
||||||
@ -1536,6 +1553,8 @@ class AmiBackend(object):
|
|||||||
description=description,
|
description=description,
|
||||||
owner_id=OWNER_ID,
|
owner_id=OWNER_ID,
|
||||||
)
|
)
|
||||||
|
for tag in tags:
|
||||||
|
ami.add_tag(tag["Key"], tag["Value"])
|
||||||
self.amis[ami_id] = ami
|
self.amis[ami_id] = ami
|
||||||
return ami
|
return ami
|
||||||
|
|
||||||
|
@ -8,9 +8,14 @@ class AmisResponse(BaseResponse):
|
|||||||
name = self.querystring.get("Name")[0]
|
name = self.querystring.get("Name")[0]
|
||||||
description = self._get_param("Description", if_none="")
|
description = self._get_param("Description", if_none="")
|
||||||
instance_id = self._get_param("InstanceId")
|
instance_id = self._get_param("InstanceId")
|
||||||
|
tag_specifications = self._get_multi_param("TagSpecification")
|
||||||
if self.is_not_dryrun("CreateImage"):
|
if self.is_not_dryrun("CreateImage"):
|
||||||
image = self.ec2_backend.create_image(
|
image = self.ec2_backend.create_image(
|
||||||
instance_id, name, description, context=self
|
instance_id,
|
||||||
|
name,
|
||||||
|
description,
|
||||||
|
context=self,
|
||||||
|
tag_specifications=tag_specifications,
|
||||||
)
|
)
|
||||||
template = self.response_template(CREATE_IMAGE_RESPONSE)
|
template = self.response_template(CREATE_IMAGE_RESPONSE)
|
||||||
return template.render(image=image)
|
return template.render(image=image)
|
||||||
|
@ -9,7 +9,7 @@ flask
|
|||||||
flask-cors
|
flask-cors
|
||||||
boto>=2.45.0
|
boto>=2.45.0
|
||||||
boto3>=1.4.4
|
boto3>=1.4.4
|
||||||
botocore>=1.18.17
|
botocore>=1.19.30
|
||||||
six>=1.9
|
six>=1.9
|
||||||
prompt-toolkit==2.0.10 # 3.x is not available with python2
|
prompt-toolkit==2.0.10 # 3.x is not available with python2
|
||||||
click==6.7
|
click==6.7
|
||||||
|
@ -876,3 +876,48 @@ def test_ami_snapshots_have_correct_owner():
|
|||||||
|
|
||||||
for snapshot in snapshots_rseponse["Snapshots"]:
|
for snapshot in snapshots_rseponse["Snapshots"]:
|
||||||
assert owner_id == snapshot["OwnerId"]
|
assert owner_id == snapshot["OwnerId"]
|
||||||
|
|
||||||
|
|
||||||
|
@mock_ec2
|
||||||
|
def test_create_image_with_tag_specification():
|
||||||
|
ec2 = boto3.resource("ec2", region_name="us-west-1")
|
||||||
|
client = boto3.client("ec2", region_name="us-west-1")
|
||||||
|
tag_specifications = [
|
||||||
|
{
|
||||||
|
"ResourceType": "image",
|
||||||
|
"Tags": [
|
||||||
|
{
|
||||||
|
"Key": "Base_AMI_Name",
|
||||||
|
"Value": "Deep Learning Base AMI (Amazon Linux 2) Version 31.0",
|
||||||
|
},
|
||||||
|
{"Key": "OS_Version", "Value": "AWS Linux 2",},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
]
|
||||||
|
instance = ec2.create_instances(ImageId=EXAMPLE_AMI_ID, MinCount=1, MaxCount=1)[0]
|
||||||
|
image_id = client.create_image(
|
||||||
|
InstanceId=instance.instance_id,
|
||||||
|
Name="test-image",
|
||||||
|
Description="test ami",
|
||||||
|
TagSpecifications=tag_specifications,
|
||||||
|
)["ImageId"]
|
||||||
|
|
||||||
|
image = client.describe_images(ImageIds=[image_id])["Images"][0]
|
||||||
|
image["Tags"].should.equal(tag_specifications[0]["Tags"])
|
||||||
|
|
||||||
|
with pytest.raises(ClientError) as ex:
|
||||||
|
client.create_image(
|
||||||
|
InstanceId=instance.instance_id,
|
||||||
|
Name="test-image",
|
||||||
|
Description="test ami",
|
||||||
|
TagSpecifications=[
|
||||||
|
{
|
||||||
|
"ResourceType": "invalid-resource-type",
|
||||||
|
"Tags": [{"Key": "key", "Value": "value"}],
|
||||||
|
}
|
||||||
|
],
|
||||||
|
)
|
||||||
|
ex.value.response["Error"]["Code"].should.equal("InvalidParameterValue")
|
||||||
|
ex.value.response["Error"]["Message"].should.equal(
|
||||||
|
"'invalid-resource-type' is not a valid taggable resource type for this operation."
|
||||||
|
)
|
||||||
|
Loading…
Reference in New Issue
Block a user