Merge pull request #1463 from spulec/feature/add-default-spot-price-to-fleet-request
Make SpotPrice optional when requesting a spot fleet
This commit is contained in:
commit
553d074241
@ -2943,7 +2943,7 @@ class SpotFleetRequest(TaggedEC2Resource):
|
|||||||
'Properties']['SpotFleetRequestConfigData']
|
'Properties']['SpotFleetRequestConfigData']
|
||||||
ec2_backend = ec2_backends[region_name]
|
ec2_backend = ec2_backends[region_name]
|
||||||
|
|
||||||
spot_price = properties['SpotPrice']
|
spot_price = properties.get('SpotPrice')
|
||||||
target_capacity = properties['TargetCapacity']
|
target_capacity = properties['TargetCapacity']
|
||||||
iam_fleet_role = properties['IamFleetRole']
|
iam_fleet_role = properties['IamFleetRole']
|
||||||
allocation_strategy = properties['AllocationStrategy']
|
allocation_strategy = properties['AllocationStrategy']
|
||||||
@ -2977,7 +2977,8 @@ class SpotFleetRequest(TaggedEC2Resource):
|
|||||||
launch_spec_index += 1
|
launch_spec_index += 1
|
||||||
else: # lowestPrice
|
else: # lowestPrice
|
||||||
cheapest_spec = sorted(
|
cheapest_spec = sorted(
|
||||||
self.launch_specs, key=lambda spec: float(spec.spot_price))[0]
|
# FIXME: change `+inf` to the on demand price scaled to weighted capacity when it's not present
|
||||||
|
self.launch_specs, key=lambda spec: float(spec.spot_price or '+inf'))[0]
|
||||||
weight_so_far = weight_to_add + (weight_to_add % cheapest_spec.weighted_capacity)
|
weight_so_far = weight_to_add + (weight_to_add % cheapest_spec.weighted_capacity)
|
||||||
weight_map[cheapest_spec] = int(
|
weight_map[cheapest_spec] = int(
|
||||||
weight_so_far // cheapest_spec.weighted_capacity)
|
weight_so_far // cheapest_spec.weighted_capacity)
|
||||||
|
@ -40,7 +40,7 @@ class SpotFleets(BaseResponse):
|
|||||||
|
|
||||||
def request_spot_fleet(self):
|
def request_spot_fleet(self):
|
||||||
spot_config = self._get_dict_param("SpotFleetRequestConfig.")
|
spot_config = self._get_dict_param("SpotFleetRequestConfig.")
|
||||||
spot_price = spot_config['spot_price']
|
spot_price = spot_config.get('spot_price')
|
||||||
target_capacity = spot_config['target_capacity']
|
target_capacity = spot_config['target_capacity']
|
||||||
iam_fleet_role = spot_config['iam_fleet_role']
|
iam_fleet_role = spot_config['iam_fleet_role']
|
||||||
allocation_strategy = spot_config['allocation_strategy']
|
allocation_strategy = spot_config['allocation_strategy']
|
||||||
@ -78,7 +78,9 @@ DESCRIBE_SPOT_FLEET_TEMPLATE = """<DescribeSpotFleetRequestsResponse xmlns="http
|
|||||||
<spotFleetRequestId>{{ request.id }}</spotFleetRequestId>
|
<spotFleetRequestId>{{ request.id }}</spotFleetRequestId>
|
||||||
<spotFleetRequestState>{{ request.state }}</spotFleetRequestState>
|
<spotFleetRequestState>{{ request.state }}</spotFleetRequestState>
|
||||||
<spotFleetRequestConfig>
|
<spotFleetRequestConfig>
|
||||||
|
{% if request.spot_price %}
|
||||||
<spotPrice>{{ request.spot_price }}</spotPrice>
|
<spotPrice>{{ request.spot_price }}</spotPrice>
|
||||||
|
{% endif %}
|
||||||
<targetCapacity>{{ request.target_capacity }}</targetCapacity>
|
<targetCapacity>{{ request.target_capacity }}</targetCapacity>
|
||||||
<iamFleetRole>{{ request.iam_fleet_role }}</iamFleetRole>
|
<iamFleetRole>{{ request.iam_fleet_role }}</iamFleetRole>
|
||||||
<allocationStrategy>{{ request.allocation_strategy }}</allocationStrategy>
|
<allocationStrategy>{{ request.allocation_strategy }}</allocationStrategy>
|
||||||
@ -93,7 +95,9 @@ DESCRIBE_SPOT_FLEET_TEMPLATE = """<DescribeSpotFleetRequestsResponse xmlns="http
|
|||||||
<iamInstanceProfile><arn>{{ launch_spec.iam_instance_profile }}</arn></iamInstanceProfile>
|
<iamInstanceProfile><arn>{{ launch_spec.iam_instance_profile }}</arn></iamInstanceProfile>
|
||||||
<keyName>{{ launch_spec.key_name }}</keyName>
|
<keyName>{{ launch_spec.key_name }}</keyName>
|
||||||
<monitoring><enabled>{{ launch_spec.monitoring }}</enabled></monitoring>
|
<monitoring><enabled>{{ launch_spec.monitoring }}</enabled></monitoring>
|
||||||
|
{% if launch_spec.spot_price %}
|
||||||
<spotPrice>{{ launch_spec.spot_price }}</spotPrice>
|
<spotPrice>{{ launch_spec.spot_price }}</spotPrice>
|
||||||
|
{% endif %}
|
||||||
<userData>{{ launch_spec.user_data }}</userData>
|
<userData>{{ launch_spec.user_data }}</userData>
|
||||||
<weightedCapacity>{{ launch_spec.weighted_capacity }}</weightedCapacity>
|
<weightedCapacity>{{ launch_spec.weighted_capacity }}</weightedCapacity>
|
||||||
<groupSet>
|
<groupSet>
|
||||||
|
@ -8,7 +8,7 @@ freezegun
|
|||||||
flask
|
flask
|
||||||
boto>=2.45.0
|
boto>=2.45.0
|
||||||
boto3>=1.4.4
|
boto3>=1.4.4
|
||||||
botocore>=1.5.77
|
botocore>=1.8.36
|
||||||
six>=1.9
|
six>=1.9
|
||||||
prompt-toolkit==1.0.14
|
prompt-toolkit==1.0.14
|
||||||
click==6.7
|
click==6.7
|
||||||
|
@ -2156,6 +2156,78 @@ def test_stack_spot_fleet():
|
|||||||
launch_spec['WeightedCapacity'].should.equal(2.0)
|
launch_spec['WeightedCapacity'].should.equal(2.0)
|
||||||
|
|
||||||
|
|
||||||
|
@mock_cloudformation()
|
||||||
|
@mock_ec2()
|
||||||
|
def test_stack_spot_fleet_should_figure_out_default_price():
|
||||||
|
conn = boto3.client('ec2', 'us-east-1')
|
||||||
|
|
||||||
|
vpc = conn.create_vpc(CidrBlock="10.0.0.0/8")['Vpc']
|
||||||
|
subnet = conn.create_subnet(
|
||||||
|
VpcId=vpc['VpcId'], CidrBlock='10.0.0.0/16', AvailabilityZone='us-east-1a')['Subnet']
|
||||||
|
subnet_id = subnet['SubnetId']
|
||||||
|
|
||||||
|
spot_fleet_template = {
|
||||||
|
'Resources': {
|
||||||
|
"SpotFleet1": {
|
||||||
|
"Type": "AWS::EC2::SpotFleet",
|
||||||
|
"Properties": {
|
||||||
|
"SpotFleetRequestConfigData": {
|
||||||
|
"IamFleetRole": "arn:aws:iam::123456789012:role/fleet",
|
||||||
|
"TargetCapacity": 6,
|
||||||
|
"AllocationStrategy": "diversified",
|
||||||
|
"LaunchSpecifications": [
|
||||||
|
{
|
||||||
|
"EbsOptimized": "false",
|
||||||
|
"InstanceType": 't2.small',
|
||||||
|
"ImageId": "ami-1234",
|
||||||
|
"SubnetId": subnet_id,
|
||||||
|
"WeightedCapacity": "2",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"EbsOptimized": "true",
|
||||||
|
"InstanceType": 't2.large',
|
||||||
|
"ImageId": "ami-1234",
|
||||||
|
"Monitoring": {"Enabled": "true"},
|
||||||
|
"SecurityGroups": [{"GroupId": "sg-123"}],
|
||||||
|
"SubnetId": subnet_id,
|
||||||
|
"IamInstanceProfile": {"Arn": "arn:aws:iam::123456789012:role/fleet"},
|
||||||
|
"WeightedCapacity": "4",
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
spot_fleet_template_json = json.dumps(spot_fleet_template)
|
||||||
|
|
||||||
|
cf_conn = boto3.client('cloudformation', 'us-east-1')
|
||||||
|
stack_id = cf_conn.create_stack(
|
||||||
|
StackName='test_stack',
|
||||||
|
TemplateBody=spot_fleet_template_json,
|
||||||
|
)['StackId']
|
||||||
|
|
||||||
|
stack_resources = cf_conn.list_stack_resources(StackName=stack_id)
|
||||||
|
stack_resources['StackResourceSummaries'].should.have.length_of(1)
|
||||||
|
spot_fleet_id = stack_resources[
|
||||||
|
'StackResourceSummaries'][0]['PhysicalResourceId']
|
||||||
|
|
||||||
|
spot_fleet_requests = conn.describe_spot_fleet_requests(
|
||||||
|
SpotFleetRequestIds=[spot_fleet_id])['SpotFleetRequestConfigs']
|
||||||
|
len(spot_fleet_requests).should.equal(1)
|
||||||
|
spot_fleet_request = spot_fleet_requests[0]
|
||||||
|
spot_fleet_request['SpotFleetRequestState'].should.equal("active")
|
||||||
|
spot_fleet_config = spot_fleet_request['SpotFleetRequestConfig']
|
||||||
|
|
||||||
|
assert 'SpotPrice' not in spot_fleet_config
|
||||||
|
len(spot_fleet_config['LaunchSpecifications']).should.equal(2)
|
||||||
|
launch_spec1 = spot_fleet_config['LaunchSpecifications'][0]
|
||||||
|
launch_spec2 = spot_fleet_config['LaunchSpecifications'][1]
|
||||||
|
|
||||||
|
assert 'SpotPrice' not in launch_spec1
|
||||||
|
assert 'SpotPrice' not in launch_spec2
|
||||||
|
|
||||||
|
|
||||||
@mock_ec2
|
@mock_ec2
|
||||||
@mock_elbv2
|
@mock_elbv2
|
||||||
@mock_cloudformation
|
@mock_cloudformation
|
||||||
|
@ -316,3 +316,30 @@ def test_modify_spot_fleet_request_down_no_terminate_after_custom_terminate():
|
|||||||
SpotFleetRequestIds=[spot_fleet_id])['SpotFleetRequestConfigs'][0]['SpotFleetRequestConfig']
|
SpotFleetRequestIds=[spot_fleet_id])['SpotFleetRequestConfigs'][0]['SpotFleetRequestConfig']
|
||||||
spot_fleet_config['TargetCapacity'].should.equal(1)
|
spot_fleet_config['TargetCapacity'].should.equal(1)
|
||||||
spot_fleet_config['FulfilledCapacity'].should.equal(2.0)
|
spot_fleet_config['FulfilledCapacity'].should.equal(2.0)
|
||||||
|
|
||||||
|
|
||||||
|
@mock_ec2
|
||||||
|
def test_create_spot_fleet_without_spot_price():
|
||||||
|
conn = boto3.client("ec2", region_name='us-west-2')
|
||||||
|
subnet_id = get_subnet_id(conn)
|
||||||
|
|
||||||
|
# remove prices to force a fallback to ondemand price
|
||||||
|
spot_config_without_price = spot_config(subnet_id)
|
||||||
|
del spot_config_without_price['SpotPrice']
|
||||||
|
for spec in spot_config_without_price['LaunchSpecifications']:
|
||||||
|
del spec['SpotPrice']
|
||||||
|
|
||||||
|
spot_fleet_id = conn.request_spot_fleet(SpotFleetRequestConfig=spot_config_without_price)['SpotFleetRequestId']
|
||||||
|
spot_fleet_requests = conn.describe_spot_fleet_requests(
|
||||||
|
SpotFleetRequestIds=[spot_fleet_id])['SpotFleetRequestConfigs']
|
||||||
|
len(spot_fleet_requests).should.equal(1)
|
||||||
|
spot_fleet_request = spot_fleet_requests[0]
|
||||||
|
spot_fleet_config = spot_fleet_request['SpotFleetRequestConfig']
|
||||||
|
|
||||||
|
len(spot_fleet_config['LaunchSpecifications']).should.equal(2)
|
||||||
|
launch_spec1 = spot_fleet_config['LaunchSpecifications'][0]
|
||||||
|
launch_spec2 = spot_fleet_config['LaunchSpecifications'][1]
|
||||||
|
|
||||||
|
# AWS will figure out the price
|
||||||
|
assert 'SpotPrice' not in launch_spec1
|
||||||
|
assert 'SpotPrice' not in launch_spec2
|
||||||
|
Loading…
Reference in New Issue
Block a user