diff --git a/moto/ec2/models.py b/moto/ec2/models.py
index f877d377251..bfc672ed764 100755
--- a/moto/ec2/models.py
+++ b/moto/ec2/models.py
@@ -2943,7 +2943,7 @@ def create_from_cloudformation_json(cls, resource_name, cloudformation_json, reg
'Properties']['SpotFleetRequestConfigData']
ec2_backend = ec2_backends[region_name]
- spot_price = properties['SpotPrice']
+ spot_price = properties.get('SpotPrice')
target_capacity = properties['TargetCapacity']
iam_fleet_role = properties['IamFleetRole']
allocation_strategy = properties['AllocationStrategy']
@@ -2977,7 +2977,8 @@ def get_launch_spec_counts(self, weight_to_add):
launch_spec_index += 1
else: # lowestPrice
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_map[cheapest_spec] = int(
weight_so_far // cheapest_spec.weighted_capacity)
diff --git a/moto/ec2/responses/spot_fleets.py b/moto/ec2/responses/spot_fleets.py
index 81d1e014633..0366af9d6cd 100644
--- a/moto/ec2/responses/spot_fleets.py
+++ b/moto/ec2/responses/spot_fleets.py
@@ -40,7 +40,7 @@ def modify_spot_fleet_request(self):
def request_spot_fleet(self):
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']
iam_fleet_role = spot_config['iam_fleet_role']
allocation_strategy = spot_config['allocation_strategy']
@@ -78,7 +78,9 @@ def request_spot_fleet(self):
{{ request.id }}
{{ request.state }}
+ {% if request.spot_price %}
{{ request.spot_price }}
+ {% endif %}
{{ request.target_capacity }}
{{ request.iam_fleet_role }}
{{ request.allocation_strategy }}
@@ -93,7 +95,9 @@ def request_spot_fleet(self):
{{ launch_spec.iam_instance_profile }}
{{ launch_spec.key_name }}
{{ launch_spec.monitoring }}
+ {% if launch_spec.spot_price %}
{{ launch_spec.spot_price }}
+ {% endif %}
{{ launch_spec.user_data }}
{{ launch_spec.weighted_capacity }}
diff --git a/requirements-dev.txt b/requirements-dev.txt
index 1b151eb2953..655be0616d1 100644
--- a/requirements-dev.txt
+++ b/requirements-dev.txt
@@ -8,7 +8,7 @@ freezegun
flask
boto>=2.45.0
boto3>=1.4.4
-botocore>=1.5.77
+botocore>=1.8.36
six>=1.9
prompt-toolkit==1.0.14
click==6.7
diff --git a/tests/test_cloudformation/test_cloudformation_stack_integration.py b/tests/test_cloudformation/test_cloudformation_stack_integration.py
index 3a7525585fb..2c808726fdd 100644
--- a/tests/test_cloudformation/test_cloudformation_stack_integration.py
+++ b/tests/test_cloudformation/test_cloudformation_stack_integration.py
@@ -2156,6 +2156,78 @@ def test_stack_spot_fleet():
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_elbv2
@mock_cloudformation
diff --git a/tests/test_ec2/test_spot_fleet.py b/tests/test_ec2/test_spot_fleet.py
index a8d33c299da..a2bd1d061ab 100644
--- a/tests/test_ec2/test_spot_fleet.py
+++ b/tests/test_ec2/test_spot_fleet.py
@@ -316,3 +316,30 @@ def test_modify_spot_fleet_request_down_no_terminate_after_custom_terminate():
SpotFleetRequestIds=[spot_fleet_id])['SpotFleetRequestConfigs'][0]['SpotFleetRequestConfig']
spot_fleet_config['TargetCapacity'].should.equal(1)
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