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:
parent
334a259856
commit
b1b269208c
@ -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):
|
class EmptyTagSpecError(EC2ClientError):
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
super().__init__(
|
super().__init__(
|
||||||
|
@ -2,6 +2,7 @@ from collections import defaultdict
|
|||||||
from typing import Any, Dict, List, Optional, Tuple, TYPE_CHECKING
|
from typing import Any, Dict, List, Optional, Tuple, TYPE_CHECKING
|
||||||
|
|
||||||
from moto.core.common_models import BaseModel, CloudFormationModel
|
from moto.core.common_models import BaseModel, CloudFormationModel
|
||||||
|
from moto.ec2.exceptions import InvalidParameterValueErrorTagSpotFleetRequest
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from moto.ec2.models.instances import Instance
|
from moto.ec2.models.instances import Instance
|
||||||
@ -185,6 +186,7 @@ class SpotFleetRequest(TaggedEC2Resource, CloudFormationModel):
|
|||||||
launch_specs: List[Dict[str, Any]],
|
launch_specs: List[Dict[str, Any]],
|
||||||
launch_template_config: Optional[List[Dict[str, Any]]],
|
launch_template_config: Optional[List[Dict[str, Any]]],
|
||||||
instance_interruption_behaviour: Optional[str],
|
instance_interruption_behaviour: Optional[str],
|
||||||
|
tag_specifications: Optional[List[Dict[str, Any]]],
|
||||||
):
|
):
|
||||||
|
|
||||||
self.ec2_backend = ec2_backend
|
self.ec2_backend = ec2_backend
|
||||||
@ -202,6 +204,14 @@ class SpotFleetRequest(TaggedEC2Resource, CloudFormationModel):
|
|||||||
|
|
||||||
self.launch_specs = []
|
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 = []
|
launch_specs_from_config = []
|
||||||
for config in launch_template_config or []:
|
for config in launch_template_config or []:
|
||||||
spec = config["LaunchTemplateSpecification"]
|
spec = config["LaunchTemplateSpecification"]
|
||||||
@ -456,6 +466,7 @@ class SpotRequestBackend:
|
|||||||
launch_specs: List[Dict[str, Any]],
|
launch_specs: List[Dict[str, Any]],
|
||||||
launch_template_config: Optional[List[Dict[str, Any]]] = None,
|
launch_template_config: Optional[List[Dict[str, Any]]] = None,
|
||||||
instance_interruption_behaviour: Optional[str] = None,
|
instance_interruption_behaviour: Optional[str] = None,
|
||||||
|
tag_specifications: Optional[List[Dict[str, Any]]] = None,
|
||||||
) -> SpotFleetRequest:
|
) -> SpotFleetRequest:
|
||||||
|
|
||||||
spot_fleet_request_id = random_spot_fleet_request_id()
|
spot_fleet_request_id = random_spot_fleet_request_id()
|
||||||
@ -470,6 +481,7 @@ class SpotRequestBackend:
|
|||||||
launch_specs=launch_specs,
|
launch_specs=launch_specs,
|
||||||
launch_template_config=launch_template_config,
|
launch_template_config=launch_template_config,
|
||||||
instance_interruption_behaviour=instance_interruption_behaviour,
|
instance_interruption_behaviour=instance_interruption_behaviour,
|
||||||
|
tag_specifications=tag_specifications,
|
||||||
)
|
)
|
||||||
self.spot_fleet_requests[spot_fleet_request_id] = request
|
self.spot_fleet_requests[spot_fleet_request_id] = request
|
||||||
return request
|
return request
|
||||||
|
@ -57,6 +57,7 @@ class SpotFleets(EC2BaseResponse):
|
|||||||
.get("LaunchTemplateConfigs", {})
|
.get("LaunchTemplateConfigs", {})
|
||||||
.values()
|
.values()
|
||||||
)
|
)
|
||||||
|
tag_specifications = spot_config.get("TagSpecification")
|
||||||
|
|
||||||
request = self.ec2_backend.request_spot_fleet(
|
request = self.ec2_backend.request_spot_fleet(
|
||||||
spot_price=spot_price,
|
spot_price=spot_price,
|
||||||
@ -66,6 +67,7 @@ class SpotFleets(EC2BaseResponse):
|
|||||||
launch_specs=launch_specs,
|
launch_specs=launch_specs,
|
||||||
launch_template_config=launch_template_config,
|
launch_template_config=launch_template_config,
|
||||||
instance_interruption_behaviour=instance_interruption_behaviour,
|
instance_interruption_behaviour=instance_interruption_behaviour,
|
||||||
|
tag_specifications=tag_specifications,
|
||||||
)
|
)
|
||||||
|
|
||||||
template = self.response_template(REQUEST_SPOT_FLEET_TEMPLATE)
|
template = self.response_template(REQUEST_SPOT_FLEET_TEMPLATE)
|
||||||
@ -89,6 +91,14 @@ DESCRIBE_SPOT_FLEET_TEMPLATE = """<DescribeSpotFleetRequestsResponse xmlns="http
|
|||||||
<item>
|
<item>
|
||||||
<spotFleetRequestId>{{ request.id }}</spotFleetRequestId>
|
<spotFleetRequestId>{{ request.id }}</spotFleetRequestId>
|
||||||
<spotFleetRequestState>{{ request.state }}</spotFleetRequestState>
|
<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>
|
<spotFleetRequestConfig>
|
||||||
{% if request.spot_price %}
|
{% if request.spot_price %}
|
||||||
<spotPrice>{{ request.spot_price }}</spotPrice>
|
<spotPrice>{{ request.spot_price }}</spotPrice>
|
||||||
|
@ -4,6 +4,7 @@ import pytest
|
|||||||
|
|
||||||
from moto import mock_ec2
|
from moto import mock_ec2
|
||||||
from moto.core import DEFAULT_ACCOUNT_ID as ACCOUNT_ID
|
from moto.core import DEFAULT_ACCOUNT_ID as ACCOUNT_ID
|
||||||
|
from botocore.exceptions import ClientError
|
||||||
from tests import EXAMPLE_AMI_ID
|
from tests import EXAMPLE_AMI_ID
|
||||||
from uuid import uuid4
|
from uuid import uuid4
|
||||||
|
|
||||||
@ -64,13 +65,49 @@ def spot_config(subnet_id, allocation_strategy="lowestPrice"):
|
|||||||
"EbsOptimized": False,
|
"EbsOptimized": False,
|
||||||
"WeightedCapacity": 4.0,
|
"WeightedCapacity": 4.0,
|
||||||
"SpotPrice": "10.00",
|
"SpotPrice": "10.00",
|
||||||
|
"TagSpecifications": [
|
||||||
|
{
|
||||||
|
"ResourceType": "instance",
|
||||||
|
"Tags": [{"Key": "test", "Value": "value"}],
|
||||||
|
}
|
||||||
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
"AllocationStrategy": allocation_strategy,
|
"AllocationStrategy": allocation_strategy,
|
||||||
"FulfilledCapacity": 6,
|
"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
|
@mock_ec2
|
||||||
def test_create_spot_fleet_with_lowest_price():
|
def test_create_spot_fleet_with_lowest_price():
|
||||||
conn = boto3.client("ec2", region_name="us-west-2")
|
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)
|
len(spot_fleet_requests).should.equal(1)
|
||||||
spot_fleet_request = spot_fleet_requests[0]
|
spot_fleet_request = spot_fleet_requests[0]
|
||||||
spot_fleet_request["SpotFleetRequestState"].should.equal("active")
|
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 = spot_fleet_request["SpotFleetRequestConfig"]
|
||||||
|
|
||||||
spot_fleet_config["SpotPrice"].should.equal("0.12")
|
spot_fleet_config["SpotPrice"].should.equal("0.12")
|
||||||
|
Loading…
Reference in New Issue
Block a user