2014-08-27 15:17:06 +00:00
|
|
|
from __future__ import unicode_literals
|
2020-10-06 05:54:49 +00:00
|
|
|
import pytest
|
2013-08-17 22:11:29 +00:00
|
|
|
import datetime
|
|
|
|
|
2013-02-22 04:13:01 +00:00
|
|
|
import boto
|
2016-11-08 04:08:30 +00:00
|
|
|
import boto3
|
2017-02-24 00:43:48 +00:00
|
|
|
from boto.exception import EC2ResponseError
|
|
|
|
from botocore.exceptions import ClientError
|
|
|
|
import pytz
|
2013-08-03 21:21:25 +00:00
|
|
|
import sure # noqa
|
2013-02-22 04:13:01 +00:00
|
|
|
|
2020-09-13 15:08:23 +00:00
|
|
|
from moto import mock_ec2, mock_ec2_deprecated, settings
|
|
|
|
from moto.ec2.models import ec2_backends
|
2014-11-30 04:34:40 +00:00
|
|
|
from moto.core.utils import iso_8601_datetime_with_milliseconds
|
2021-01-13 09:02:11 +00:00
|
|
|
from tests import EXAMPLE_AMI_ID
|
2013-08-17 22:11:29 +00:00
|
|
|
|
|
|
|
|
|
|
|
@mock_ec2
|
|
|
|
def test_request_spot_instances():
|
2019-10-31 15:44:26 +00:00
|
|
|
conn = boto3.client("ec2", "us-east-1")
|
|
|
|
vpc = conn.create_vpc(CidrBlock="10.0.0.0/16")["Vpc"]
|
2017-02-24 02:37:43 +00:00
|
|
|
subnet = conn.create_subnet(
|
2019-10-31 15:44:26 +00:00
|
|
|
VpcId=vpc["VpcId"], CidrBlock="10.0.0.0/16", AvailabilityZone="us-east-1a"
|
|
|
|
)["Subnet"]
|
|
|
|
subnet_id = subnet["SubnetId"]
|
2016-11-08 04:08:30 +00:00
|
|
|
|
2019-10-31 15:44:26 +00:00
|
|
|
conn.create_security_group(GroupName="group1", Description="description")
|
|
|
|
conn.create_security_group(GroupName="group2", Description="description")
|
2013-08-17 22:11:29 +00:00
|
|
|
|
2017-02-24 00:43:48 +00:00
|
|
|
start_dt = datetime.datetime(2013, 1, 1).replace(tzinfo=pytz.utc)
|
|
|
|
end_dt = datetime.datetime(2013, 1, 2).replace(tzinfo=pytz.utc)
|
|
|
|
start = iso_8601_datetime_with_milliseconds(start_dt)
|
|
|
|
end = iso_8601_datetime_with_milliseconds(end_dt)
|
2013-08-17 22:11:29 +00:00
|
|
|
|
2020-10-06 05:54:49 +00:00
|
|
|
with pytest.raises(ClientError) as ex:
|
2016-10-15 13:08:44 +00:00
|
|
|
request = conn.request_spot_instances(
|
2019-10-31 15:44:26 +00:00
|
|
|
SpotPrice="0.5",
|
|
|
|
InstanceCount=1,
|
|
|
|
Type="one-time",
|
|
|
|
ValidFrom=start,
|
|
|
|
ValidUntil=end,
|
|
|
|
LaunchGroup="the-group",
|
|
|
|
AvailabilityZoneGroup="my-group",
|
2017-02-24 00:43:48 +00:00
|
|
|
LaunchSpecification={
|
2021-01-13 09:02:11 +00:00
|
|
|
"ImageId": EXAMPLE_AMI_ID,
|
2017-02-24 00:43:48 +00:00
|
|
|
"KeyName": "test",
|
2019-10-31 15:44:26 +00:00
|
|
|
"SecurityGroups": ["group1", "group2"],
|
2017-02-24 03:28:09 +00:00
|
|
|
"UserData": "some test data",
|
2019-10-31 15:44:26 +00:00
|
|
|
"InstanceType": "m1.small",
|
|
|
|
"Placement": {"AvailabilityZone": "us-east-1c"},
|
2017-02-24 00:43:48 +00:00
|
|
|
"KernelId": "test-kernel",
|
|
|
|
"RamdiskId": "test-ramdisk",
|
2019-10-31 15:44:26 +00:00
|
|
|
"Monitoring": {"Enabled": True},
|
2017-02-24 00:43:48 +00:00
|
|
|
"SubnetId": subnet_id,
|
|
|
|
},
|
|
|
|
DryRun=True,
|
2016-10-15 13:08:44 +00:00
|
|
|
)
|
2020-10-06 06:04:09 +00:00
|
|
|
ex.value.response["Error"]["Code"].should.equal("DryRunOperation")
|
2021-09-07 16:10:01 +00:00
|
|
|
ex.value.response["ResponseMetadata"]["HTTPStatusCode"].should.equal(412)
|
2020-10-06 06:04:09 +00:00
|
|
|
ex.value.response["Error"]["Message"].should.equal(
|
2019-10-31 15:44:26 +00:00
|
|
|
"An error occurred (DryRunOperation) when calling the RequestSpotInstance operation: Request would have succeeded, but DryRun flag is set"
|
|
|
|
)
|
2016-10-15 13:08:44 +00:00
|
|
|
|
2013-08-17 22:11:29 +00:00
|
|
|
request = conn.request_spot_instances(
|
2019-10-31 15:44:26 +00:00
|
|
|
SpotPrice="0.5",
|
|
|
|
InstanceCount=1,
|
|
|
|
Type="one-time",
|
|
|
|
ValidFrom=start,
|
|
|
|
ValidUntil=end,
|
|
|
|
LaunchGroup="the-group",
|
|
|
|
AvailabilityZoneGroup="my-group",
|
2017-02-24 02:37:43 +00:00
|
|
|
LaunchSpecification={
|
2021-01-13 09:02:11 +00:00
|
|
|
"ImageId": EXAMPLE_AMI_ID,
|
2017-02-24 02:37:43 +00:00
|
|
|
"KeyName": "test",
|
2019-10-31 15:44:26 +00:00
|
|
|
"SecurityGroups": ["group1", "group2"],
|
2017-02-24 03:28:09 +00:00
|
|
|
"UserData": "some test data",
|
2019-10-31 15:44:26 +00:00
|
|
|
"InstanceType": "m1.small",
|
|
|
|
"Placement": {"AvailabilityZone": "us-east-1c"},
|
2017-02-24 02:37:43 +00:00
|
|
|
"KernelId": "test-kernel",
|
|
|
|
"RamdiskId": "test-ramdisk",
|
2019-10-31 15:44:26 +00:00
|
|
|
"Monitoring": {"Enabled": True},
|
2017-02-24 02:37:43 +00:00
|
|
|
"SubnetId": subnet_id,
|
|
|
|
},
|
2013-08-17 22:11:29 +00:00
|
|
|
)
|
|
|
|
|
2019-10-31 15:44:26 +00:00
|
|
|
requests = conn.describe_spot_instance_requests()["SpotInstanceRequests"]
|
2013-08-17 22:11:29 +00:00
|
|
|
requests.should.have.length_of(1)
|
|
|
|
request = requests[0]
|
|
|
|
|
2019-10-31 15:44:26 +00:00
|
|
|
request["State"].should.equal("open")
|
|
|
|
request["SpotPrice"].should.equal("0.5")
|
|
|
|
request["Type"].should.equal("one-time")
|
|
|
|
request["ValidFrom"].should.equal(start_dt)
|
|
|
|
request["ValidUntil"].should.equal(end_dt)
|
|
|
|
request["LaunchGroup"].should.equal("the-group")
|
|
|
|
request["AvailabilityZoneGroup"].should.equal("my-group")
|
2017-02-24 00:43:48 +00:00
|
|
|
|
2019-10-31 15:44:26 +00:00
|
|
|
launch_spec = request["LaunchSpecification"]
|
|
|
|
security_group_names = [
|
|
|
|
group["GroupName"] for group in launch_spec["SecurityGroups"]
|
|
|
|
]
|
|
|
|
set(security_group_names).should.equal(set(["group1", "group2"]))
|
2013-02-22 04:13:01 +00:00
|
|
|
|
2021-01-13 09:02:11 +00:00
|
|
|
launch_spec["ImageId"].should.equal(EXAMPLE_AMI_ID)
|
2019-10-31 15:44:26 +00:00
|
|
|
launch_spec["KeyName"].should.equal("test")
|
|
|
|
launch_spec["InstanceType"].should.equal("m1.small")
|
|
|
|
launch_spec["KernelId"].should.equal("test-kernel")
|
|
|
|
launch_spec["RamdiskId"].should.equal("test-ramdisk")
|
|
|
|
launch_spec["SubnetId"].should.equal(subnet_id)
|
2013-02-22 04:13:01 +00:00
|
|
|
|
2017-02-24 00:43:48 +00:00
|
|
|
|
|
|
|
@mock_ec2
|
2013-08-17 22:11:29 +00:00
|
|
|
def test_request_spot_instances_default_arguments():
|
|
|
|
"""
|
|
|
|
Test that moto set the correct default arguments
|
|
|
|
"""
|
2019-10-31 15:44:26 +00:00
|
|
|
conn = boto3.client("ec2", "us-east-1")
|
2013-08-17 22:11:29 +00:00
|
|
|
|
|
|
|
request = conn.request_spot_instances(
|
2021-01-13 09:02:11 +00:00
|
|
|
SpotPrice="0.5", LaunchSpecification={"ImageId": EXAMPLE_AMI_ID}
|
2013-08-17 22:11:29 +00:00
|
|
|
)
|
|
|
|
|
2019-10-31 15:44:26 +00:00
|
|
|
requests = conn.describe_spot_instance_requests()["SpotInstanceRequests"]
|
2013-08-17 22:11:29 +00:00
|
|
|
requests.should.have.length_of(1)
|
|
|
|
request = requests[0]
|
|
|
|
|
2019-10-31 15:44:26 +00:00
|
|
|
request["State"].should.equal("open")
|
|
|
|
request["SpotPrice"].should.equal("0.5")
|
|
|
|
request["Type"].should.equal("one-time")
|
|
|
|
request.shouldnt.contain("ValidFrom")
|
|
|
|
request.shouldnt.contain("ValidUntil")
|
|
|
|
request.shouldnt.contain("LaunchGroup")
|
|
|
|
request.shouldnt.contain("AvailabilityZoneGroup")
|
2017-02-24 00:43:48 +00:00
|
|
|
|
2019-10-31 15:44:26 +00:00
|
|
|
launch_spec = request["LaunchSpecification"]
|
2017-02-24 00:43:48 +00:00
|
|
|
|
2019-10-31 15:44:26 +00:00
|
|
|
security_group_names = [
|
|
|
|
group["GroupName"] for group in launch_spec["SecurityGroups"]
|
|
|
|
]
|
2013-08-17 22:11:29 +00:00
|
|
|
security_group_names.should.equal(["default"])
|
2017-02-24 00:43:48 +00:00
|
|
|
|
2021-01-13 09:02:11 +00:00
|
|
|
launch_spec["ImageId"].should.equal(EXAMPLE_AMI_ID)
|
2019-10-31 15:44:26 +00:00
|
|
|
request.shouldnt.contain("KeyName")
|
|
|
|
launch_spec["InstanceType"].should.equal("m1.small")
|
|
|
|
request.shouldnt.contain("KernelId")
|
|
|
|
request.shouldnt.contain("RamdiskId")
|
|
|
|
request.shouldnt.contain("SubnetId")
|
2013-08-17 22:11:29 +00:00
|
|
|
|
|
|
|
|
2017-02-16 03:35:45 +00:00
|
|
|
@mock_ec2_deprecated
|
2013-08-17 22:11:29 +00:00
|
|
|
def test_cancel_spot_instance_request():
|
|
|
|
conn = boto.connect_ec2()
|
|
|
|
|
2021-01-13 09:02:11 +00:00
|
|
|
conn.request_spot_instances(price=0.5, image_id=EXAMPLE_AMI_ID)
|
2013-08-17 22:11:29 +00:00
|
|
|
|
|
|
|
requests = conn.get_all_spot_instance_requests()
|
|
|
|
requests.should.have.length_of(1)
|
|
|
|
|
2020-10-06 05:54:49 +00:00
|
|
|
with pytest.raises(EC2ResponseError) as ex:
|
2016-10-15 13:08:44 +00:00
|
|
|
conn.cancel_spot_instance_requests([requests[0].id], dry_run=True)
|
2020-10-06 06:04:09 +00:00
|
|
|
ex.value.error_code.should.equal("DryRunOperation")
|
2021-09-07 16:10:01 +00:00
|
|
|
ex.value.status.should.equal(412)
|
2020-10-06 06:04:09 +00:00
|
|
|
ex.value.message.should.equal(
|
2019-10-31 15:44:26 +00:00
|
|
|
"An error occurred (DryRunOperation) when calling the CancelSpotInstance operation: Request would have succeeded, but DryRun flag is set"
|
|
|
|
)
|
2016-10-15 13:08:44 +00:00
|
|
|
|
2013-08-17 22:11:29 +00:00
|
|
|
conn.cancel_spot_instance_requests([requests[0].id])
|
|
|
|
|
|
|
|
requests = conn.get_all_spot_instance_requests()
|
|
|
|
requests.should.have.length_of(0)
|
2014-07-29 12:29:52 +00:00
|
|
|
|
|
|
|
|
2017-02-16 03:35:45 +00:00
|
|
|
@mock_ec2_deprecated
|
2014-07-29 12:29:52 +00:00
|
|
|
def test_request_spot_instances_fulfilled():
|
|
|
|
"""
|
|
|
|
Test that moto correctly fullfills a spot instance request
|
|
|
|
"""
|
2014-11-15 18:50:58 +00:00
|
|
|
conn = boto.ec2.connect_to_region("us-east-1")
|
2014-07-29 12:29:52 +00:00
|
|
|
|
2021-01-13 09:02:11 +00:00
|
|
|
request = conn.request_spot_instances(price=0.5, image_id=EXAMPLE_AMI_ID)
|
2014-07-29 12:29:52 +00:00
|
|
|
|
|
|
|
requests = conn.get_all_spot_instance_requests()
|
|
|
|
requests.should.have.length_of(1)
|
|
|
|
request = requests[0]
|
|
|
|
|
|
|
|
request.state.should.equal("open")
|
|
|
|
|
2020-09-13 15:08:23 +00:00
|
|
|
if not settings.TEST_SERVER_MODE:
|
|
|
|
ec2_backends["us-east-1"].spot_instance_requests[request.id].state = "active"
|
2014-07-29 12:29:52 +00:00
|
|
|
|
2020-09-13 15:08:23 +00:00
|
|
|
requests = conn.get_all_spot_instance_requests()
|
|
|
|
requests.should.have.length_of(1)
|
|
|
|
request = requests[0]
|
2014-07-29 12:29:52 +00:00
|
|
|
|
2020-09-13 15:08:23 +00:00
|
|
|
request.state.should.equal("active")
|
2014-09-04 19:38:09 +00:00
|
|
|
|
|
|
|
|
2017-02-16 03:35:45 +00:00
|
|
|
@mock_ec2_deprecated
|
2014-09-04 19:38:09 +00:00
|
|
|
def test_tag_spot_instance_request():
|
|
|
|
"""
|
|
|
|
Test that moto correctly tags a spot instance request
|
|
|
|
"""
|
|
|
|
conn = boto.connect_ec2()
|
|
|
|
|
2021-01-13 09:02:11 +00:00
|
|
|
request = conn.request_spot_instances(price=0.5, image_id=EXAMPLE_AMI_ID)
|
2019-10-31 15:44:26 +00:00
|
|
|
request[0].add_tag("tag1", "value1")
|
|
|
|
request[0].add_tag("tag2", "value2")
|
2014-09-04 19:38:09 +00:00
|
|
|
|
|
|
|
requests = conn.get_all_spot_instance_requests()
|
|
|
|
requests.should.have.length_of(1)
|
|
|
|
request = requests[0]
|
|
|
|
|
|
|
|
tag_dict = dict(request.tags)
|
2019-10-31 15:44:26 +00:00
|
|
|
tag_dict.should.equal({"tag1": "value1", "tag2": "value2"})
|
2014-09-04 19:39:14 +00:00
|
|
|
|
|
|
|
|
2017-02-16 03:35:45 +00:00
|
|
|
@mock_ec2_deprecated
|
2014-09-04 19:39:14 +00:00
|
|
|
def test_get_all_spot_instance_requests_filtering():
|
|
|
|
"""
|
|
|
|
Test that moto correctly filters spot instance requests
|
|
|
|
"""
|
|
|
|
conn = boto.connect_ec2()
|
|
|
|
|
2021-01-13 09:02:11 +00:00
|
|
|
request1 = conn.request_spot_instances(price=0.5, image_id=EXAMPLE_AMI_ID)
|
|
|
|
request2 = conn.request_spot_instances(price=0.5, image_id=EXAMPLE_AMI_ID)
|
|
|
|
conn.request_spot_instances(price=0.5, image_id=EXAMPLE_AMI_ID)
|
2019-10-31 15:44:26 +00:00
|
|
|
request1[0].add_tag("tag1", "value1")
|
|
|
|
request1[0].add_tag("tag2", "value2")
|
|
|
|
request2[0].add_tag("tag1", "value1")
|
|
|
|
request2[0].add_tag("tag2", "wrong")
|
2014-09-04 19:39:14 +00:00
|
|
|
|
2019-10-31 15:44:26 +00:00
|
|
|
requests = conn.get_all_spot_instance_requests(filters={"state": "active"})
|
2014-09-04 19:39:14 +00:00
|
|
|
requests.should.have.length_of(0)
|
|
|
|
|
2019-10-31 15:44:26 +00:00
|
|
|
requests = conn.get_all_spot_instance_requests(filters={"state": "open"})
|
2014-09-04 19:39:14 +00:00
|
|
|
requests.should.have.length_of(3)
|
|
|
|
|
2019-10-31 15:44:26 +00:00
|
|
|
requests = conn.get_all_spot_instance_requests(filters={"tag:tag1": "value1"})
|
2014-09-04 19:39:14 +00:00
|
|
|
requests.should.have.length_of(2)
|
|
|
|
|
2017-02-24 02:37:43 +00:00
|
|
|
requests = conn.get_all_spot_instance_requests(
|
2019-10-31 15:44:26 +00:00
|
|
|
filters={"tag:tag1": "value1", "tag:tag2": "value2"}
|
|
|
|
)
|
2014-09-04 19:39:14 +00:00
|
|
|
requests.should.have.length_of(1)
|
2014-10-06 02:55:36 +00:00
|
|
|
|
|
|
|
|
2017-02-16 03:35:45 +00:00
|
|
|
@mock_ec2_deprecated
|
2014-10-06 02:55:36 +00:00
|
|
|
def test_request_spot_instances_setting_instance_id():
|
2014-11-15 18:50:58 +00:00
|
|
|
conn = boto.ec2.connect_to_region("us-east-1")
|
2021-01-13 09:02:11 +00:00
|
|
|
request = conn.request_spot_instances(price=0.5, image_id=EXAMPLE_AMI_ID)
|
2014-10-06 02:55:36 +00:00
|
|
|
|
2020-09-13 15:08:23 +00:00
|
|
|
if not settings.TEST_SERVER_MODE:
|
|
|
|
req = ec2_backends["us-east-1"].spot_instance_requests[request[0].id]
|
|
|
|
req.state = "active"
|
|
|
|
req.instance_id = "i-12345678"
|
2014-10-06 02:55:36 +00:00
|
|
|
|
2020-09-13 15:08:23 +00:00
|
|
|
request = conn.get_all_spot_instance_requests()[0]
|
|
|
|
assert request.state == "active"
|
|
|
|
assert request.instance_id == "i-12345678"
|
2021-09-02 13:12:17 +00:00
|
|
|
|
|
|
|
|
|
|
|
@mock_ec2
|
|
|
|
def test_request_spot_instances_instance_lifecycle():
|
|
|
|
client = boto3.client("ec2", region_name="us-east-1")
|
|
|
|
request = client.request_spot_instances(SpotPrice="0.5")
|
|
|
|
|
|
|
|
response = client.describe_instances()
|
|
|
|
|
|
|
|
instance = response["Reservations"][0]["Instances"][0]
|
|
|
|
instance["InstanceLifecycle"].should.equal("spot")
|
|
|
|
|
|
|
|
|
|
|
|
@mock_ec2
|
|
|
|
def test_launch_spot_instance_instance_lifecycle():
|
|
|
|
client = boto3.client("ec2", region_name="us-east-1")
|
|
|
|
|
|
|
|
kwargs = {
|
|
|
|
"KeyName": "foobar",
|
|
|
|
"ImageId": "ami-pytest",
|
|
|
|
"MinCount": 1,
|
|
|
|
"MaxCount": 1,
|
|
|
|
"InstanceType": "c4.2xlarge",
|
|
|
|
"TagSpecifications": [
|
|
|
|
{"ResourceType": "instance", "Tags": [{"Key": "key", "Value": "val"}]},
|
|
|
|
],
|
|
|
|
"InstanceMarketOptions": {"MarketType": "spot"},
|
|
|
|
}
|
|
|
|
|
|
|
|
client.run_instances(**kwargs)["Instances"][0]
|
|
|
|
|
|
|
|
response = client.describe_instances()
|
|
|
|
instance = response["Reservations"][0]["Instances"][0]
|
|
|
|
instance["InstanceLifecycle"].should.equal("spot")
|
|
|
|
|
|
|
|
|
|
|
|
@mock_ec2
|
|
|
|
def test_launch_instance_instance_lifecycle():
|
|
|
|
client = boto3.client("ec2", region_name="us-east-1")
|
|
|
|
|
|
|
|
kwargs = {
|
|
|
|
"KeyName": "foobar",
|
|
|
|
"ImageId": "ami-pytest",
|
|
|
|
"MinCount": 1,
|
|
|
|
"MaxCount": 1,
|
|
|
|
"InstanceType": "c4.2xlarge",
|
|
|
|
"TagSpecifications": [
|
|
|
|
{"ResourceType": "instance", "Tags": [{"Key": "key", "Value": "val"}]},
|
|
|
|
],
|
|
|
|
}
|
|
|
|
|
|
|
|
client.run_instances(**kwargs)["Instances"][0]
|
|
|
|
|
|
|
|
response = client.describe_instances()
|
|
|
|
instance = response["Reservations"][0]["Instances"][0]
|
2021-09-22 16:32:30 +00:00
|
|
|
instance.get("InstanceLifecycle").should.equal(None)
|
2021-09-04 15:57:01 +00:00
|
|
|
|
|
|
|
|
|
|
|
@mock_ec2
|
|
|
|
def test_spot_price_history():
|
|
|
|
client = boto3.client("ec2", region_name="us-east-1")
|
|
|
|
# test filter
|
|
|
|
response = client.describe_spot_price_history(
|
|
|
|
Filters=[
|
|
|
|
{"Name": "availability-zone", "Values": ["us-east-1a"]},
|
|
|
|
{"Name": "instance-type", "Values": ["t3a.micro"]},
|
|
|
|
]
|
|
|
|
)
|
|
|
|
price = response["SpotPriceHistory"][0]
|
|
|
|
price["InstanceType"].should.equal("t3a.micro")
|
|
|
|
price["AvailabilityZone"].should.equal("us-east-1a")
|
|
|
|
|
|
|
|
# test instance types
|
|
|
|
i_types = ["t3a.micro", "t3.micro"]
|
|
|
|
response = client.describe_spot_price_history(InstanceTypes=i_types)
|
|
|
|
price = response["SpotPriceHistory"][0]
|
|
|
|
assert price["InstanceType"] in i_types
|