Add Tags field for describe_spot_fleet_requests response (#6332)

* add tag specifications for request parameters
* add unit test for invalid tag resource type
This commit is contained in:
Akira Noda 2023-05-23 05:53:57 +09:00 committed by GitHub
parent 334a259856
commit b1b269208c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 68 additions and 0 deletions

View File

@ -436,6 +436,14 @@ class InvalidParameterValueError(EC2ClientError):
)
class InvalidParameterValueErrorTagSpotFleetRequest(EC2ClientError):
def __init__(self, resource_type: str):
super().__init__(
"InvalidParameterValue",
f"The value for `ResourceType` must be `spot-fleet-request`, but got `{resource_type}` instead.",
)
class EmptyTagSpecError(EC2ClientError):
def __init__(self) -> None:
super().__init__(

View File

@ -2,6 +2,7 @@ from collections import defaultdict
from typing import Any, Dict, List, Optional, Tuple, TYPE_CHECKING
from moto.core.common_models import BaseModel, CloudFormationModel
from moto.ec2.exceptions import InvalidParameterValueErrorTagSpotFleetRequest
if TYPE_CHECKING:
from moto.ec2.models.instances import Instance
@ -185,6 +186,7 @@ class SpotFleetRequest(TaggedEC2Resource, CloudFormationModel):
launch_specs: List[Dict[str, Any]],
launch_template_config: Optional[List[Dict[str, Any]]],
instance_interruption_behaviour: Optional[str],
tag_specifications: Optional[List[Dict[str, Any]]],
):
self.ec2_backend = ec2_backend
@ -202,6 +204,14 @@ class SpotFleetRequest(TaggedEC2Resource, CloudFormationModel):
self.launch_specs = []
self.tags = {}
if tag_specifications is not None:
tags = convert_tag_spec(tag_specifications)
for resource_type in tags:
if resource_type != "spot-fleet-request":
raise InvalidParameterValueErrorTagSpotFleetRequest(resource_type)
self.tags.update(tags)
launch_specs_from_config = []
for config in launch_template_config or []:
spec = config["LaunchTemplateSpecification"]
@ -456,6 +466,7 @@ class SpotRequestBackend:
launch_specs: List[Dict[str, Any]],
launch_template_config: Optional[List[Dict[str, Any]]] = None,
instance_interruption_behaviour: Optional[str] = None,
tag_specifications: Optional[List[Dict[str, Any]]] = None,
) -> SpotFleetRequest:
spot_fleet_request_id = random_spot_fleet_request_id()
@ -470,6 +481,7 @@ class SpotRequestBackend:
launch_specs=launch_specs,
launch_template_config=launch_template_config,
instance_interruption_behaviour=instance_interruption_behaviour,
tag_specifications=tag_specifications,
)
self.spot_fleet_requests[spot_fleet_request_id] = request
return request

View File

@ -57,6 +57,7 @@ class SpotFleets(EC2BaseResponse):
.get("LaunchTemplateConfigs", {})
.values()
)
tag_specifications = spot_config.get("TagSpecification")
request = self.ec2_backend.request_spot_fleet(
spot_price=spot_price,
@ -66,6 +67,7 @@ class SpotFleets(EC2BaseResponse):
launch_specs=launch_specs,
launch_template_config=launch_template_config,
instance_interruption_behaviour=instance_interruption_behaviour,
tag_specifications=tag_specifications,
)
template = self.response_template(REQUEST_SPOT_FLEET_TEMPLATE)
@ -89,6 +91,14 @@ DESCRIBE_SPOT_FLEET_TEMPLATE = """<DescribeSpotFleetRequestsResponse xmlns="http
<item>
<spotFleetRequestId>{{ request.id }}</spotFleetRequestId>
<spotFleetRequestState>{{ request.state }}</spotFleetRequestState>
<tagSet>
{% for key, value in request.tags.get('spot-fleet-request', {}).items() %}
<item>
<key>{{ key }}</key>
<value>{{ value }}</value>
</item>
{% endfor %}
</tagSet>
<spotFleetRequestConfig>
{% if request.spot_price %}
<spotPrice>{{ request.spot_price }}</spotPrice>

View File

@ -4,6 +4,7 @@ import pytest
from moto import mock_ec2
from moto.core import DEFAULT_ACCOUNT_ID as ACCOUNT_ID
from botocore.exceptions import ClientError
from tests import EXAMPLE_AMI_ID
from uuid import uuid4
@ -64,13 +65,49 @@ def spot_config(subnet_id, allocation_strategy="lowestPrice"):
"EbsOptimized": False,
"WeightedCapacity": 4.0,
"SpotPrice": "10.00",
"TagSpecifications": [
{
"ResourceType": "instance",
"Tags": [{"Key": "test", "Value": "value"}],
}
],
},
],
"AllocationStrategy": allocation_strategy,
"FulfilledCapacity": 6,
"TagSpecifications": [
{
"ResourceType": "spot-fleet-request",
"Tags": [{"Key": "test2", "Value": "value2"}],
}
],
}
@mock_ec2
def test_create_spot_fleet_with_invalid_tag_specifications():
conn = boto3.client("ec2", region_name="us-west-2")
subnet_id = get_subnet_id(conn)
config = spot_config(subnet_id)
invalid_resource_type = "invalid-resource-type"
config["TagSpecifications"] = [
{
"ResourceType": invalid_resource_type,
"Tags": [{"Key": "test2", "Value": "value2"}],
}
]
with pytest.raises(ClientError) as ex:
_ = conn.request_spot_fleet(SpotFleetRequestConfig=config)
ex.value.response["Error"]["Code"].should.equal("InvalidParameterValue")
ex.value.response["ResponseMetadata"]["HTTPStatusCode"].should.equal(400)
ex.value.response["Error"]["Message"].should.equal(
f"The value for `ResourceType` must be `spot-fleet-request`, but got `{invalid_resource_type}` instead."
)
@mock_ec2
def test_create_spot_fleet_with_lowest_price():
conn = boto3.client("ec2", region_name="us-west-2")
@ -87,6 +124,7 @@ def test_create_spot_fleet_with_lowest_price():
len(spot_fleet_requests).should.equal(1)
spot_fleet_request = spot_fleet_requests[0]
spot_fleet_request["SpotFleetRequestState"].should.equal("active")
spot_fleet_request["Tags"].should.equal([{"Key": "test2", "Value": "value2"}])
spot_fleet_config = spot_fleet_request["SpotFleetRequestConfig"]
spot_fleet_config["SpotPrice"].should.equal("0.12")