EC2 - SubnetId and NetworkInterfaces cannot both be specified when calling run_instances (#4719)

This commit is contained in:
Bert Blommers 2021-12-24 14:12:57 -01:00 committed by GitHub
parent b0a59fe05e
commit a1bfb6a48e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 45 additions and 9 deletions

View File

@ -49,6 +49,11 @@ class InvalidDHCPOptionsIdError(EC2ClientError):
) )
class InvalidParameterCombination(EC2ClientError):
def __init__(self, msg):
super().__init__("InvalidParameterCombination", msg)
class MalformedDHCPOptionsIdError(EC2ClientError): class MalformedDHCPOptionsIdError(EC2ClientError):
def __init__(self, dhcp_options_id): def __init__(self, dhcp_options_id):
super().__init__( super().__init__(

View File

@ -677,6 +677,8 @@ class Instance(TaggedEC2Resource, BotoInstance, CloudFormationModel):
self.id = random_instance_id() self.id = random_instance_id()
self.lifecycle = kwargs.get("lifecycle") self.lifecycle = kwargs.get("lifecycle")
nics = kwargs.get("nics", {})
launch_template_arg = kwargs.get("launch_template", {}) launch_template_arg = kwargs.get("launch_template", {})
if launch_template_arg and not image_id: if launch_template_arg and not image_id:
# the image id from the template should be used # the image id from the template should be used
@ -703,6 +705,10 @@ class Instance(TaggedEC2Resource, BotoInstance, CloudFormationModel):
self.region_name = kwargs.get("region_name", "us-east-1") self.region_name = kwargs.get("region_name", "us-east-1")
placement = kwargs.get("placement", None) placement = kwargs.get("placement", None)
self.subnet_id = kwargs.get("subnet_id") self.subnet_id = kwargs.get("subnet_id")
if not self.subnet_id:
self.subnet_id = next(
(n["SubnetId"] for n in nics.values() if "SubnetId" in n), None
)
in_ec2_classic = not bool(self.subnet_id) in_ec2_classic = not bool(self.subnet_id)
self.key_name = kwargs.get("key_name") self.key_name = kwargs.get("key_name")
self.ebs_optimized = kwargs.get("ebs_optimized", False) self.ebs_optimized = kwargs.get("ebs_optimized", False)
@ -759,7 +765,7 @@ class Instance(TaggedEC2Resource, BotoInstance, CloudFormationModel):
self._private_ips = set() self._private_ips = set()
self.prep_nics( self.prep_nics(
kwargs.get("nics", {}), nics,
private_ip=kwargs.get("private_ip"), private_ip=kwargs.get("private_ip"),
associate_public_ip=self.associate_public_ip, associate_public_ip=self.associate_public_ip,
security_groups=self.security_groups, security_groups=self.security_groups,

View File

@ -1,7 +1,7 @@
from moto.autoscaling import autoscaling_backends from moto.autoscaling import autoscaling_backends
from moto.core.responses import BaseResponse from moto.core.responses import BaseResponse
from moto.core.utils import camelcase_to_underscores from moto.core.utils import camelcase_to_underscores
from moto.ec2.exceptions import MissingParameterError from moto.ec2.exceptions import MissingParameterError, InvalidParameterCombination
from moto.ec2.utils import ( from moto.ec2.utils import (
filters_from_querystring, filters_from_querystring,
dict_from_querystring, dict_from_querystring,
@ -70,6 +70,10 @@ class InstanceResponse(BaseResponse):
), ),
"launch_template": self._get_multi_param_dict("LaunchTemplate"), "launch_template": self._get_multi_param_dict("LaunchTemplate"),
} }
if len(kwargs["nics"]) and kwargs["subnet_id"]:
raise InvalidParameterCombination(
msg="Network interfaces and an instance-level subnet ID may not be specified on the same request"
)
mappings = self._parse_block_device_mapping() mappings = self._parse_block_device_mapping()
if mappings: if mappings:

View File

@ -2116,7 +2116,6 @@ def test_run_instance_with_nic_preexisting_boto3():
MinCount=1, MinCount=1,
MaxCount=1, MaxCount=1,
NetworkInterfaces=[{"DeviceIndex": 0, "NetworkInterfaceId": eni.id,}], NetworkInterfaces=[{"DeviceIndex": 0, "NetworkInterfaceId": eni.id,}],
SubnetId=subnet.id,
SecurityGroupIds=[security_group2.group_id], SecurityGroupIds=[security_group2.group_id],
)[0] )[0]
@ -3053,8 +3052,7 @@ def test_run_instance_and_associate_public_ip():
ImageId=EXAMPLE_AMI_ID, ImageId=EXAMPLE_AMI_ID,
MinCount=1, MinCount=1,
MaxCount=1, MaxCount=1,
NetworkInterfaces=[{"DeviceIndex": 0}], NetworkInterfaces=[{"DeviceIndex": 0, "SubnetId": subnet.id}],
SubnetId=subnet.id,
)[0] )[0]
interfaces = instance.network_interfaces_attribute interfaces = instance.network_interfaces_attribute
addresses = interfaces[0]["PrivateIpAddresses"][0] addresses = interfaces[0]["PrivateIpAddresses"][0]
@ -3067,8 +3065,9 @@ def test_run_instance_and_associate_public_ip():
ImageId=EXAMPLE_AMI_ID, ImageId=EXAMPLE_AMI_ID,
MinCount=1, MinCount=1,
MaxCount=1, MaxCount=1,
NetworkInterfaces=[{"DeviceIndex": 0, "AssociatePublicIpAddress": False}], NetworkInterfaces=[
SubnetId=subnet.id, {"DeviceIndex": 0, "SubnetId": subnet.id, "AssociatePublicIpAddress": False}
],
)[0] )[0]
interfaces = instance.network_interfaces_attribute interfaces = instance.network_interfaces_attribute
addresses = interfaces[0]["PrivateIpAddresses"][0] addresses = interfaces[0]["PrivateIpAddresses"][0]
@ -3081,8 +3080,9 @@ def test_run_instance_and_associate_public_ip():
ImageId=EXAMPLE_AMI_ID, ImageId=EXAMPLE_AMI_ID,
MinCount=1, MinCount=1,
MaxCount=1, MaxCount=1,
NetworkInterfaces=[{"DeviceIndex": 0, "AssociatePublicIpAddress": True}], NetworkInterfaces=[
SubnetId=subnet.id, {"DeviceIndex": 0, "SubnetId": subnet.id, "AssociatePublicIpAddress": True}
],
)[0] )[0]
interfaces = instance.network_interfaces_attribute interfaces = instance.network_interfaces_attribute
addresses = interfaces[0]["PrivateIpAddresses"][0] addresses = interfaces[0]["PrivateIpAddresses"][0]
@ -3094,6 +3094,27 @@ def test_run_instance_and_associate_public_ip():
addresses["Association"].should.have.key("PublicIp") addresses["Association"].should.have.key("PublicIp")
@mock_ec2
def test_run_instance_cannot_have_subnet_and_networkinterface_parameter():
ec2 = boto3.resource("ec2", "us-west-1")
vpc = ec2.create_vpc(CidrBlock="10.0.0.0/16")
subnet = ec2.create_subnet(VpcId=vpc.id, CidrBlock="10.0.0.0/18")
with pytest.raises(ClientError) as exc:
ec2.create_instances(
ImageId=EXAMPLE_AMI_ID,
MinCount=1,
MaxCount=1,
SubnetId=subnet.id,
NetworkInterfaces=[{"DeviceIndex": 0}],
)
err = exc.value.response["Error"]
err["Code"].should.equal("InvalidParameterCombination")
err["Message"].should.equal(
"Network interfaces and an instance-level subnet ID may not be specified on the same request"
)
@mock_ec2 @mock_ec2
def test_describe_instances_dryrun(): def test_describe_instances_dryrun():
client = boto3.client("ec2", region_name="us-east-1") client = boto3.client("ec2", region_name="us-east-1")