2023-09-03 14:59:09 +00:00
|
|
|
import os
|
2021-10-05 17:11:07 +00:00
|
|
|
import random
|
2023-11-30 15:55:51 +00:00
|
|
|
from unittest import SkipTest, mock
|
|
|
|
from uuid import uuid4
|
|
|
|
|
|
|
|
import boto3
|
|
|
|
import pytest
|
|
|
|
from botocore.exceptions import ClientError
|
2013-02-22 04:13:01 +00:00
|
|
|
|
2024-01-07 12:03:33 +00:00
|
|
|
from moto import mock_aws, settings
|
2022-08-13 09:49:43 +00:00
|
|
|
from moto.core import DEFAULT_ACCOUNT_ID as ACCOUNT_ID
|
2023-11-30 15:55:51 +00:00
|
|
|
from moto.ec2.models.amis import AMIS
|
2021-10-05 17:11:07 +00:00
|
|
|
from tests import EXAMPLE_AMI_ID, EXAMPLE_AMI_PARAVIRTUAL
|
2013-02-22 04:13:01 +00:00
|
|
|
|
|
|
|
|
2023-09-03 14:59:09 +00:00
|
|
|
# The default AMIs are not loaded for our test case, to speed things up
|
|
|
|
# But we do need it for this specific test (and others in this file..)
|
|
|
|
@mock.patch.dict(os.environ, {"MOTO_EC2_LOAD_DEFAULT_AMIS": "true"})
|
2024-01-07 12:03:33 +00:00
|
|
|
@mock_aws
|
2021-10-05 17:11:07 +00:00
|
|
|
def test_snapshots_for_initial_amis():
|
2023-09-03 14:59:09 +00:00
|
|
|
if settings.TEST_SERVER_MODE:
|
|
|
|
raise SkipTest("Can't set environment variables in ServerMode")
|
2021-09-25 11:13:07 +00:00
|
|
|
ec2 = boto3.client("ec2", region_name="us-east-1")
|
|
|
|
|
2021-10-05 17:11:07 +00:00
|
|
|
snapshots = ec2.describe_snapshots()["Snapshots"]
|
|
|
|
snapshot_descs = [s["Description"] for s in snapshots]
|
2021-09-25 11:13:07 +00:00
|
|
|
initial_ami_count = len(AMIS)
|
2021-10-05 17:11:07 +00:00
|
|
|
|
|
|
|
assert (
|
|
|
|
len(snapshots) >= initial_ami_count
|
|
|
|
), "Should have at least as many snapshots as AMIs"
|
|
|
|
|
|
|
|
for ami in AMIS:
|
|
|
|
ami_id = ami["ami_id"]
|
|
|
|
expected_description = f"Auto-created snapshot for AMI {ami_id}"
|
2023-07-17 09:31:05 +00:00
|
|
|
assert expected_description in snapshot_descs
|
2021-10-05 17:11:07 +00:00
|
|
|
|
|
|
|
|
2023-09-03 14:59:09 +00:00
|
|
|
@mock.patch.dict(os.environ, {"MOTO_EC2_LOAD_DEFAULT_AMIS": "true"})
|
2024-01-07 12:03:33 +00:00
|
|
|
@mock_aws
|
2022-04-18 20:44:56 +00:00
|
|
|
def test_ami_create_and_delete():
|
2023-09-03 14:59:09 +00:00
|
|
|
if settings.TEST_SERVER_MODE:
|
|
|
|
raise SkipTest("Can't set environment variables in ServerMode")
|
2021-10-05 17:11:07 +00:00
|
|
|
ec2 = boto3.client("ec2", region_name="us-east-1")
|
2021-09-25 11:13:07 +00:00
|
|
|
|
|
|
|
reservation = ec2.run_instances(ImageId=EXAMPLE_AMI_ID, MinCount=1, MaxCount=1)
|
|
|
|
instance = reservation["Instances"][0]
|
2021-10-05 17:11:07 +00:00
|
|
|
instance_id = instance["InstanceId"]
|
2021-09-25 11:13:07 +00:00
|
|
|
|
|
|
|
with pytest.raises(ClientError) as ex:
|
|
|
|
ec2.create_image(
|
|
|
|
InstanceId=instance["InstanceId"], Name="test-ami", DryRun=True
|
|
|
|
)
|
2023-07-17 09:31:05 +00:00
|
|
|
assert ex.value.response["ResponseMetadata"]["HTTPStatusCode"] == 412
|
2021-09-25 11:13:07 +00:00
|
|
|
err = ex.value.response["Error"]
|
2023-07-17 09:31:05 +00:00
|
|
|
assert err["Code"] == "DryRunOperation"
|
|
|
|
assert (
|
|
|
|
err["Message"]
|
|
|
|
== "An error occurred (DryRunOperation) when calling the CreateImage operation: Request would have succeeded, but DryRun flag is set"
|
2021-09-25 11:13:07 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
image_id = ec2.create_image(
|
2021-10-05 17:11:07 +00:00
|
|
|
InstanceId=instance_id, Name="test-ami", Description="this is a test ami"
|
2021-09-25 11:13:07 +00:00
|
|
|
)["ImageId"]
|
|
|
|
|
|
|
|
all_images = ec2.describe_images()["Images"]
|
2023-07-17 09:31:05 +00:00
|
|
|
assert image_id in set([i["ImageId"] for i in all_images])
|
2021-09-25 11:13:07 +00:00
|
|
|
|
|
|
|
retrieved_image = [i for i in all_images if i["ImageId"] == image_id][0]
|
|
|
|
|
2023-07-17 09:31:05 +00:00
|
|
|
assert retrieved_image["ImageId"] == image_id
|
|
|
|
assert retrieved_image["VirtualizationType"] == instance["VirtualizationType"]
|
|
|
|
assert retrieved_image["Architecture"] == instance["Architecture"]
|
|
|
|
assert retrieved_image["KernelId"] == instance["KernelId"]
|
|
|
|
assert retrieved_image["Platform"] == instance["Platform"]
|
|
|
|
assert "CreationDate" in retrieved_image
|
2021-10-05 17:11:07 +00:00
|
|
|
ec2.terminate_instances(InstanceIds=[instance_id])
|
2021-09-25 11:13:07 +00:00
|
|
|
|
|
|
|
# Ensure we're no longer creating a volume
|
2021-10-05 17:11:07 +00:00
|
|
|
volumes_for_instance = [
|
|
|
|
v
|
|
|
|
for v in ec2.describe_volumes()["Volumes"]
|
|
|
|
if "Attachment" in v and v["Attachment"][0]["InstanceId"] == instance_id
|
|
|
|
]
|
2023-07-17 09:31:05 +00:00
|
|
|
assert len(volumes_for_instance) == 0
|
2021-09-25 11:13:07 +00:00
|
|
|
|
|
|
|
# Validate auto-created snapshot
|
|
|
|
snapshots = ec2.describe_snapshots()["Snapshots"]
|
|
|
|
|
|
|
|
retrieved_image_snapshot_id = retrieved_image["BlockDeviceMappings"][0]["Ebs"][
|
|
|
|
"SnapshotId"
|
|
|
|
]
|
2023-07-17 09:31:05 +00:00
|
|
|
assert retrieved_image_snapshot_id in [s["SnapshotId"] for s in snapshots]
|
2021-09-25 11:13:07 +00:00
|
|
|
snapshot = [s for s in snapshots if s["SnapshotId"] == retrieved_image_snapshot_id][
|
|
|
|
0
|
|
|
|
]
|
2021-12-25 21:37:39 +00:00
|
|
|
image_id = retrieved_image["ImageId"]
|
2023-07-17 09:31:05 +00:00
|
|
|
assert (
|
|
|
|
snapshot["Description"]
|
|
|
|
== f"Created by CreateImage({instance_id}) for {image_id}"
|
2021-09-25 11:13:07 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
# root device should be in AMI's block device mappings
|
|
|
|
root_mapping = [
|
|
|
|
m
|
|
|
|
for m in retrieved_image["BlockDeviceMappings"]
|
|
|
|
if m["DeviceName"] == retrieved_image["RootDeviceName"]
|
|
|
|
]
|
2023-07-17 09:31:05 +00:00
|
|
|
assert root_mapping != []
|
2021-09-25 11:13:07 +00:00
|
|
|
|
|
|
|
# Deregister
|
|
|
|
with pytest.raises(ClientError) as ex:
|
|
|
|
ec2.deregister_image(ImageId=image_id, DryRun=True)
|
2023-07-17 09:31:05 +00:00
|
|
|
assert ex.value.response["ResponseMetadata"]["HTTPStatusCode"] == 412
|
2021-09-25 11:13:07 +00:00
|
|
|
err = ex.value.response["Error"]
|
2023-07-17 09:31:05 +00:00
|
|
|
assert err["Code"] == "DryRunOperation"
|
|
|
|
assert (
|
|
|
|
err["Message"]
|
|
|
|
== "An error occurred (DryRunOperation) when calling the DeregisterImage operation: Request would have succeeded, but DryRun flag is set"
|
2021-09-25 11:13:07 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
success = ec2.deregister_image(ImageId=image_id)
|
2023-07-17 09:31:05 +00:00
|
|
|
assert success["ResponseMetadata"]["HTTPStatusCode"] == 200
|
2021-09-25 11:13:07 +00:00
|
|
|
|
|
|
|
with pytest.raises(ClientError) as ex:
|
|
|
|
ec2.deregister_image(ImageId=image_id)
|
2023-07-17 09:31:05 +00:00
|
|
|
assert ex.value.response["ResponseMetadata"]["HTTPStatusCode"] == 400
|
2021-09-25 11:13:07 +00:00
|
|
|
err = ex.value.response["Error"]
|
2023-07-17 09:31:05 +00:00
|
|
|
assert err["Code"] == "InvalidAMIID.Unavailable"
|
|
|
|
assert ex.value.response["ResponseMetadata"]["RequestId"] is not None
|
2022-04-22 15:40:30 +00:00
|
|
|
|
|
|
|
|
2024-01-07 12:03:33 +00:00
|
|
|
@mock_aws
|
2022-04-22 15:40:30 +00:00
|
|
|
def test_deregister_image__unknown():
|
|
|
|
ec2 = boto3.client("ec2", region_name="us-east-1")
|
|
|
|
|
|
|
|
with pytest.raises(ClientError) as ex:
|
|
|
|
ec2.deregister_image(ImageId="ami-unknown-ami")
|
2023-07-17 09:31:05 +00:00
|
|
|
assert ex.value.response["ResponseMetadata"]["HTTPStatusCode"] == 400
|
2022-04-22 15:40:30 +00:00
|
|
|
err = ex.value.response["Error"]
|
2023-07-17 09:31:05 +00:00
|
|
|
assert err["Code"] == "InvalidAMIID.NotFound"
|
|
|
|
assert ex.value.response["ResponseMetadata"]["RequestId"] is not None
|
2021-09-25 11:13:07 +00:00
|
|
|
|
|
|
|
|
2024-01-07 12:03:33 +00:00
|
|
|
@mock_aws
|
2022-04-22 15:40:30 +00:00
|
|
|
def test_deregister_image__and_describe():
|
|
|
|
ec2 = boto3.client("ec2", region_name="us-east-1")
|
|
|
|
|
|
|
|
reservation = ec2.run_instances(ImageId=EXAMPLE_AMI_ID, MinCount=1, MaxCount=1)
|
|
|
|
instance = reservation["Instances"][0]
|
|
|
|
instance_id = instance["InstanceId"]
|
|
|
|
|
|
|
|
image_id = ec2.create_image(
|
|
|
|
InstanceId=instance_id, Name="test-ami", Description="this is a test ami"
|
|
|
|
)["ImageId"]
|
|
|
|
|
|
|
|
ec2.deregister_image(ImageId=image_id)
|
|
|
|
|
|
|
|
# Searching for a deleted image ID should not throw an error
|
|
|
|
# It should simply not return this image
|
2023-07-17 09:31:05 +00:00
|
|
|
assert len(ec2.describe_images(ImageIds=[image_id])["Images"]) == 0
|
2022-04-22 15:40:30 +00:00
|
|
|
|
|
|
|
|
2024-01-07 12:03:33 +00:00
|
|
|
@mock_aws
|
2022-04-18 20:44:56 +00:00
|
|
|
def test_ami_copy_dryrun():
|
2021-09-25 11:13:07 +00:00
|
|
|
ec2 = boto3.client("ec2", region_name="us-west-1")
|
|
|
|
|
|
|
|
reservation = ec2.run_instances(ImageId=EXAMPLE_AMI_ID, MinCount=1, MaxCount=1)
|
|
|
|
instance = reservation["Instances"][0]
|
|
|
|
|
|
|
|
source_image_id = ec2.create_image(
|
|
|
|
InstanceId=instance["InstanceId"],
|
|
|
|
Name="test-ami",
|
|
|
|
Description="this is a test ami",
|
|
|
|
)["ImageId"]
|
|
|
|
source_image = ec2.describe_images(ImageIds=[source_image_id])["Images"][0]
|
|
|
|
|
|
|
|
with pytest.raises(ClientError) as ex:
|
|
|
|
ec2.copy_image(
|
|
|
|
SourceRegion="us-west-1",
|
|
|
|
SourceImageId=source_image["ImageId"],
|
|
|
|
Name="test-copy-ami",
|
|
|
|
Description="this is a test copy ami",
|
|
|
|
DryRun=True,
|
|
|
|
)
|
2023-07-17 09:31:05 +00:00
|
|
|
assert ex.value.response["ResponseMetadata"]["HTTPStatusCode"] == 412
|
2021-09-25 11:13:07 +00:00
|
|
|
err = ex.value.response["Error"]
|
2023-07-17 09:31:05 +00:00
|
|
|
assert err["Code"] == "DryRunOperation"
|
|
|
|
assert (
|
|
|
|
err["Message"]
|
|
|
|
== "An error occurred (DryRunOperation) when calling the CopyImage operation: Request would have succeeded, but DryRun flag is set"
|
2021-09-25 11:13:07 +00:00
|
|
|
)
|
|
|
|
|
2021-10-05 17:11:07 +00:00
|
|
|
|
2023-09-03 14:59:09 +00:00
|
|
|
@mock.patch.dict(os.environ, {"MOTO_EC2_LOAD_DEFAULT_AMIS": "true"})
|
2024-01-07 12:03:33 +00:00
|
|
|
@mock_aws
|
2022-04-18 20:44:56 +00:00
|
|
|
def test_ami_copy():
|
2023-09-03 14:59:09 +00:00
|
|
|
if settings.TEST_SERVER_MODE:
|
|
|
|
raise SkipTest("Can't set environment variables in ServerMode")
|
2021-10-05 17:11:07 +00:00
|
|
|
ec2 = boto3.client("ec2", region_name="us-west-1")
|
|
|
|
|
|
|
|
reservation = ec2.run_instances(ImageId=EXAMPLE_AMI_ID, MinCount=1, MaxCount=1)
|
|
|
|
instance = reservation["Instances"][0]
|
|
|
|
|
|
|
|
source_image_id = ec2.create_image(
|
|
|
|
InstanceId=instance["InstanceId"],
|
|
|
|
Name="test-ami",
|
|
|
|
Description="this is a test ami",
|
|
|
|
)["ImageId"]
|
|
|
|
ec2.terminate_instances(InstanceIds=[instance["InstanceId"]])
|
|
|
|
source_image = ec2.describe_images(ImageIds=[source_image_id])["Images"][0]
|
|
|
|
|
2021-09-25 11:13:07 +00:00
|
|
|
copy_image_ref = ec2.copy_image(
|
|
|
|
SourceRegion="us-west-1",
|
|
|
|
SourceImageId=source_image["ImageId"],
|
|
|
|
Name="test-copy-ami",
|
|
|
|
Description="this is a test copy ami",
|
|
|
|
)
|
|
|
|
copy_image_id = copy_image_ref["ImageId"]
|
|
|
|
copy_image = ec2.describe_images(ImageIds=[copy_image_id])["Images"][0]
|
|
|
|
|
2023-07-17 09:31:05 +00:00
|
|
|
assert copy_image["Name"] == "test-copy-ami"
|
|
|
|
assert copy_image["Description"] == "this is a test copy ami"
|
|
|
|
assert copy_image["ImageId"] == copy_image_id
|
|
|
|
assert copy_image["VirtualizationType"] == source_image["VirtualizationType"]
|
|
|
|
assert copy_image["Architecture"] == source_image["Architecture"]
|
|
|
|
assert copy_image["KernelId"] == source_image["KernelId"]
|
|
|
|
assert copy_image["Platform"] == source_image["Platform"]
|
2021-09-25 11:13:07 +00:00
|
|
|
|
|
|
|
# Validate auto-created snapshot
|
2021-10-05 17:11:07 +00:00
|
|
|
source_image_snapshot_id = source_image["BlockDeviceMappings"][0]["Ebs"][
|
|
|
|
"SnapshotId"
|
|
|
|
]
|
|
|
|
copied_image_snapshot_id = copy_image["BlockDeviceMappings"][0]["Ebs"]["SnapshotId"]
|
2021-09-25 11:13:07 +00:00
|
|
|
|
2021-10-05 17:11:07 +00:00
|
|
|
snapshot_ids = [s["SnapshotId"] for s in ec2.describe_snapshots()["Snapshots"]]
|
2023-07-17 09:31:05 +00:00
|
|
|
assert source_image_snapshot_id in snapshot_ids
|
|
|
|
assert copied_image_snapshot_id in snapshot_ids
|
2021-10-05 17:11:07 +00:00
|
|
|
|
2023-07-17 09:31:05 +00:00
|
|
|
assert copied_image_snapshot_id != source_image_snapshot_id
|
2021-10-05 17:11:07 +00:00
|
|
|
|
|
|
|
|
2024-01-07 12:03:33 +00:00
|
|
|
@mock_aws
|
2021-10-05 17:11:07 +00:00
|
|
|
def test_ami_copy_nonexistent_source_id():
|
|
|
|
ec2 = boto3.client("ec2", region_name="us-west-1")
|
2021-09-25 11:13:07 +00:00
|
|
|
|
|
|
|
# Copy from non-existent source ID.
|
|
|
|
with pytest.raises(ClientError) as ex:
|
|
|
|
ec2.copy_image(
|
|
|
|
SourceRegion="us-west-1",
|
|
|
|
SourceImageId="ami-abcd1234",
|
|
|
|
Name="test-copy-ami",
|
|
|
|
Description="this is a test copy ami",
|
|
|
|
)
|
2023-07-17 09:31:05 +00:00
|
|
|
assert ex.value.response["ResponseMetadata"]["HTTPStatusCode"] == 400
|
|
|
|
assert "RequestId" in ex.value.response["ResponseMetadata"]
|
|
|
|
assert ex.value.response["Error"]["Code"] == "InvalidAMIID.NotFound"
|
2021-09-25 11:13:07 +00:00
|
|
|
|
2021-10-05 17:11:07 +00:00
|
|
|
|
2024-01-07 12:03:33 +00:00
|
|
|
@mock_aws
|
2021-10-05 17:11:07 +00:00
|
|
|
def test_ami_copy_nonexisting_source_region():
|
|
|
|
ec2 = boto3.client("ec2", region_name="us-west-1")
|
|
|
|
|
|
|
|
reservation = ec2.run_instances(ImageId=EXAMPLE_AMI_ID, MinCount=1, MaxCount=1)
|
|
|
|
instance = reservation["Instances"][0]
|
|
|
|
|
|
|
|
source_image_id = ec2.create_image(
|
|
|
|
InstanceId=instance["InstanceId"],
|
|
|
|
Name="test-ami",
|
|
|
|
Description="this is a test ami",
|
|
|
|
)["ImageId"]
|
|
|
|
source_image = ec2.describe_images(ImageIds=[source_image_id])["Images"][0]
|
|
|
|
|
2021-09-25 11:13:07 +00:00
|
|
|
# Copy from non-existent source region.
|
|
|
|
with pytest.raises(ClientError) as ex:
|
|
|
|
ec2.copy_image(
|
|
|
|
SourceRegion="us-east-1",
|
|
|
|
SourceImageId=source_image["ImageId"],
|
|
|
|
Name="test-copy-ami",
|
|
|
|
Description="this is a test copy ami",
|
|
|
|
)
|
2023-07-17 09:31:05 +00:00
|
|
|
assert ex.value.response["ResponseMetadata"]["HTTPStatusCode"] == 400
|
|
|
|
assert "RequestId" in ex.value.response["ResponseMetadata"]
|
|
|
|
assert ex.value.response["Error"]["Code"] == "InvalidAMIID.NotFound"
|
2021-09-25 11:13:07 +00:00
|
|
|
|
|
|
|
|
2023-09-03 14:59:09 +00:00
|
|
|
@mock.patch.dict(os.environ, {"MOTO_EC2_LOAD_DEFAULT_AMIS": "true"})
|
2024-01-07 12:03:33 +00:00
|
|
|
@mock_aws
|
2019-07-23 02:57:15 +00:00
|
|
|
def test_copy_image_changes_owner_id():
|
2023-09-03 14:59:09 +00:00
|
|
|
if settings.TEST_SERVER_MODE:
|
|
|
|
raise SkipTest("Can't set environment variables in ServerMode")
|
2019-07-23 02:57:15 +00:00
|
|
|
conn = boto3.client("ec2", region_name="us-east-1")
|
|
|
|
|
|
|
|
# this source AMI ID is from moto/ec2/resources/amis.json
|
|
|
|
source_ami_id = "ami-03cf127a"
|
|
|
|
|
|
|
|
# confirm the source ami owner id is different from the default owner id.
|
|
|
|
# if they're ever the same it means this test is invalid.
|
|
|
|
check_resp = conn.describe_images(ImageIds=[source_ami_id])
|
2023-07-17 09:31:05 +00:00
|
|
|
assert check_resp["Images"][0]["OwnerId"] != ACCOUNT_ID
|
2019-07-23 02:57:15 +00:00
|
|
|
|
2021-10-05 17:11:07 +00:00
|
|
|
new_image_name = str(uuid4())[0:6]
|
|
|
|
|
2019-07-23 02:57:15 +00:00
|
|
|
copy_resp = conn.copy_image(
|
|
|
|
SourceImageId=source_ami_id,
|
2021-10-05 17:11:07 +00:00
|
|
|
Name=new_image_name,
|
2019-07-23 02:57:15 +00:00
|
|
|
Description="a copy of an image",
|
|
|
|
SourceRegion="us-east-1",
|
|
|
|
)
|
|
|
|
|
2021-10-05 17:11:07 +00:00
|
|
|
describe_resp = conn.describe_images(
|
|
|
|
Owners=["self"], Filters=[{"Name": "name", "Values": [new_image_name]}]
|
|
|
|
)["Images"]
|
2023-07-17 09:31:05 +00:00
|
|
|
assert len(describe_resp) == 1
|
|
|
|
assert describe_resp[0]["OwnerId"] == ACCOUNT_ID
|
|
|
|
assert describe_resp[0]["ImageId"] == copy_resp["ImageId"]
|
2019-07-23 02:57:15 +00:00
|
|
|
|
|
|
|
|
2024-01-07 12:03:33 +00:00
|
|
|
@mock_aws
|
2022-04-18 20:44:56 +00:00
|
|
|
def test_ami_tagging():
|
2021-09-25 11:13:07 +00:00
|
|
|
ec2 = boto3.client("ec2", region_name="us-east-1")
|
|
|
|
res = boto3.resource("ec2", region_name="us-east-1")
|
|
|
|
reservation = ec2.run_instances(ImageId=EXAMPLE_AMI_ID, MinCount=1, MaxCount=1)
|
|
|
|
instance = reservation["Instances"][0]
|
|
|
|
image_id = ec2.create_image(
|
|
|
|
InstanceId=instance["InstanceId"],
|
|
|
|
Name="test-ami",
|
|
|
|
Description="this is a test ami",
|
|
|
|
)["ImageId"]
|
|
|
|
image = res.Image(image_id)
|
|
|
|
|
|
|
|
with pytest.raises(ClientError) as ex:
|
|
|
|
image.create_tags(Tags=[{"Key": "a key", "Value": "some value"}], DryRun=True)
|
2023-07-17 09:31:05 +00:00
|
|
|
assert ex.value.response["ResponseMetadata"]["HTTPStatusCode"] == 412
|
2021-09-25 11:13:07 +00:00
|
|
|
err = ex.value.response["Error"]
|
2023-07-17 09:31:05 +00:00
|
|
|
assert err["Code"] == "DryRunOperation"
|
|
|
|
assert (
|
|
|
|
err["Message"]
|
|
|
|
== "An error occurred (DryRunOperation) when calling the CreateTags operation: Request would have succeeded, but DryRun flag is set"
|
2021-09-25 11:13:07 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
image.create_tags(Tags=[{"Key": "a key", "Value": "some value"}])
|
2023-07-17 09:31:05 +00:00
|
|
|
assert image.tags == [{"Value": "some value", "Key": "a key"}]
|
2021-09-25 11:13:07 +00:00
|
|
|
|
|
|
|
image = ec2.describe_images(ImageIds=[image_id])["Images"][0]
|
2023-07-17 09:31:05 +00:00
|
|
|
assert image["Tags"] == [{"Value": "some value", "Key": "a key"}]
|
2021-09-25 11:13:07 +00:00
|
|
|
|
|
|
|
|
2024-01-07 12:03:33 +00:00
|
|
|
@mock_aws
|
2022-04-18 20:44:56 +00:00
|
|
|
def test_ami_create_from_missing_instance():
|
2021-09-25 11:13:07 +00:00
|
|
|
ec2 = boto3.client("ec2", region_name="us-east-1")
|
|
|
|
|
|
|
|
with pytest.raises(ClientError) as ex:
|
|
|
|
ec2.create_image(
|
|
|
|
InstanceId="i-abcdefg", Name="test-ami", Description="this is a test ami"
|
|
|
|
)
|
2023-07-17 09:31:05 +00:00
|
|
|
assert ex.value.response["ResponseMetadata"]["HTTPStatusCode"] == 400
|
|
|
|
assert "RequestId" in ex.value.response["ResponseMetadata"]
|
|
|
|
assert ex.value.response["Error"]["Code"] == "InvalidInstanceID.NotFound"
|
2021-09-25 11:13:07 +00:00
|
|
|
|
|
|
|
|
2024-01-07 12:03:33 +00:00
|
|
|
@mock_aws
|
2022-04-18 20:44:56 +00:00
|
|
|
def test_ami_pulls_attributes_from_instance():
|
2021-09-25 11:13:07 +00:00
|
|
|
ec2 = boto3.client("ec2", region_name="us-east-1")
|
|
|
|
reservation = ec2.run_instances(ImageId=EXAMPLE_AMI_ID, MinCount=1, MaxCount=1)
|
|
|
|
instance = reservation["Instances"][0]
|
|
|
|
ec2.modify_instance_attribute(
|
|
|
|
InstanceId=instance["InstanceId"], Kernel={"Value": "test-kernel"}
|
|
|
|
)
|
|
|
|
|
|
|
|
image_id = ec2.create_image(InstanceId=instance["InstanceId"], Name="test-ami")[
|
|
|
|
"ImageId"
|
|
|
|
]
|
|
|
|
image = boto3.resource("ec2", region_name="us-east-1").Image(image_id)
|
2023-07-17 09:31:05 +00:00
|
|
|
assert image.kernel_id == "test-kernel"
|
2021-09-25 11:13:07 +00:00
|
|
|
|
|
|
|
|
2024-01-07 12:03:33 +00:00
|
|
|
@mock_aws
|
2022-04-18 20:44:56 +00:00
|
|
|
def test_ami_uses_account_id_if_valid_access_key_is_supplied():
|
2021-09-25 11:13:07 +00:00
|
|
|
# The boto-equivalent required an access_key to be passed in, but Moto will always mock this in boto3
|
|
|
|
# So the only thing we're testing here, really.. is whether OwnerId is equal to ACCOUNT_ID?
|
|
|
|
# TODO: Maybe patch account_id with multiple values, and verify it always matches with OwnerId
|
|
|
|
ec2 = boto3.client("ec2", region_name="us-east-1")
|
|
|
|
reservation = ec2.run_instances(ImageId=EXAMPLE_AMI_ID, MinCount=1, MaxCount=1)
|
|
|
|
instance = reservation["Instances"][0]
|
|
|
|
|
|
|
|
image_id = ec2.create_image(InstanceId=instance["InstanceId"], Name="test-ami")[
|
|
|
|
"ImageId"
|
|
|
|
]
|
|
|
|
images = ec2.describe_images(Owners=["self"])["Images"]
|
2023-07-17 09:31:05 +00:00
|
|
|
assert (image_id, ACCOUNT_ID) in [
|
|
|
|
(ami["ImageId"], ami["OwnerId"]) for ami in images
|
|
|
|
]
|
2021-09-25 11:13:07 +00:00
|
|
|
|
|
|
|
|
2023-09-03 14:59:09 +00:00
|
|
|
@mock.patch.dict(os.environ, {"MOTO_EC2_LOAD_DEFAULT_AMIS": "true"})
|
2024-01-07 12:03:33 +00:00
|
|
|
@mock_aws
|
2022-04-18 20:44:56 +00:00
|
|
|
def test_ami_filters():
|
2023-09-03 14:59:09 +00:00
|
|
|
if settings.TEST_SERVER_MODE:
|
|
|
|
raise SkipTest("Can't set environment variables in ServerMode")
|
2021-10-05 17:11:07 +00:00
|
|
|
image_name_A = f"test-ami-{str(uuid4())[0:6]}"
|
|
|
|
kernel_value_A = f"k-{str(uuid4())[0:6]}"
|
|
|
|
kernel_value_B = f"k-{str(uuid4())[0:6]}"
|
|
|
|
|
2021-09-25 11:13:07 +00:00
|
|
|
ec2 = boto3.client("ec2", region_name="us-east-1")
|
|
|
|
reservationA = ec2.run_instances(ImageId=EXAMPLE_AMI_ID, MinCount=1, MaxCount=1)
|
|
|
|
instanceA = reservationA["Instances"][0]
|
|
|
|
ec2.modify_instance_attribute(
|
2021-10-05 17:11:07 +00:00
|
|
|
InstanceId=instanceA["InstanceId"], Kernel={"Value": kernel_value_A}
|
2021-09-25 11:13:07 +00:00
|
|
|
)
|
|
|
|
|
2021-10-05 17:11:07 +00:00
|
|
|
imageA_id = ec2.create_image(InstanceId=instanceA["InstanceId"], Name=image_name_A)[
|
2021-09-25 11:13:07 +00:00
|
|
|
"ImageId"
|
|
|
|
]
|
|
|
|
imageA = boto3.resource("ec2", region_name="us-east-1").Image(imageA_id)
|
|
|
|
|
|
|
|
reservationB = ec2.run_instances(
|
|
|
|
ImageId=EXAMPLE_AMI_PARAVIRTUAL, MinCount=1, MaxCount=1
|
|
|
|
)
|
|
|
|
instanceB = reservationB["Instances"][0]
|
|
|
|
ec2.modify_instance_attribute(
|
2021-10-05 17:11:07 +00:00
|
|
|
InstanceId=instanceB["InstanceId"], Kernel={"Value": kernel_value_B}
|
2021-09-25 11:13:07 +00:00
|
|
|
)
|
|
|
|
imageB_id = ec2.create_image(InstanceId=instanceB["InstanceId"], Name="test-ami-B")[
|
|
|
|
"ImageId"
|
|
|
|
]
|
|
|
|
imageB = boto3.resource("ec2", region_name="us-east-1").Image(imageB_id)
|
|
|
|
imageB.modify_attribute(LaunchPermission={"Add": [{"Group": "all"}]})
|
2024-02-08 22:21:42 +00:00
|
|
|
assert imageB.describe_attribute(Attribute="launchPermission")[
|
|
|
|
"LaunchPermissions"
|
|
|
|
] == [{"Group": "all"}]
|
2021-09-25 11:13:07 +00:00
|
|
|
|
|
|
|
amis_by_architecture = ec2.describe_images(
|
|
|
|
Filters=[{"Name": "architecture", "Values": ["x86_64"]}]
|
|
|
|
)["Images"]
|
2023-07-17 09:31:05 +00:00
|
|
|
assert imageB_id in [ami["ImageId"] for ami in amis_by_architecture]
|
2021-10-05 17:11:07 +00:00
|
|
|
assert (
|
|
|
|
len(amis_by_architecture) >= 40
|
|
|
|
), "Should have at least 40 AMI's of type x86_64"
|
2021-09-25 11:13:07 +00:00
|
|
|
|
|
|
|
amis_by_kernel = ec2.describe_images(
|
2021-10-05 17:11:07 +00:00
|
|
|
Filters=[{"Name": "kernel-id", "Values": [kernel_value_B]}]
|
2021-09-25 11:13:07 +00:00
|
|
|
)["Images"]
|
2023-07-17 09:31:05 +00:00
|
|
|
assert [ami["ImageId"] for ami in amis_by_kernel] == [imageB.id]
|
2021-09-25 11:13:07 +00:00
|
|
|
|
|
|
|
amis_by_virtualization = ec2.describe_images(
|
|
|
|
Filters=[{"Name": "virtualization-type", "Values": ["paravirtual"]}]
|
|
|
|
)["Images"]
|
2023-07-17 09:31:05 +00:00
|
|
|
assert imageB.id in [ami["ImageId"] for ami in amis_by_virtualization]
|
2021-10-05 17:11:07 +00:00
|
|
|
assert len(amis_by_virtualization) >= 3, "Should have at least 3 paravirtual AMI's"
|
2021-09-25 11:13:07 +00:00
|
|
|
|
|
|
|
amis_by_platform = ec2.describe_images(
|
|
|
|
Filters=[{"Name": "platform", "Values": ["windows"]}]
|
|
|
|
)["Images"]
|
2023-07-17 09:31:05 +00:00
|
|
|
assert imageA_id in [ami["ImageId"] for ami in amis_by_platform]
|
2021-10-05 17:11:07 +00:00
|
|
|
assert len(amis_by_platform) >= 25, "Should have at least 25 Windows images"
|
2021-09-25 11:13:07 +00:00
|
|
|
|
|
|
|
amis_by_id = ec2.describe_images(
|
|
|
|
Filters=[{"Name": "image-id", "Values": [imageA_id]}]
|
|
|
|
)["Images"]
|
2023-07-17 09:31:05 +00:00
|
|
|
assert [ami["ImageId"] for ami in amis_by_id] == [imageA_id]
|
2021-09-25 11:13:07 +00:00
|
|
|
|
|
|
|
amis_by_state = ec2.describe_images(
|
|
|
|
Filters=[{"Name": "state", "Values": ["available"]}]
|
|
|
|
)["Images"]
|
|
|
|
ami_ids_by_state = [ami["ImageId"] for ami in amis_by_state]
|
2023-07-17 09:31:05 +00:00
|
|
|
assert imageA_id in ami_ids_by_state
|
|
|
|
assert imageB.id in ami_ids_by_state
|
2021-10-05 17:11:07 +00:00
|
|
|
assert len(amis_by_state) >= 40, "Should have at least 40 images available"
|
2021-09-25 11:13:07 +00:00
|
|
|
|
|
|
|
amis_by_name = ec2.describe_images(
|
|
|
|
Filters=[{"Name": "name", "Values": [imageA.name]}]
|
|
|
|
)["Images"]
|
2023-07-17 09:31:05 +00:00
|
|
|
assert [ami["ImageId"] for ami in amis_by_name] == [imageA.id]
|
2021-09-25 11:13:07 +00:00
|
|
|
|
|
|
|
amis_by_public = ec2.describe_images(
|
|
|
|
Filters=[{"Name": "is-public", "Values": ["true"]}]
|
|
|
|
)["Images"]
|
2021-10-05 17:11:07 +00:00
|
|
|
assert len(amis_by_public) >= 38, "Should have at least 38 public images"
|
2021-09-25 11:13:07 +00:00
|
|
|
|
|
|
|
amis_by_nonpublic = ec2.describe_images(
|
|
|
|
Filters=[{"Name": "is-public", "Values": ["false"]}]
|
|
|
|
)["Images"]
|
2023-07-17 09:31:05 +00:00
|
|
|
assert imageA.id in [ami["ImageId"] for ami in amis_by_nonpublic]
|
2021-09-25 11:13:07 +00:00
|
|
|
|
|
|
|
|
2024-01-07 12:03:33 +00:00
|
|
|
@mock_aws
|
2022-04-18 20:44:56 +00:00
|
|
|
def test_ami_filtering_via_tag():
|
2021-10-05 17:11:07 +00:00
|
|
|
tag_value = f"value {str(uuid4())}"
|
|
|
|
other_value = f"value {str(uuid4())}"
|
2021-09-25 11:13:07 +00:00
|
|
|
ec2 = boto3.client("ec2", region_name="us-east-1")
|
|
|
|
reservationA = ec2.run_instances(ImageId=EXAMPLE_AMI_ID, MinCount=1, MaxCount=1)
|
|
|
|
instanceA = reservationA["Instances"][0]
|
|
|
|
|
|
|
|
imageA_id = ec2.create_image(InstanceId=instanceA["InstanceId"], Name="test-ami-A")[
|
|
|
|
"ImageId"
|
|
|
|
]
|
|
|
|
imageA = boto3.resource("ec2", region_name="us-east-1").Image(imageA_id)
|
2021-10-05 17:11:07 +00:00
|
|
|
imageA.create_tags(Tags=[{"Key": "a key", "Value": tag_value}])
|
2021-09-25 11:13:07 +00:00
|
|
|
|
|
|
|
reservationB = ec2.run_instances(ImageId=EXAMPLE_AMI_ID, MinCount=1, MaxCount=1)
|
|
|
|
instanceB = reservationB["Instances"][0]
|
|
|
|
imageB_id = ec2.create_image(InstanceId=instanceB["InstanceId"], Name="test-ami-B")[
|
|
|
|
"ImageId"
|
|
|
|
]
|
|
|
|
imageB = boto3.resource("ec2", region_name="us-east-1").Image(imageB_id)
|
2021-10-05 17:11:07 +00:00
|
|
|
imageB.create_tags(Tags=[{"Key": "another key", "Value": other_value}])
|
2021-09-25 11:13:07 +00:00
|
|
|
|
|
|
|
amis_by_tagA = ec2.describe_images(
|
2021-10-05 17:11:07 +00:00
|
|
|
Filters=[{"Name": "tag:a key", "Values": [tag_value]}]
|
2021-09-25 11:13:07 +00:00
|
|
|
)["Images"]
|
2023-07-17 09:31:05 +00:00
|
|
|
assert [ami["ImageId"] for ami in amis_by_tagA] == [imageA_id]
|
2021-09-25 11:13:07 +00:00
|
|
|
|
|
|
|
amis_by_tagB = ec2.describe_images(
|
2021-10-05 17:11:07 +00:00
|
|
|
Filters=[{"Name": "tag:another key", "Values": [other_value]}]
|
2021-09-25 11:13:07 +00:00
|
|
|
)["Images"]
|
2023-07-17 09:31:05 +00:00
|
|
|
assert [ami["ImageId"] for ami in amis_by_tagB] == [imageB_id]
|
2021-09-25 11:13:07 +00:00
|
|
|
|
|
|
|
|
2024-01-07 12:03:33 +00:00
|
|
|
@mock_aws
|
2022-04-18 20:44:56 +00:00
|
|
|
def test_getting_missing_ami():
|
2021-09-25 11:13:07 +00:00
|
|
|
ec2 = boto3.resource("ec2", region_name="us-east-1")
|
|
|
|
|
|
|
|
with pytest.raises(ClientError) as ex:
|
|
|
|
ec2.Image("ami-missing").load()
|
2023-07-17 09:31:05 +00:00
|
|
|
assert ex.value.response["ResponseMetadata"]["HTTPStatusCode"] == 400
|
|
|
|
assert "RequestId" in ex.value.response["ResponseMetadata"]
|
|
|
|
assert ex.value.response["Error"]["Code"] == "InvalidAMIID.NotFound"
|
2021-09-25 11:13:07 +00:00
|
|
|
|
|
|
|
|
2024-01-07 12:03:33 +00:00
|
|
|
@mock_aws
|
2022-04-18 20:44:56 +00:00
|
|
|
def test_getting_malformed_ami():
|
2021-09-25 11:13:07 +00:00
|
|
|
ec2 = boto3.resource("ec2", region_name="us-east-1")
|
|
|
|
|
|
|
|
with pytest.raises(ClientError) as ex:
|
|
|
|
ec2.Image("foo-missing").load()
|
2023-07-17 09:31:05 +00:00
|
|
|
assert ex.value.response["ResponseMetadata"]["HTTPStatusCode"] == 400
|
|
|
|
assert "RequestId" in ex.value.response["ResponseMetadata"]
|
|
|
|
assert ex.value.response["Error"]["Code"] == "InvalidAMIID.Malformed"
|
2021-09-25 11:13:07 +00:00
|
|
|
|
|
|
|
|
2024-01-07 12:03:33 +00:00
|
|
|
@mock_aws
|
2022-04-18 20:44:56 +00:00
|
|
|
def test_ami_attribute_group_permissions():
|
2021-09-25 11:13:07 +00:00
|
|
|
ec2 = boto3.client("ec2", region_name="us-east-1")
|
|
|
|
reservation = ec2.run_instances(ImageId=EXAMPLE_AMI_ID, MinCount=1, MaxCount=1)
|
|
|
|
instance = reservation["Instances"][0]
|
|
|
|
|
|
|
|
image_id = ec2.create_image(InstanceId=instance["InstanceId"], Name="test-ami-A")[
|
|
|
|
"ImageId"
|
|
|
|
]
|
|
|
|
image = boto3.resource("ec2", region_name="us-east-1").Image(image_id)
|
|
|
|
|
|
|
|
# Baseline
|
|
|
|
permissions = image.describe_attribute(Attribute="launchPermission")[
|
|
|
|
"LaunchPermissions"
|
|
|
|
]
|
2023-07-17 09:31:05 +00:00
|
|
|
assert permissions == []
|
2021-09-25 11:13:07 +00:00
|
|
|
|
|
|
|
ADD_GROUP_ARGS = {
|
|
|
|
"ImageId": image_id,
|
|
|
|
"Attribute": "launchPermission",
|
|
|
|
"OperationType": "add",
|
|
|
|
"UserGroups": ["all"],
|
|
|
|
}
|
|
|
|
|
|
|
|
REMOVE_GROUP_ARGS = {
|
|
|
|
"ImageId": image_id,
|
|
|
|
"Attribute": "launchPermission",
|
|
|
|
"OperationType": "remove",
|
|
|
|
"UserGroups": ["all"],
|
|
|
|
}
|
|
|
|
|
|
|
|
# Add 'all' group and confirm
|
|
|
|
with pytest.raises(ClientError) as ex:
|
|
|
|
image.modify_attribute(DryRun=True)
|
2023-07-17 09:31:05 +00:00
|
|
|
assert ex.value.response["ResponseMetadata"]["HTTPStatusCode"] == 412
|
|
|
|
assert ex.value.response["Error"]["Code"] == "DryRunOperation"
|
|
|
|
assert (
|
|
|
|
ex.value.response["Error"]["Message"]
|
|
|
|
== "An error occurred (DryRunOperation) when calling the ModifyImageAttribute operation: Request would have succeeded, but DryRun flag is set"
|
2021-09-25 11:13:07 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
image.modify_attribute(**ADD_GROUP_ARGS)
|
|
|
|
|
|
|
|
permissions = image.describe_attribute(Attribute="launchPermission")[
|
|
|
|
"LaunchPermissions"
|
|
|
|
]
|
2023-07-17 09:31:05 +00:00
|
|
|
assert permissions == [{"Group": "all"}]
|
2021-09-25 11:13:07 +00:00
|
|
|
image.reload()
|
2023-07-17 09:31:05 +00:00
|
|
|
assert image.public is True
|
2021-09-25 11:13:07 +00:00
|
|
|
|
|
|
|
# Add is idempotent
|
|
|
|
image.modify_attribute(**ADD_GROUP_ARGS)
|
|
|
|
|
|
|
|
# Remove 'all' group and confirm
|
|
|
|
image.modify_attribute(**REMOVE_GROUP_ARGS)
|
|
|
|
|
|
|
|
permissions = image.describe_attribute(Attribute="launchPermission")[
|
|
|
|
"LaunchPermissions"
|
|
|
|
]
|
2023-07-17 09:31:05 +00:00
|
|
|
assert permissions == []
|
2021-09-25 11:13:07 +00:00
|
|
|
image.reload()
|
2023-07-17 09:31:05 +00:00
|
|
|
assert image.public is False
|
2021-09-25 11:13:07 +00:00
|
|
|
|
|
|
|
# Remove is idempotent
|
|
|
|
image.modify_attribute(**REMOVE_GROUP_ARGS)
|
|
|
|
|
|
|
|
|
2024-01-07 12:03:33 +00:00
|
|
|
@mock_aws
|
2022-04-18 20:44:56 +00:00
|
|
|
def test_ami_attribute_user_permissions():
|
2021-09-25 11:13:07 +00:00
|
|
|
ec2 = boto3.client("ec2", region_name="us-east-1")
|
|
|
|
reservation = ec2.run_instances(ImageId=EXAMPLE_AMI_ID, MinCount=1, MaxCount=1)
|
|
|
|
instance = reservation["Instances"][0]
|
|
|
|
|
|
|
|
image_id = ec2.create_image(InstanceId=instance["InstanceId"], Name="test-ami-A")[
|
|
|
|
"ImageId"
|
|
|
|
]
|
|
|
|
image = boto3.resource("ec2", region_name="us-east-1").Image(image_id)
|
|
|
|
|
|
|
|
# Baseline
|
|
|
|
permissions = image.describe_attribute(Attribute="launchPermission")[
|
|
|
|
"LaunchPermissions"
|
|
|
|
]
|
2023-07-17 09:31:05 +00:00
|
|
|
assert permissions == []
|
2021-09-25 11:13:07 +00:00
|
|
|
|
2022-11-17 22:41:08 +00:00
|
|
|
USER1 = "".join([f"{random.randint(0, 9)}" for _ in range(0, 12)])
|
|
|
|
USER2 = "".join([f"{random.randint(0, 9)}" for _ in range(0, 12)])
|
2021-09-25 11:13:07 +00:00
|
|
|
|
|
|
|
ADD_USERS_ARGS = {
|
|
|
|
"ImageId": image.id,
|
|
|
|
"Attribute": "launchPermission",
|
|
|
|
"OperationType": "add",
|
|
|
|
"UserIds": [USER1, USER2],
|
|
|
|
}
|
|
|
|
|
|
|
|
REMOVE_USERS_ARGS = {
|
|
|
|
"ImageId": image.id,
|
|
|
|
"Attribute": "launchPermission",
|
|
|
|
"OperationType": "remove",
|
|
|
|
"UserIds": [USER1, USER2],
|
|
|
|
}
|
|
|
|
|
|
|
|
REMOVE_SINGLE_USER_ARGS = {
|
|
|
|
"ImageId": image.id,
|
|
|
|
"Attribute": "launchPermission",
|
|
|
|
"OperationType": "remove",
|
|
|
|
"UserIds": [USER1],
|
|
|
|
}
|
|
|
|
|
|
|
|
# Add multiple users and confirm
|
|
|
|
image.modify_attribute(**ADD_USERS_ARGS)
|
|
|
|
|
|
|
|
permissions = image.describe_attribute(Attribute="launchPermission")[
|
|
|
|
"LaunchPermissions"
|
|
|
|
]
|
2023-07-17 09:31:05 +00:00
|
|
|
assert len(permissions) == 2
|
|
|
|
assert {"UserId": USER1} in permissions
|
|
|
|
assert {"UserId": USER2} in permissions
|
2021-09-25 11:13:07 +00:00
|
|
|
image.reload()
|
2023-07-17 09:31:05 +00:00
|
|
|
assert image.public is False
|
2021-09-25 11:13:07 +00:00
|
|
|
|
|
|
|
# Add is idempotent
|
|
|
|
image.modify_attribute(**ADD_USERS_ARGS)
|
|
|
|
|
|
|
|
# Remove single user and confirm
|
|
|
|
image.modify_attribute(**REMOVE_SINGLE_USER_ARGS)
|
|
|
|
|
|
|
|
permissions = image.describe_attribute(Attribute="launchPermission")[
|
|
|
|
"LaunchPermissions"
|
|
|
|
]
|
2023-07-17 09:31:05 +00:00
|
|
|
assert permissions == [{"UserId": USER2}]
|
2021-09-25 11:13:07 +00:00
|
|
|
image.reload()
|
2023-07-17 09:31:05 +00:00
|
|
|
assert image.public is False
|
2021-09-25 11:13:07 +00:00
|
|
|
|
|
|
|
# Remove multiple users and confirm
|
|
|
|
image.modify_attribute(**REMOVE_USERS_ARGS)
|
|
|
|
|
|
|
|
permissions = image.describe_attribute(Attribute="launchPermission")[
|
|
|
|
"LaunchPermissions"
|
|
|
|
]
|
2023-07-17 09:31:05 +00:00
|
|
|
assert permissions == []
|
2021-09-25 11:13:07 +00:00
|
|
|
image.reload()
|
2023-07-17 09:31:05 +00:00
|
|
|
assert image.public is False
|
2021-09-25 11:13:07 +00:00
|
|
|
|
|
|
|
# Remove is idempotent
|
|
|
|
image.modify_attribute(**REMOVE_USERS_ARGS)
|
|
|
|
|
|
|
|
|
2024-02-08 22:21:42 +00:00
|
|
|
@mock_aws
|
|
|
|
def test_ami_attribute_organizations():
|
|
|
|
ec2 = boto3.client("ec2", region_name="us-east-1")
|
|
|
|
reservation = ec2.run_instances(ImageId=EXAMPLE_AMI_ID, MinCount=1, MaxCount=1)
|
|
|
|
instance_id = reservation["Instances"][0]["InstanceId"]
|
|
|
|
image_id = ec2.create_image(InstanceId=instance_id, Name="test-ami-A")["ImageId"]
|
|
|
|
image = boto3.resource("ec2", "us-east-1").Image(image_id)
|
|
|
|
arn = "someOrganizationArn"
|
|
|
|
image.modify_attribute(
|
|
|
|
Attribute="launchPermission",
|
|
|
|
OperationType="add",
|
|
|
|
OrganizationArns=[arn],
|
|
|
|
)
|
|
|
|
image.modify_attribute(
|
|
|
|
Attribute="launchPermission",
|
|
|
|
OperationType="add",
|
|
|
|
OrganizationalUnitArns=["ou1"],
|
|
|
|
)
|
|
|
|
|
|
|
|
ec2.modify_image_attribute(
|
|
|
|
Attribute="launchPermission",
|
|
|
|
ImageId=image_id,
|
|
|
|
LaunchPermission={
|
|
|
|
"Add": [
|
|
|
|
{"UserId": "111122223333"},
|
|
|
|
{"UserId": "555566667777"},
|
|
|
|
{"Group": "all"},
|
|
|
|
{"OrganizationArn": "orgarn"},
|
|
|
|
{"OrganizationalUnitArn": "ou2"},
|
|
|
|
]
|
|
|
|
},
|
|
|
|
)
|
|
|
|
|
|
|
|
launch_permissions = image.describe_attribute(Attribute="launchPermission")[
|
|
|
|
"LaunchPermissions"
|
|
|
|
]
|
|
|
|
assert launch_permissions == [
|
|
|
|
{"OrganizationArn": "someOrganizationArn"},
|
|
|
|
{"OrganizationalUnitArn": "ou1"},
|
|
|
|
{"UserId": "111122223333"},
|
|
|
|
{"UserId": "555566667777"},
|
|
|
|
{"Group": "all"},
|
|
|
|
{"OrganizationArn": "orgarn"},
|
|
|
|
{"OrganizationalUnitArn": "ou2"},
|
|
|
|
]
|
|
|
|
|
|
|
|
|
2024-01-07 12:03:33 +00:00
|
|
|
@mock_aws
|
2017-05-11 01:58:42 +00:00
|
|
|
def test_ami_describe_executable_users():
|
|
|
|
conn = boto3.client("ec2", region_name="us-east-1")
|
|
|
|
ec2 = boto3.resource("ec2", "us-east-1")
|
2021-01-13 09:02:11 +00:00
|
|
|
ec2.create_instances(ImageId=EXAMPLE_AMI_ID, MinCount=1, MaxCount=1)
|
2017-12-05 21:53:30 +00:00
|
|
|
response = conn.describe_instances(
|
|
|
|
Filters=[{"Name": "instance-state-name", "Values": ["running"]}]
|
|
|
|
)
|
2017-05-11 01:58:42 +00:00
|
|
|
instance_id = response["Reservations"][0]["Instances"][0]["InstanceId"]
|
|
|
|
image_id = conn.create_image(InstanceId=instance_id, Name="TestImage")["ImageId"]
|
2019-10-31 15:44:26 +00:00
|
|
|
|
2022-11-17 22:41:08 +00:00
|
|
|
USER1 = "".join([f"{random.randint(0, 9)}" for _ in range(0, 12)])
|
2019-10-31 15:44:26 +00:00
|
|
|
|
2017-05-11 01:58:42 +00:00
|
|
|
ADD_USER_ARGS = {
|
|
|
|
"ImageId": image_id,
|
|
|
|
"Attribute": "launchPermission",
|
|
|
|
"OperationType": "add",
|
|
|
|
"UserIds": [USER1],
|
|
|
|
}
|
|
|
|
|
|
|
|
# Add users and get no images
|
|
|
|
conn.modify_image_attribute(**ADD_USER_ARGS)
|
|
|
|
|
|
|
|
attributes = conn.describe_image_attribute(
|
2023-05-04 09:16:22 +00:00
|
|
|
ImageId=image_id, Attribute="launchPermission", DryRun=False
|
2017-05-11 01:58:42 +00:00
|
|
|
)
|
2023-07-17 09:31:05 +00:00
|
|
|
assert len(attributes["LaunchPermissions"]) == 1
|
|
|
|
assert attributes["LaunchPermissions"][0]["UserId"] == USER1
|
2017-05-11 01:58:42 +00:00
|
|
|
images = conn.describe_images(ExecutableUsers=[USER1])["Images"]
|
2023-07-17 09:31:05 +00:00
|
|
|
assert len(images) == 1
|
|
|
|
assert images[0]["ImageId"] == image_id
|
2017-05-11 01:58:42 +00:00
|
|
|
|
|
|
|
|
2024-01-07 12:03:33 +00:00
|
|
|
@mock_aws
|
2017-05-11 01:58:42 +00:00
|
|
|
def test_ami_describe_executable_users_negative():
|
|
|
|
conn = boto3.client("ec2", region_name="us-east-1")
|
|
|
|
ec2 = boto3.resource("ec2", "us-east-1")
|
2021-01-13 09:02:11 +00:00
|
|
|
ec2.create_instances(ImageId=EXAMPLE_AMI_ID, MinCount=1, MaxCount=1)
|
2017-12-05 21:53:30 +00:00
|
|
|
response = conn.describe_instances(
|
|
|
|
Filters=[{"Name": "instance-state-name", "Values": ["running"]}]
|
|
|
|
)
|
2017-05-11 01:58:42 +00:00
|
|
|
instance_id = response["Reservations"][0]["Instances"][0]["InstanceId"]
|
|
|
|
image_id = conn.create_image(InstanceId=instance_id, Name="TestImage")["ImageId"]
|
2019-10-31 15:44:26 +00:00
|
|
|
|
2022-11-17 22:41:08 +00:00
|
|
|
USER1 = "".join([f"{random.randint(0, 9)}" for _ in range(0, 12)])
|
|
|
|
USER2 = "".join([f"{random.randint(0, 9)}" for _ in range(0, 12)])
|
2019-10-31 15:44:26 +00:00
|
|
|
|
2017-05-11 01:58:42 +00:00
|
|
|
ADD_USER_ARGS = {
|
|
|
|
"ImageId": image_id,
|
|
|
|
"Attribute": "launchPermission",
|
|
|
|
"OperationType": "add",
|
|
|
|
"UserIds": [USER1],
|
|
|
|
}
|
|
|
|
|
2017-12-05 21:53:30 +00:00
|
|
|
# Add users and get no images
|
2017-05-11 01:58:42 +00:00
|
|
|
# Add users and get no images
|
|
|
|
conn.modify_image_attribute(**ADD_USER_ARGS)
|
|
|
|
|
|
|
|
attributes = conn.describe_image_attribute(
|
2023-05-04 09:16:22 +00:00
|
|
|
ImageId=image_id, Attribute="launchPermission", DryRun=False
|
2017-05-11 01:58:42 +00:00
|
|
|
)
|
2023-07-17 09:31:05 +00:00
|
|
|
assert len(attributes["LaunchPermissions"]) == 1
|
|
|
|
assert attributes["LaunchPermissions"][0]["UserId"] == USER1
|
2017-05-11 01:58:42 +00:00
|
|
|
images = conn.describe_images(ExecutableUsers=[USER2])["Images"]
|
2023-07-17 09:31:05 +00:00
|
|
|
assert len(images) == 0
|
2017-05-11 01:58:42 +00:00
|
|
|
|
|
|
|
|
2024-01-07 12:03:33 +00:00
|
|
|
@mock_aws
|
2017-05-11 01:58:42 +00:00
|
|
|
def test_ami_describe_executable_users_and_filter():
|
|
|
|
conn = boto3.client("ec2", region_name="us-east-1")
|
|
|
|
ec2 = boto3.resource("ec2", "us-east-1")
|
2021-01-13 09:02:11 +00:00
|
|
|
ec2.create_instances(ImageId=EXAMPLE_AMI_ID, MinCount=1, MaxCount=1)
|
2017-12-05 21:53:30 +00:00
|
|
|
response = conn.describe_instances(
|
|
|
|
Filters=[{"Name": "instance-state-name", "Values": ["running"]}]
|
|
|
|
)
|
2017-05-11 01:58:42 +00:00
|
|
|
instance_id = response["Reservations"][0]["Instances"][0]["InstanceId"]
|
|
|
|
image_id = conn.create_image(InstanceId=instance_id, Name="ImageToDelete")[
|
2017-12-05 21:53:30 +00:00
|
|
|
"ImageId"
|
|
|
|
]
|
2019-10-31 15:44:26 +00:00
|
|
|
|
2022-11-17 22:41:08 +00:00
|
|
|
USER1 = "".join([f"{random.randint(0, 9)}" for _ in range(0, 12)])
|
2019-10-31 15:44:26 +00:00
|
|
|
|
2017-05-11 01:58:42 +00:00
|
|
|
ADD_USER_ARGS = {
|
|
|
|
"ImageId": image_id,
|
|
|
|
"Attribute": "launchPermission",
|
|
|
|
"OperationType": "add",
|
|
|
|
"UserIds": [USER1],
|
|
|
|
}
|
|
|
|
|
|
|
|
# Add users and get no images
|
|
|
|
conn.modify_image_attribute(**ADD_USER_ARGS)
|
|
|
|
|
|
|
|
attributes = conn.describe_image_attribute(
|
2023-05-04 09:16:22 +00:00
|
|
|
ImageId=image_id, Attribute="launchPermission", DryRun=False
|
2017-05-11 01:58:42 +00:00
|
|
|
)
|
2023-07-17 09:31:05 +00:00
|
|
|
assert len(attributes["LaunchPermissions"]) == 1
|
|
|
|
assert attributes["LaunchPermissions"][0]["UserId"] == USER1
|
2017-05-11 01:58:42 +00:00
|
|
|
images = conn.describe_images(
|
|
|
|
ExecutableUsers=[USER1], Filters=[{"Name": "state", "Values": ["available"]}]
|
|
|
|
)["Images"]
|
2023-07-17 09:31:05 +00:00
|
|
|
assert len(images) == 1
|
|
|
|
assert images[0]["ImageId"] == image_id
|
2017-05-11 01:58:42 +00:00
|
|
|
|
|
|
|
|
2024-01-07 12:03:33 +00:00
|
|
|
@mock_aws
|
2022-04-18 20:44:56 +00:00
|
|
|
def test_ami_attribute_user_and_group_permissions():
|
2021-09-25 11:13:07 +00:00
|
|
|
"""
|
|
|
|
Boto supports adding/removing both users and groups at the same time.
|
|
|
|
Just spot-check this -- input variations, idempotency, etc are validated
|
|
|
|
via user-specific and group-specific tests above.
|
|
|
|
"""
|
|
|
|
ec2 = boto3.client("ec2", region_name="us-east-1")
|
|
|
|
reservation = ec2.run_instances(ImageId=EXAMPLE_AMI_ID, MinCount=1, MaxCount=1)
|
|
|
|
instance = reservation["Instances"][0]
|
|
|
|
|
|
|
|
image_id = ec2.create_image(InstanceId=instance["InstanceId"], Name="test-ami-A")[
|
|
|
|
"ImageId"
|
|
|
|
]
|
|
|
|
image = boto3.resource("ec2", region_name="us-east-1").Image(image_id)
|
|
|
|
|
|
|
|
# Baseline
|
|
|
|
permissions = image.describe_attribute(Attribute="launchPermission")[
|
|
|
|
"LaunchPermissions"
|
|
|
|
]
|
2023-07-17 09:31:05 +00:00
|
|
|
assert permissions == []
|
2021-09-25 11:13:07 +00:00
|
|
|
|
|
|
|
USER1 = "123456789011"
|
|
|
|
USER2 = "123456789022"
|
|
|
|
|
|
|
|
ADD_ARGS = {
|
|
|
|
"ImageId": image.id,
|
|
|
|
"Attribute": "launchPermission",
|
|
|
|
"OperationType": "add",
|
|
|
|
"UserGroups": ["all"],
|
|
|
|
"UserIds": [USER1, USER2],
|
|
|
|
}
|
|
|
|
|
|
|
|
REMOVE_ARGS = {
|
|
|
|
"ImageId": image.id,
|
|
|
|
"Attribute": "launchPermission",
|
|
|
|
"OperationType": "remove",
|
|
|
|
"UserGroups": ["all"],
|
|
|
|
"UserIds": [USER1, USER2],
|
|
|
|
}
|
|
|
|
|
|
|
|
# Add and confirm
|
|
|
|
image.modify_attribute(**ADD_ARGS)
|
|
|
|
|
|
|
|
permissions = image.describe_attribute(Attribute="launchPermission")[
|
|
|
|
"LaunchPermissions"
|
|
|
|
]
|
2023-07-17 09:31:05 +00:00
|
|
|
assert len(permissions) == 3
|
|
|
|
assert {"Group": "all"} in permissions
|
|
|
|
assert {"UserId": "123456789022"} in permissions
|
|
|
|
assert {"UserId": "123456789011"} in permissions
|
2021-09-25 11:13:07 +00:00
|
|
|
image.reload()
|
2023-07-17 09:31:05 +00:00
|
|
|
assert image.public is True
|
2021-09-25 11:13:07 +00:00
|
|
|
|
|
|
|
# Remove and confirm
|
|
|
|
image.modify_attribute(**REMOVE_ARGS)
|
|
|
|
|
|
|
|
permissions = image.describe_attribute(Attribute="launchPermission")[
|
|
|
|
"LaunchPermissions"
|
|
|
|
]
|
2023-07-17 09:31:05 +00:00
|
|
|
assert permissions == []
|
2021-09-25 11:13:07 +00:00
|
|
|
image.reload()
|
2023-07-17 09:31:05 +00:00
|
|
|
assert image.public is False
|
2021-09-25 11:13:07 +00:00
|
|
|
|
|
|
|
|
2023-09-03 14:59:09 +00:00
|
|
|
@mock.patch.dict(os.environ, {"MOTO_EC2_LOAD_DEFAULT_AMIS": "true"})
|
2024-01-07 12:03:33 +00:00
|
|
|
@mock_aws
|
2021-10-23 11:29:16 +00:00
|
|
|
def test_filter_description():
|
2023-09-03 14:59:09 +00:00
|
|
|
if settings.TEST_SERVER_MODE:
|
|
|
|
raise SkipTest("Can't set environment variables in ServerMode")
|
2023-01-07 11:35:14 +00:00
|
|
|
# https://github.com/getmoto/moto/issues/4460
|
2021-10-23 11:29:16 +00:00
|
|
|
client = boto3.client("ec2", region_name="us-west-2")
|
|
|
|
|
|
|
|
# Search for partial description
|
|
|
|
resp = client.describe_images(
|
|
|
|
Owners=["amazon"],
|
|
|
|
Filters=[{"Name": "description", "Values": ["Amazon Linux AMI*"]}],
|
|
|
|
)["Images"]
|
2023-07-17 09:31:05 +00:00
|
|
|
assert len(resp) > 9
|
2021-10-23 11:29:16 +00:00
|
|
|
|
|
|
|
# Search for full description
|
|
|
|
resp = client.describe_images(
|
|
|
|
Owners=["amazon"],
|
|
|
|
Filters=[
|
|
|
|
{
|
|
|
|
"Name": "description",
|
|
|
|
"Values": ["Amazon Linux AMI 2018.03.0.20210721.0 x86_64 VPC HVM ebs"],
|
|
|
|
}
|
|
|
|
],
|
|
|
|
)["Images"]
|
2023-07-17 09:31:05 +00:00
|
|
|
assert len(resp) == 1
|
2021-10-23 11:29:16 +00:00
|
|
|
|
|
|
|
|
2024-01-07 12:03:33 +00:00
|
|
|
@mock_aws
|
2022-04-18 20:44:56 +00:00
|
|
|
def test_ami_attribute_error_cases():
|
2021-09-25 11:13:07 +00:00
|
|
|
ec2 = boto3.client("ec2", region_name="us-east-1")
|
|
|
|
reservation = ec2.run_instances(ImageId=EXAMPLE_AMI_ID, MinCount=1, MaxCount=1)
|
|
|
|
instance = reservation["Instances"][0]
|
|
|
|
|
|
|
|
image_id = ec2.create_image(InstanceId=instance["InstanceId"], Name="test-ami-A")[
|
|
|
|
"ImageId"
|
|
|
|
]
|
|
|
|
image = boto3.resource("ec2", region_name="us-east-1").Image(image_id)
|
|
|
|
|
|
|
|
# Error: Add with group != 'all'
|
|
|
|
with pytest.raises(ClientError) as ex:
|
|
|
|
image.modify_attribute(
|
|
|
|
ImageId=image_id,
|
|
|
|
Attribute="launchPermission",
|
|
|
|
OperationType="add",
|
|
|
|
UserGroups=["everyone"],
|
|
|
|
)
|
2023-07-17 09:31:05 +00:00
|
|
|
assert ex.value.response["ResponseMetadata"]["HTTPStatusCode"] == 400
|
|
|
|
assert "RequestId" in ex.value.response["ResponseMetadata"]
|
|
|
|
assert ex.value.response["Error"]["Code"] == "InvalidAMIAttributeItemValue"
|
2021-09-25 11:13:07 +00:00
|
|
|
|
|
|
|
# Error: Add with user ID that isn't an integer.
|
|
|
|
with pytest.raises(ClientError) as ex:
|
|
|
|
image.modify_attribute(
|
|
|
|
ImageId=image_id,
|
|
|
|
Attribute="launchPermission",
|
|
|
|
OperationType="add",
|
|
|
|
UserIds=["12345678901A"],
|
|
|
|
)
|
2023-07-17 09:31:05 +00:00
|
|
|
assert ex.value.response["ResponseMetadata"]["HTTPStatusCode"] == 400
|
|
|
|
assert "RequestId" in ex.value.response["ResponseMetadata"]
|
|
|
|
assert ex.value.response["Error"]["Code"] == "InvalidAMIAttributeItemValue"
|
2021-09-25 11:13:07 +00:00
|
|
|
|
|
|
|
# Error: Add with user ID that is > length 12.
|
|
|
|
with pytest.raises(ClientError) as ex:
|
|
|
|
image.modify_attribute(
|
|
|
|
ImageId=image_id,
|
|
|
|
Attribute="launchPermission",
|
|
|
|
OperationType="add",
|
|
|
|
UserIds=["1234567890123"],
|
|
|
|
)
|
2023-07-17 09:31:05 +00:00
|
|
|
assert ex.value.response["ResponseMetadata"]["HTTPStatusCode"] == 400
|
|
|
|
assert "RequestId" in ex.value.response["ResponseMetadata"]
|
|
|
|
assert ex.value.response["Error"]["Code"] == "InvalidAMIAttributeItemValue"
|
2021-09-25 11:13:07 +00:00
|
|
|
|
|
|
|
# Error: Add with user ID that is < length 12.
|
|
|
|
with pytest.raises(ClientError) as ex:
|
|
|
|
image.modify_attribute(
|
|
|
|
ImageId=image_id,
|
|
|
|
Attribute="launchPermission",
|
|
|
|
OperationType="add",
|
|
|
|
UserIds=["12345678901"],
|
|
|
|
)
|
2023-07-17 09:31:05 +00:00
|
|
|
assert ex.value.response["ResponseMetadata"]["HTTPStatusCode"] == 400
|
|
|
|
assert "RequestId" in ex.value.response["ResponseMetadata"]
|
|
|
|
assert ex.value.response["Error"]["Code"] == "InvalidAMIAttributeItemValue"
|
2021-09-25 11:13:07 +00:00
|
|
|
|
|
|
|
# Error: Add with one invalid user ID among other valid IDs, ensure no
|
|
|
|
# partial changes.
|
|
|
|
with pytest.raises(ClientError) as ex:
|
|
|
|
image.modify_attribute(
|
|
|
|
ImageId=image_id,
|
|
|
|
Attribute="launchPermission",
|
|
|
|
OperationType="add",
|
|
|
|
UserIds=["123456789011", "foo", "123456789022"],
|
|
|
|
)
|
2023-07-17 09:31:05 +00:00
|
|
|
assert ex.value.response["ResponseMetadata"]["HTTPStatusCode"] == 400
|
|
|
|
assert "RequestId" in ex.value.response["ResponseMetadata"]
|
|
|
|
assert ex.value.response["Error"]["Code"] == "InvalidAMIAttributeItemValue"
|
2021-09-25 11:13:07 +00:00
|
|
|
|
|
|
|
permissions = image.describe_attribute(Attribute="launchPermission")[
|
|
|
|
"LaunchPermissions"
|
|
|
|
]
|
2023-07-17 09:31:05 +00:00
|
|
|
assert permissions == []
|
2021-09-25 11:13:07 +00:00
|
|
|
|
|
|
|
# Error: Add with invalid image ID
|
|
|
|
with pytest.raises(ClientError) as ex:
|
|
|
|
image.modify_attribute(
|
|
|
|
ImageId="ami-abcd1234",
|
|
|
|
Attribute="launchPermission",
|
|
|
|
OperationType="add",
|
|
|
|
UserGroups=["all"],
|
|
|
|
)
|
2023-07-17 09:31:05 +00:00
|
|
|
assert ex.value.response["ResponseMetadata"]["HTTPStatusCode"] == 400
|
|
|
|
assert "RequestId" in ex.value.response["ResponseMetadata"]
|
|
|
|
assert ex.value.response["Error"]["Code"] == "InvalidAMIID.NotFound"
|
2021-09-25 11:13:07 +00:00
|
|
|
|
|
|
|
# Error: Remove with invalid image ID
|
|
|
|
with pytest.raises(ClientError) as ex:
|
|
|
|
image.modify_attribute(
|
|
|
|
ImageId="ami-abcd1234",
|
|
|
|
Attribute="launchPermission",
|
|
|
|
OperationType="remove",
|
|
|
|
UserGroups=["all"],
|
|
|
|
)
|
2023-07-17 09:31:05 +00:00
|
|
|
assert ex.value.response["ResponseMetadata"]["HTTPStatusCode"] == 400
|
|
|
|
assert "RequestId" in ex.value.response["ResponseMetadata"]
|
|
|
|
assert ex.value.response["Error"]["Code"] == "InvalidAMIID.NotFound"
|
2021-09-25 11:13:07 +00:00
|
|
|
|
|
|
|
|
2024-01-07 12:03:33 +00:00
|
|
|
@mock_aws
|
2017-12-22 13:20:18 +00:00
|
|
|
def test_ami_describe_non_existent():
|
|
|
|
ec2 = boto3.resource("ec2", region_name="us-west-1")
|
|
|
|
# Valid pattern but non-existent id
|
|
|
|
img = ec2.Image("ami-abcd1234")
|
2020-10-06 05:54:49 +00:00
|
|
|
with pytest.raises(ClientError):
|
2017-12-22 13:20:18 +00:00
|
|
|
img.load()
|
|
|
|
# Invalid ami pattern
|
|
|
|
img = ec2.Image("not_an_ami_id")
|
2020-10-06 05:54:49 +00:00
|
|
|
with pytest.raises(ClientError):
|
2017-12-22 13:20:18 +00:00
|
|
|
img.load()
|
|
|
|
|
|
|
|
|
2024-01-07 12:03:33 +00:00
|
|
|
@mock_aws
|
2020-07-26 12:51:26 +00:00
|
|
|
def test_ami_registration():
|
|
|
|
ec2 = boto3.client("ec2", region_name="us-east-1")
|
|
|
|
image_id = ec2.register_image(Name="test-register-image").get("ImageId", "")
|
|
|
|
images = ec2.describe_images(ImageIds=[image_id]).get("Images", [])
|
|
|
|
assert images[0]["Name"] == "test-register-image", "No image was registered."
|
|
|
|
assert images[0]["RootDeviceName"] == "/dev/sda1", "Wrong root device name."
|
|
|
|
assert images[0]["State"] == "available", "State should be available."
|
|
|
|
|
|
|
|
|
2024-01-07 12:03:33 +00:00
|
|
|
@mock_aws
|
2017-05-11 01:33:30 +00:00
|
|
|
def test_ami_filter_wildcard():
|
2018-01-11 19:51:42 +00:00
|
|
|
ec2_resource = boto3.resource("ec2", region_name="us-west-1")
|
|
|
|
ec2_client = boto3.client("ec2", region_name="us-west-1")
|
|
|
|
|
2022-02-14 22:40:48 +00:00
|
|
|
image_name = str(uuid4())[0:12]
|
2021-10-05 17:11:07 +00:00
|
|
|
|
2018-01-11 19:51:42 +00:00
|
|
|
instance = ec2_resource.create_instances(
|
2021-01-13 09:02:11 +00:00
|
|
|
ImageId=EXAMPLE_AMI_ID, MinCount=1, MaxCount=1
|
2018-01-11 19:51:42 +00:00
|
|
|
)[0]
|
2021-10-05 17:11:07 +00:00
|
|
|
instance.create_image(Name=image_name)
|
2017-12-05 21:53:30 +00:00
|
|
|
|
|
|
|
# create an image with the same owner but will not match the filter
|
2021-10-05 17:11:07 +00:00
|
|
|
instance.create_image(Name=str(uuid4())[0:6])
|
2017-12-05 21:53:30 +00:00
|
|
|
|
2018-01-11 19:51:42 +00:00
|
|
|
my_images = ec2_client.describe_images(
|
2021-10-05 17:11:07 +00:00
|
|
|
Owners=[ACCOUNT_ID],
|
2022-02-14 22:40:48 +00:00
|
|
|
Filters=[{"Name": "name", "Values": [f"{image_name[0:8]}*"]}],
|
2018-01-11 19:51:42 +00:00
|
|
|
)["Images"]
|
2023-07-17 09:31:05 +00:00
|
|
|
assert len(my_images) == 1
|
2017-05-11 01:33:30 +00:00
|
|
|
|
2017-11-12 11:18:25 +00:00
|
|
|
|
2023-09-03 14:59:09 +00:00
|
|
|
@mock.patch.dict(os.environ, {"MOTO_EC2_LOAD_DEFAULT_AMIS": "true"})
|
2024-01-07 12:03:33 +00:00
|
|
|
@mock_aws
|
2017-11-12 11:18:25 +00:00
|
|
|
def test_ami_filter_by_owner_id():
|
2023-09-03 14:59:09 +00:00
|
|
|
if settings.TEST_SERVER_MODE:
|
|
|
|
raise SkipTest("Can't set environment variables in ServerMode")
|
2017-11-12 11:18:25 +00:00
|
|
|
client = boto3.client("ec2", region_name="us-east-1")
|
|
|
|
|
|
|
|
ubuntu_id = "099720109477"
|
|
|
|
|
|
|
|
ubuntu_images = client.describe_images(Owners=[ubuntu_id])
|
|
|
|
all_images = client.describe_images()
|
|
|
|
|
|
|
|
ubuntu_ids = [ami["OwnerId"] for ami in ubuntu_images["Images"]]
|
|
|
|
all_ids = [ami["OwnerId"] for ami in all_images["Images"]]
|
|
|
|
|
|
|
|
# Assert all ubuntu_ids are the same and one equals ubuntu_id
|
|
|
|
assert all(ubuntu_ids) and ubuntu_ids[0] == ubuntu_id
|
|
|
|
# Check we actually have a subset of images
|
|
|
|
assert len(ubuntu_ids) < len(all_ids)
|
2018-01-04 10:01:17 +00:00
|
|
|
|
2018-01-11 19:51:42 +00:00
|
|
|
|
2024-01-07 12:03:33 +00:00
|
|
|
@mock_aws
|
2018-01-04 10:01:17 +00:00
|
|
|
def test_ami_filter_by_self():
|
2018-01-11 19:51:42 +00:00
|
|
|
ec2_resource = boto3.resource("ec2", region_name="us-west-1")
|
|
|
|
ec2_client = boto3.client("ec2", region_name="us-west-1")
|
2018-01-04 10:01:17 +00:00
|
|
|
|
2021-10-05 17:11:07 +00:00
|
|
|
unique_name = str(uuid4())[0:6]
|
|
|
|
|
|
|
|
images = ec2_client.describe_images(Owners=["self"])["Images"]
|
|
|
|
image_names = [i["Name"] for i in images]
|
2023-07-17 09:31:05 +00:00
|
|
|
assert unique_name not in image_names
|
2018-01-04 10:01:17 +00:00
|
|
|
|
|
|
|
# Create a new image
|
2018-01-11 19:51:42 +00:00
|
|
|
instance = ec2_resource.create_instances(
|
2021-01-13 09:02:11 +00:00
|
|
|
ImageId=EXAMPLE_AMI_ID, MinCount=1, MaxCount=1
|
2018-01-11 19:51:42 +00:00
|
|
|
)[0]
|
2021-10-05 17:11:07 +00:00
|
|
|
instance.create_image(Name=unique_name)
|
2018-01-04 10:01:17 +00:00
|
|
|
|
2021-10-05 17:11:07 +00:00
|
|
|
images = ec2_client.describe_images(Owners=["self"])["Images"]
|
|
|
|
image_names = [i["Name"] for i in images]
|
2023-07-17 09:31:05 +00:00
|
|
|
assert unique_name in image_names
|
2018-03-21 15:55:58 +00:00
|
|
|
|
|
|
|
|
2024-01-07 12:03:33 +00:00
|
|
|
@mock_aws
|
2018-03-21 15:55:58 +00:00
|
|
|
def test_ami_snapshots_have_correct_owner():
|
|
|
|
ec2_client = boto3.client("ec2", region_name="us-west-1")
|
|
|
|
|
|
|
|
images_response = ec2_client.describe_images()
|
|
|
|
|
|
|
|
owner_id_to_snapshot_ids = {}
|
|
|
|
for image in images_response["Images"]:
|
|
|
|
owner_id = image["OwnerId"]
|
|
|
|
snapshot_ids = [
|
|
|
|
block_device_mapping["Ebs"]["SnapshotId"]
|
|
|
|
for block_device_mapping in image["BlockDeviceMappings"]
|
|
|
|
]
|
|
|
|
existing_snapshot_ids = owner_id_to_snapshot_ids.get(owner_id, [])
|
|
|
|
owner_id_to_snapshot_ids[owner_id] = existing_snapshot_ids + snapshot_ids
|
2020-06-15 09:32:43 +00:00
|
|
|
# adding an assertion to volumeType
|
|
|
|
assert (
|
|
|
|
image.get("BlockDeviceMappings", {})[0].get("Ebs", {}).get("VolumeType")
|
|
|
|
== "standard"
|
|
|
|
)
|
2018-03-21 15:55:58 +00:00
|
|
|
for owner_id in owner_id_to_snapshot_ids:
|
|
|
|
snapshots_rseponse = ec2_client.describe_snapshots(
|
|
|
|
SnapshotIds=owner_id_to_snapshot_ids[owner_id]
|
|
|
|
)
|
|
|
|
|
|
|
|
for snapshot in snapshots_rseponse["Snapshots"]:
|
|
|
|
assert owner_id == snapshot["OwnerId"]
|
2021-01-23 12:57:34 +00:00
|
|
|
|
|
|
|
|
2024-01-07 12:03:33 +00:00
|
|
|
@mock_aws
|
2021-01-23 12:57:34 +00:00
|
|
|
def test_create_image_with_tag_specification():
|
|
|
|
ec2 = boto3.resource("ec2", region_name="us-west-1")
|
|
|
|
client = boto3.client("ec2", region_name="us-west-1")
|
|
|
|
tag_specifications = [
|
|
|
|
{
|
|
|
|
"ResourceType": "image",
|
|
|
|
"Tags": [
|
|
|
|
{
|
|
|
|
"Key": "Base_AMI_Name",
|
|
|
|
"Value": "Deep Learning Base AMI (Amazon Linux 2) Version 31.0",
|
|
|
|
},
|
|
|
|
{"Key": "OS_Version", "Value": "AWS Linux 2"},
|
|
|
|
],
|
|
|
|
},
|
|
|
|
]
|
|
|
|
instance = ec2.create_instances(ImageId=EXAMPLE_AMI_ID, MinCount=1, MaxCount=1)[0]
|
|
|
|
image_id = client.create_image(
|
|
|
|
InstanceId=instance.instance_id,
|
|
|
|
Name="test-image",
|
|
|
|
Description="test ami",
|
|
|
|
TagSpecifications=tag_specifications,
|
|
|
|
)["ImageId"]
|
|
|
|
|
|
|
|
image = client.describe_images(ImageIds=[image_id])["Images"][0]
|
2023-07-17 09:31:05 +00:00
|
|
|
assert image["Tags"] == tag_specifications[0]["Tags"]
|
2021-01-23 12:57:34 +00:00
|
|
|
|
|
|
|
with pytest.raises(ClientError) as ex:
|
|
|
|
client.create_image(
|
|
|
|
InstanceId=instance.instance_id,
|
|
|
|
Name="test-image",
|
|
|
|
Description="test ami",
|
|
|
|
TagSpecifications=[
|
|
|
|
{
|
|
|
|
"ResourceType": "invalid-resource-type",
|
|
|
|
"Tags": [{"Key": "key", "Value": "value"}],
|
|
|
|
}
|
|
|
|
],
|
|
|
|
)
|
2023-07-17 09:31:05 +00:00
|
|
|
assert ex.value.response["Error"]["Code"] == "InvalidParameterValue"
|
|
|
|
assert (
|
|
|
|
ex.value.response["Error"]["Message"]
|
|
|
|
== "'invalid-resource-type' is not a valid taggable resource type for this operation."
|
2021-01-23 12:57:34 +00:00
|
|
|
)
|
2021-01-24 12:00:25 +00:00
|
|
|
|
|
|
|
|
2024-01-07 12:03:33 +00:00
|
|
|
@mock_aws
|
2021-01-24 12:00:25 +00:00
|
|
|
def test_ami_filter_by_empty_tag():
|
|
|
|
ec2 = boto3.resource("ec2", region_name="us-west-1")
|
|
|
|
client = boto3.client("ec2", region_name="us-west-1")
|
|
|
|
|
|
|
|
fake_images = []
|
|
|
|
instance = ec2.create_instances(ImageId=EXAMPLE_AMI_ID, MinCount=1, MaxCount=1)[0]
|
|
|
|
for i in range(10):
|
|
|
|
image = client.create_image(
|
|
|
|
InstanceId=instance.instance_id,
|
2022-11-17 22:41:08 +00:00
|
|
|
Name=f"MyAMI{i}",
|
2021-01-24 12:00:25 +00:00
|
|
|
Description="Test",
|
|
|
|
)
|
|
|
|
|
|
|
|
ec2.create_tags(
|
|
|
|
Resources=[image["ImageId"]],
|
|
|
|
Tags=[
|
|
|
|
{
|
|
|
|
"Key": "Base_AMI_Name",
|
|
|
|
"Value": "Deep Learning Base AMI (Amazon Linux 2) Version 31.0",
|
|
|
|
},
|
|
|
|
{"Key": "OS_Version", "Value": "AWS Linux 2"},
|
|
|
|
],
|
|
|
|
)
|
|
|
|
fake_images.append(image)
|
|
|
|
# Add release tags to some of the images in the middle
|
2021-10-05 17:11:07 +00:00
|
|
|
release_version = str(uuid4())[0:6]
|
2021-01-24 12:00:25 +00:00
|
|
|
for image in fake_images[3:6]:
|
|
|
|
ec2.create_tags(
|
2021-10-05 17:11:07 +00:00
|
|
|
Resources=[image["ImageId"]],
|
|
|
|
Tags=[{"Key": "RELEASE", "Value": release_version}],
|
2021-01-24 12:00:25 +00:00
|
|
|
)
|
|
|
|
images_filter = [
|
|
|
|
{
|
|
|
|
"Name": "tag:Base_AMI_Name",
|
|
|
|
"Values": ["Deep Learning Base AMI (Amazon Linux 2) Version 31.0"],
|
|
|
|
},
|
|
|
|
{"Name": "tag:OS_Version", "Values": ["AWS Linux 2"]},
|
2021-10-05 17:11:07 +00:00
|
|
|
{"Name": "tag:RELEASE", "Values": [release_version]},
|
2021-01-24 12:00:25 +00:00
|
|
|
]
|
|
|
|
assert len(client.describe_images(Filters=images_filter)["Images"]) == 3
|
2021-09-01 12:23:24 +00:00
|
|
|
|
|
|
|
|
2023-09-03 14:59:09 +00:00
|
|
|
@mock.patch.dict(os.environ, {"MOTO_EC2_LOAD_DEFAULT_AMIS": "true"})
|
2024-01-07 12:03:33 +00:00
|
|
|
@mock_aws
|
2021-09-01 12:23:24 +00:00
|
|
|
def test_ami_filter_by_ownerid():
|
2023-09-03 14:59:09 +00:00
|
|
|
if settings.TEST_SERVER_MODE:
|
|
|
|
raise SkipTest("Can't set environment variables in ServerMode")
|
2021-09-01 12:23:24 +00:00
|
|
|
ec2_connection = boto3.client("ec2", region_name="us-east-1")
|
|
|
|
|
|
|
|
images = ec2_connection.describe_images(
|
|
|
|
Filters=[
|
|
|
|
{"Name": "name", "Values": ["amzn-ami-*"]},
|
|
|
|
{"Name": "owner-alias", "Values": ["amazon"]},
|
|
|
|
]
|
|
|
|
)["Images"]
|
|
|
|
assert len(images) > 0, "We should have at least 1 image created by amazon"
|
|
|
|
|
|
|
|
|
2024-01-07 12:03:33 +00:00
|
|
|
@mock_aws
|
2021-09-01 12:23:24 +00:00
|
|
|
def test_ami_filter_by_unknown_ownerid():
|
|
|
|
ec2_connection = boto3.client("ec2", region_name="us-east-1")
|
|
|
|
|
|
|
|
images = ec2_connection.describe_images(
|
|
|
|
Filters=[{"Name": "owner-alias", "Values": ["unknown"]}]
|
|
|
|
)["Images"]
|
2023-07-17 09:31:05 +00:00
|
|
|
assert len(images) == 0
|
2021-10-15 22:43:00 +00:00
|
|
|
|
|
|
|
|
2024-01-07 12:03:33 +00:00
|
|
|
@mock_aws
|
2021-10-15 22:43:00 +00:00
|
|
|
def test_describe_images_dryrun():
|
|
|
|
client = boto3.client("ec2", region_name="us-east-1")
|
|
|
|
|
|
|
|
with pytest.raises(ClientError) as ex:
|
|
|
|
client.describe_images(DryRun=True)
|
2023-07-17 09:31:05 +00:00
|
|
|
assert ex.value.response["ResponseMetadata"]["HTTPStatusCode"] == 412
|
|
|
|
assert ex.value.response["Error"]["Code"] == "DryRunOperation"
|
|
|
|
assert (
|
|
|
|
ex.value.response["Error"]["Message"]
|
|
|
|
== "An error occurred (DryRunOperation) when calling the DescribeImages operation: Request would have succeeded, but DryRun flag is set"
|
2021-10-15 22:43:00 +00:00
|
|
|
)
|
2021-12-25 21:37:39 +00:00
|
|
|
|
|
|
|
|
2024-01-07 12:03:33 +00:00
|
|
|
@mock_aws
|
2021-12-25 21:37:39 +00:00
|
|
|
def test_delete_snapshot_from_create_image():
|
|
|
|
ec2_client = boto3.client("ec2", region_name="us-east-1")
|
|
|
|
resp = ec2_client.run_instances(ImageId=EXAMPLE_AMI_ID, MinCount=1, MaxCount=1)
|
|
|
|
instance_id = resp["Instances"][0]["InstanceId"]
|
|
|
|
ami = ec2_client.create_image(InstanceId=instance_id, Name="test")
|
|
|
|
ami_id = ami["ImageId"]
|
|
|
|
|
|
|
|
snapshots = ec2_client.describe_snapshots(
|
|
|
|
Filters=[
|
|
|
|
{
|
|
|
|
"Name": "description",
|
|
|
|
"Values": ["Created by CreateImage(" + instance_id + "*"],
|
|
|
|
}
|
|
|
|
]
|
|
|
|
)["Snapshots"]
|
|
|
|
snapshot_id = snapshots[0]["SnapshotId"]
|
|
|
|
with pytest.raises(ClientError) as exc:
|
|
|
|
ec2_client.delete_snapshot(SnapshotId=snapshot_id)
|
|
|
|
err = exc.value.response["Error"]
|
2023-07-17 09:31:05 +00:00
|
|
|
assert err["Code"] == "InvalidSnapshot.InUse"
|
|
|
|
assert (
|
|
|
|
err["Message"] == f"The snapshot {snapshot_id} is currently in use by {ami_id}"
|
2021-12-25 21:37:39 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
# Deregister the Ami first
|
|
|
|
ec2_client.deregister_image(ImageId=ami_id)
|
|
|
|
|
|
|
|
# Now we can delete the snapshot without problems
|
|
|
|
ec2_client.delete_snapshot(SnapshotId=snapshot_id)
|
|
|
|
|
|
|
|
with pytest.raises(ClientError) as exc:
|
|
|
|
ec2_client.describe_snapshots(SnapshotIds=[snapshot_id])
|
2023-07-17 09:31:05 +00:00
|
|
|
assert exc.value.response["Error"]["Code"] == "InvalidSnapshot.NotFound"
|
2023-05-04 09:16:22 +00:00
|
|
|
|
|
|
|
|
2023-09-03 14:59:09 +00:00
|
|
|
@mock.patch.dict(os.environ, {"MOTO_EC2_LOAD_DEFAULT_AMIS": "true"})
|
2024-01-07 12:03:33 +00:00
|
|
|
@mock_aws
|
2023-05-04 09:16:22 +00:00
|
|
|
def test_ami_describe_image_attribute_product_codes():
|
2023-09-03 14:59:09 +00:00
|
|
|
if settings.TEST_SERVER_MODE:
|
|
|
|
raise SkipTest("Can't set environment variables in ServerMode")
|
2023-05-04 09:16:22 +00:00
|
|
|
# Setup
|
|
|
|
conn = boto3.client("ec2", region_name="us-east-1")
|
|
|
|
|
|
|
|
# test ami loaded from moto/ec2/resources/ami.json
|
|
|
|
test_image = conn.describe_images(
|
|
|
|
Filters=[{"Name": "name", "Values": ["product_codes_test"]}]
|
|
|
|
)
|
|
|
|
image_id = test_image["Images"][0]["ImageId"]
|
|
|
|
expected_codes = [
|
|
|
|
{"ProductCodeId": "code123", "ProductCodeType": "marketplace"},
|
|
|
|
{"ProductCodeId": "code456", "ProductCodeType": "marketplace"},
|
|
|
|
]
|
|
|
|
# Execute
|
|
|
|
attributes = conn.describe_image_attribute(
|
|
|
|
ImageId=image_id, Attribute="productCodes", DryRun=False
|
|
|
|
)
|
|
|
|
|
|
|
|
# Verify
|
|
|
|
assert "ProductCodes" in attributes
|
|
|
|
assert len(attributes["ProductCodes"]) == 2
|
|
|
|
assert attributes["ProductCodes"] == expected_codes
|
|
|
|
|
|
|
|
|
2023-09-03 14:59:09 +00:00
|
|
|
@mock.patch.dict(os.environ, {"MOTO_EC2_LOAD_DEFAULT_AMIS": "true"})
|
2024-01-07 12:03:33 +00:00
|
|
|
@mock_aws
|
2023-05-04 09:16:22 +00:00
|
|
|
def test_ami_describe_image_attribute():
|
2023-09-03 14:59:09 +00:00
|
|
|
if settings.TEST_SERVER_MODE:
|
|
|
|
raise SkipTest("Can't set environment variables in ServerMode")
|
2023-05-04 09:16:22 +00:00
|
|
|
# Setup
|
|
|
|
conn = boto3.client("ec2", region_name="us-east-1")
|
|
|
|
|
|
|
|
# test ami loaded from moto/ec2/resources/ami.json
|
|
|
|
test_image = conn.describe_images(
|
|
|
|
Filters=[{"Name": "name", "Values": ["product_codes_test"]}]
|
|
|
|
)
|
|
|
|
image_id = test_image["Images"][0]["ImageId"]
|
|
|
|
|
|
|
|
# Execute
|
|
|
|
description = conn.describe_image_attribute(
|
|
|
|
ImageId=image_id, Attribute="description", DryRun=False
|
|
|
|
)
|
|
|
|
boot_mode = conn.describe_image_attribute(
|
|
|
|
ImageId=image_id, Attribute="bootMode", DryRun=False
|
|
|
|
)
|
|
|
|
sriov = conn.describe_image_attribute(
|
|
|
|
ImageId=image_id, Attribute="sriovNetSupport", DryRun=False
|
|
|
|
)
|
|
|
|
|
|
|
|
# Verify
|
|
|
|
assert "Description" in description
|
|
|
|
assert description["Description"]["Value"] == "Test ami for product codes"
|
|
|
|
assert "BootMode" in boot_mode
|
|
|
|
assert boot_mode["BootMode"]["Value"] == "uefi"
|
|
|
|
assert "SriovNetSupport" in sriov
|
|
|
|
assert sriov["SriovNetSupport"]["Value"] == "simple"
|
|
|
|
|
|
|
|
|
2023-09-03 14:59:09 +00:00
|
|
|
@mock.patch.dict(os.environ, {"MOTO_EC2_LOAD_DEFAULT_AMIS": "true"})
|
2024-01-07 12:03:33 +00:00
|
|
|
@mock_aws
|
2023-05-04 09:16:22 +00:00
|
|
|
def test_ami_describe_image_attribute_block_device_fail():
|
2023-09-03 14:59:09 +00:00
|
|
|
if settings.TEST_SERVER_MODE:
|
|
|
|
raise SkipTest("Can't set environment variables in ServerMode")
|
2023-05-04 09:16:22 +00:00
|
|
|
# Setup
|
|
|
|
conn = boto3.client("ec2", region_name="us-east-1")
|
|
|
|
test_image = conn.describe_images()
|
|
|
|
image_id = test_image["Images"][0]["ImageId"]
|
|
|
|
|
|
|
|
# Execute
|
|
|
|
with pytest.raises(ClientError) as e:
|
|
|
|
conn.describe_image_attribute(
|
|
|
|
ImageId=image_id, Attribute="blockDeviceMapping", DryRun=False
|
|
|
|
)
|
|
|
|
|
|
|
|
# Verify
|
|
|
|
assert e.value.response["Error"]["Code"] == "AuthFailure"
|
|
|
|
assert (
|
|
|
|
e.value.response["Error"]["Message"]
|
|
|
|
== "Unauthorized attempt to access restricted resource"
|
|
|
|
)
|
|
|
|
|
|
|
|
|
2023-09-03 14:59:09 +00:00
|
|
|
@mock.patch.dict(os.environ, {"MOTO_EC2_LOAD_DEFAULT_AMIS": "true"})
|
2024-01-07 12:03:33 +00:00
|
|
|
@mock_aws
|
2023-05-04 09:16:22 +00:00
|
|
|
def test_ami_describe_image_attribute_invalid_param():
|
2023-09-03 14:59:09 +00:00
|
|
|
if settings.TEST_SERVER_MODE:
|
|
|
|
raise SkipTest("Can't set environment variables in ServerMode")
|
2023-05-04 09:16:22 +00:00
|
|
|
# Setup
|
|
|
|
conn = boto3.client("ec2", region_name="us-east-1")
|
|
|
|
test_image = conn.describe_images()
|
|
|
|
image_id = test_image["Images"][0]["ImageId"]
|
|
|
|
|
|
|
|
# Execute
|
|
|
|
with pytest.raises(ClientError) as e:
|
|
|
|
conn.describe_image_attribute(
|
|
|
|
ImageId=image_id, Attribute="invalid", DryRun=False
|
|
|
|
)
|
|
|
|
|
|
|
|
# Verify
|
|
|
|
assert e.value.response["Error"]["Code"] == "InvalidRequest"
|
|
|
|
assert e.value.response["Error"]["Message"] == "The request received was invalid"
|