From 6b14d01f5dd6298351ea65fb228257fd73ca58ad Mon Sep 17 00:00:00 2001 From: szopen321 <32538420+szopen321@users.noreply.github.com> Date: Thu, 26 May 2022 18:04:36 +0200 Subject: [PATCH] EC2 - Add ami validation (#5160) --- moto/ec2/models/instances.py | 2 + moto/settings.py | 2 + tests/test_autoscaling/test_autoscaling.py | 2 +- .../test_cloudformation_stack_crud_boto3.py | 19 +++--- tests/test_ec2/test_ec2_integration.py | 4 +- tests/test_ec2/test_instances.py | 58 +++++++++++++++---- tests/test_ec2/test_spot_fleet.py | 6 +- tests/test_ec2/test_spot_instances.py | 4 +- 8 files changed, 68 insertions(+), 29 deletions(-) diff --git a/moto/ec2/models/instances.py b/moto/ec2/models/instances.py index d9de06873..c3745523b 100644 --- a/moto/ec2/models/instances.py +++ b/moto/ec2/models/instances.py @@ -555,6 +555,8 @@ class InstanceBackend(object): default_region = "us-east-1" if settings.ENABLE_KEYPAIR_VALIDATION: self.describe_key_pairs(key_names=[kwargs.get("key_name")]) + if settings.ENABLE_AMI_VALIDATION: + self.describe_images(ami_ids=[image_id] if image_id else []) valid_instance_types = INSTANCE_TYPE_OFFERINGS[location_type] if "region_name" in kwargs and kwargs.get("placement"): valid_availability_zones = { diff --git a/moto/settings.py b/moto/settings.py index de91b5c66..e3f833360 100644 --- a/moto/settings.py +++ b/moto/settings.py @@ -26,6 +26,8 @@ ENABLE_KEYPAIR_VALIDATION = bool( os.environ.get("MOTO_ENABLE_KEYPAIR_VALIDATION", False) ) +ENABLE_AMI_VALIDATION = bool(os.environ.get("MOTO_ENABLE_AMI_VALIDATION", False)) + def get_sf_execution_history_type(): """ diff --git a/tests/test_autoscaling/test_autoscaling.py b/tests/test_autoscaling/test_autoscaling.py index e90e896dd..0b53400f5 100644 --- a/tests/test_autoscaling/test_autoscaling.py +++ b/tests/test_autoscaling/test_autoscaling.py @@ -2671,7 +2671,7 @@ def test_attach_instances(): kwargs = { "KeyName": "foobar", - "ImageId": "ami-pytest", + "ImageId": EXAMPLE_AMI_ID, "MinCount": 1, "MaxCount": 1, "InstanceType": "c4.2xlarge", diff --git a/tests/test_cloudformation/test_cloudformation_stack_crud_boto3.py b/tests/test_cloudformation/test_cloudformation_stack_crud_boto3.py index c0fe03f81..904023c40 100644 --- a/tests/test_cloudformation/test_cloudformation_stack_crud_boto3.py +++ b/tests/test_cloudformation/test_cloudformation_stack_crud_boto3.py @@ -4,27 +4,23 @@ from collections import OrderedDict from datetime import datetime, timedelta import os import pytz - -import boto3 -from botocore.exceptions import ClientError -import sure # noqa # pylint: disable=unused-import - import pytest from unittest import SkipTest - +import boto3 +from botocore.exceptions import ClientError from moto import ( mock_cloudformation, mock_dynamodb, - mock_s3, - mock_sns, - mock_sqs, mock_ec2, mock_iam, mock_lambda, + mock_s3, + mock_sns, + mock_sqs, + settings, ) -from moto import settings -from moto.core import ACCOUNT_ID from moto.cloudformation import cloudformation_backends +from moto.core import ACCOUNT_ID from tests import EXAMPLE_AMI_ID @@ -1181,7 +1177,6 @@ def test_update_stack_fail_missing_new_parameter(): @mock_cloudformation def test_update_stack_fail_update_same_template_body(): - name = "update_stack_with_previous_value" cf_conn = boto3.client("cloudformation", region_name="us-east-1") params = [ diff --git a/tests/test_ec2/test_ec2_integration.py b/tests/test_ec2/test_ec2_integration.py index 8298eb522..3619fe39b 100644 --- a/tests/test_ec2/test_ec2_integration.py +++ b/tests/test_ec2/test_ec2_integration.py @@ -11,8 +11,10 @@ def test_run_instance_with_encrypted_ebs(): kms = boto3.client("kms", region_name="us-east-1") resp = kms.create_key(Description="my key", KeyUsage="ENCRYPT_DECRYPT") key_id = resp["KeyMetadata"]["Arn"] - ec2 = boto3.client("ec2", region_name="us-east-1") + key_name = "keypair_name" + ec2.create_key_pair(KeyName=key_name) + kwargs = { "MinCount": 1, "MaxCount": 1, diff --git a/tests/test_ec2/test_instances.py b/tests/test_ec2/test_instances.py index 6c416af11..750f91f73 100644 --- a/tests/test_ec2/test_instances.py +++ b/tests/test_ec2/test_instances.py @@ -1,20 +1,16 @@ -from botocore.exceptions import ClientError, ParamValidationError - -import pytest -from unittest import SkipTest, mock - import base64 import ipaddress +from unittest import SkipTest, mock +from uuid import uuid4 import boto3 -from freezegun import freeze_time +import pytest import sure # noqa # pylint: disable=unused-import - +from botocore.exceptions import ClientError, ParamValidationError +from freezegun import freeze_time from moto import mock_ec2, settings from moto.core import ACCOUNT_ID from tests import EXAMPLE_AMI_ID -from uuid import uuid4 - decode_method = base64.decodebytes @@ -2094,6 +2090,48 @@ def test_warn_on_invalid_ami(): ec2.create_instances(ImageId="invalid-ami", MinCount=1, MaxCount=1) +@mock_ec2 +@mock.patch( + "moto.ec2.models.instances.settings.ENABLE_AMI_VALIDATION", + new_callable=mock.PropertyMock(return_value=True), +) +def test_error_on_invalid_ami(m_flag): + if settings.TEST_SERVER_MODE: + raise SkipTest("Can't capture warnings in server mode.") + ec2 = boto3.resource("ec2", "us-east-1") + with pytest.raises(ClientError) as ex: + ec2.create_instances(ImageId="ami-invalid", MinCount=1, MaxCount=1) + ex.value.response["ResponseMetadata"]["HTTPStatusCode"].should.equal(400) + ex.value.response["Error"]["Code"].should.equal("InvalidAMIID.NotFound") + ex.value.response["Error"]["Message"].should.equal( + "The image id '[['ami-invalid']]' does not exist" + ) + + assert m_flag is True + + +@mock_ec2 +@mock.patch( + "moto.ec2.models.instances.settings.ENABLE_AMI_VALIDATION", + new_callable=mock.PropertyMock(return_value=True), +) +def test_error_on_invalid_ami_format(m_flag): + if settings.TEST_SERVER_MODE: + raise SkipTest( + "It is not possible to set the environment variable in server mode" + ) + ec2 = boto3.resource("ec2", "us-east-1") + with pytest.raises(ClientError) as ex: + ec2.create_instances(ImageId="invalid-ami-format", MinCount=1, MaxCount=1) + ex.value.response["ResponseMetadata"]["HTTPStatusCode"].should.equal(400) + ex.value.response["Error"]["Code"].should.equal("InvalidAMIID.Malformed") + ex.value.response["Error"]["Message"].should.equal( + 'Invalid id: "[\'invalid-ami-format\']" (expecting "ami-...")' + ) + + assert m_flag is True + + @mock_ec2 def test_filter_wildcard_in_specified_tag_only(): ec2_client = boto3.client("ec2", region_name="us-west-1") @@ -2365,7 +2403,7 @@ def test_describe_instances_filter_vpcid_via_networkinterface(): "PrivateIpAddresses": [{"Primary": True, "PrivateIpAddress": "10.26.1.3"}], } instance = ec2.create_instances( - ImageId="myami", NetworkInterfaces=[my_interface], MinCount=1, MaxCount=1 + ImageId=EXAMPLE_AMI_ID, NetworkInterfaces=[my_interface], MinCount=1, MaxCount=1 )[0] _filter = [{"Name": "vpc-id", "Values": [vpc.id]}] diff --git a/tests/test_ec2/test_spot_fleet.py b/tests/test_ec2/test_spot_fleet.py index 379198b19..689b56cf9 100644 --- a/tests/test_ec2/test_spot_fleet.py +++ b/tests/test_ec2/test_spot_fleet.py @@ -147,7 +147,7 @@ def test_request_spot_fleet_using_launch_template_config__name(allocation_strate conn = boto3.client("ec2", region_name="us-east-2") template_data = { - "ImageId": "ami-04d4e25790238c5f4", + "ImageId": EXAMPLE_AMI_ID, "InstanceType": "t2.medium", "DisableApiTermination": False, "TagSpecifications": [ @@ -194,7 +194,7 @@ def test_request_spot_fleet_using_launch_template_config__id(): conn = boto3.client("ec2", region_name="us-east-2") template_data = { - "ImageId": "ami-04d4e25790238c5f4", + "ImageId": EXAMPLE_AMI_ID, "InstanceType": "t2.medium", "DisableApiTermination": False, "TagSpecifications": [ @@ -238,7 +238,7 @@ def test_request_spot_fleet_using_launch_template_config__overrides(): subnet_id = get_subnet_id(conn) template_data = { - "ImageId": "ami-04d4e25790238c5f4", + "ImageId": EXAMPLE_AMI_ID, "InstanceType": "t2.medium", "DisableApiTermination": False, "TagSpecifications": [ diff --git a/tests/test_ec2/test_spot_instances.py b/tests/test_ec2/test_spot_instances.py index 4e7f5bc03..25c9d5128 100644 --- a/tests/test_ec2/test_spot_instances.py +++ b/tests/test_ec2/test_spot_instances.py @@ -338,7 +338,7 @@ def test_launch_spot_instance_instance_lifecycle(): kwargs = { "KeyName": "foobar", - "ImageId": "ami-pytest", + "ImageId": EXAMPLE_AMI_ID, "MinCount": 1, "MaxCount": 1, "InstanceType": "c4.2xlarge", @@ -362,7 +362,7 @@ def test_launch_instance_instance_lifecycle(): kwargs = { "KeyName": "foobar", - "ImageId": "ami-pytest", + "ImageId": EXAMPLE_AMI_ID, "MinCount": 1, "MaxCount": 1, "InstanceType": "c4.2xlarge",