Skip to content

Commit cf55e67

Browse files
ec2: Add support for security groups from NetworkInterfaces in RunInstances (#145)
1 parent 86e1908 commit cf55e67

2 files changed

Lines changed: 128 additions & 3 deletions

File tree

moto/ec2/models/instances.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -773,6 +773,7 @@ def run_instances(
773773
The KeyPair-parameter can be validated, to see if it is a known key-pair.
774774
Enable this validation by setting the environment variable `MOTO_ENABLE_KEYPAIR_VALIDATION=true`
775775
"""
776+
template_nics: list[dict[str, Any]] = []
776777
if launch_template := kwargs.get("launch_template"):
777778
tmpl = self._get_template_from_args(launch_template).data
778779

@@ -795,6 +796,15 @@ def run_instances(
795796
template_sgs := tmpl.get("SecurityGroups")
796797
):
797798
security_group_names = template_sgs
799+
template_nics = tmpl.get("NetworkInterfaces") or []
800+
801+
if not kwargs.get("security_group_ids") and not security_group_names:
802+
if nic_groups := [
803+
g
804+
for nic in (kwargs.get("nics") or template_nics)
805+
for g in (nic.get("Groups") or [])
806+
]:
807+
kwargs["security_group_ids"] = nic_groups
798808

799809
location_type = "availability-zone" if kwargs.get("placement") else "region"
800810
default_region = "us-east-1"

tests/test_ec2/test_instances.py

Lines changed: 118 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3124,6 +3124,67 @@ def test_block_device_status_conversion():
31243124
assert Instance.get_block_device_status("deleting") == "deleting"
31253125

31263126

3127+
def _schedule_terminate_and_wait(ec2_client, instance_id, cleanups):
3128+
def _terminate():
3129+
ec2_client.terminate_instances(InstanceIds=[instance_id])
3130+
ec2_client.get_waiter("instance_terminated").wait(InstanceIds=[instance_id])
3131+
3132+
cleanups.append(_terminate)
3133+
3134+
3135+
@ec2_aws_verified()
3136+
@pytest.mark.aws_verified
3137+
def test_run_instances__security_groups_from_request_network_interfaces(
3138+
valid_ami, cleanups, ec2_client=None
3139+
):
3140+
vpc_id = ec2_client.create_vpc(CidrBlock="10.0.0.0/16")["Vpc"]["VpcId"]
3141+
cleanups.append(lambda: ec2_client.delete_vpc(VpcId=vpc_id))
3142+
3143+
subnet_id = ec2_client.create_subnet(
3144+
VpcId=vpc_id,
3145+
CidrBlock="10.0.1.0/24",
3146+
AvailabilityZone=ec2_client.meta.region_name + "a",
3147+
)["Subnet"]["SubnetId"]
3148+
cleanups.append(lambda: ec2_client.delete_subnet(SubnetId=subnet_id))
3149+
3150+
sg_id = ec2_client.create_security_group(
3151+
GroupName=f"test-sg-{str(uuid4())[0:6]}",
3152+
Description="test",
3153+
VpcId=vpc_id,
3154+
)["GroupId"]
3155+
cleanups.append(lambda: ec2_client.delete_security_group(GroupId=sg_id))
3156+
3157+
instance = ec2_client.run_instances(
3158+
MinCount=1,
3159+
MaxCount=1,
3160+
ImageId=valid_ami,
3161+
NetworkInterfaces=[
3162+
{"DeviceIndex": 0, "SubnetId": subnet_id, "Groups": [sg_id]}
3163+
],
3164+
)["Instances"][0]
3165+
instance_id = instance["InstanceId"]
3166+
_schedule_terminate_and_wait(ec2_client, instance_id, cleanups)
3167+
3168+
sg_ids = [g["GroupId"] for g in instance["SecurityGroups"]]
3169+
assert sg_id in sg_ids
3170+
nic_sg_ids = [g["GroupId"] for g in instance["NetworkInterfaces"][0]["Groups"]]
3171+
assert sg_id in nic_sg_ids
3172+
3173+
described = ec2_client.describe_instances(InstanceIds=[instance_id])
3174+
sg_ids = [
3175+
g["GroupId"]
3176+
for g in described["Reservations"][0]["Instances"][0]["SecurityGroups"]
3177+
]
3178+
assert sg_id in sg_ids
3179+
nic_sg_ids = [
3180+
g["GroupId"]
3181+
for g in described["Reservations"][0]["Instances"][0]["NetworkInterfaces"][0][
3182+
"Groups"
3183+
]
3184+
]
3185+
assert sg_id in nic_sg_ids
3186+
3187+
31273188
class TestCreateInstanceFromLaunchTemplate:
31283189
_LT_USER_DATA_SCRIPT = b"#!/bin/bash\necho from-template"
31293190
_LT_USER_DATA_B64 = base64.b64encode(_LT_USER_DATA_SCRIPT).decode()
@@ -3383,9 +3444,7 @@ def test_run_instances__security_group_ids_from_launch_template(
33833444
ec2_client, template_name=lt_name, ImageId=valid_ami, SubnetId=subnet_id
33843445
)
33853446
instance_id = instance["InstanceId"]
3386-
cleanups.append(
3387-
lambda: ec2_client.terminate_instances(InstanceIds=[instance_id])
3388-
)
3447+
_schedule_terminate_and_wait(ec2_client, instance_id, cleanups)
33893448

33903449
instance_sg_ids = [g["GroupId"] for g in instance["SecurityGroups"]]
33913450
assert sg_id in instance_sg_ids
@@ -3405,6 +3464,62 @@ def test_run_instances__security_group_ids_from_launch_template(
34053464
]
34063465
assert sg_id in instance_sg_ids
34073466

3467+
@ec2_aws_verified()
3468+
@pytest.mark.aws_verified
3469+
def test_run_instances__security_groups_from_launch_template_network_interfaces(
3470+
self, valid_ami, cleanups, ec2_client=None
3471+
):
3472+
"""
3473+
SecurityGroups specified in NetworkInterfaces[].Groups in a launch template
3474+
must appear in DescribeInstances SecurityGroups.
3475+
"""
3476+
vpc_id = ec2_client.create_vpc(CidrBlock="10.0.0.0/16")["Vpc"]["VpcId"]
3477+
cleanups.append(lambda: ec2_client.delete_vpc(VpcId=vpc_id))
3478+
3479+
subnet_id = ec2_client.create_subnet(
3480+
VpcId=vpc_id,
3481+
CidrBlock="10.0.1.0/24",
3482+
AvailabilityZone=ec2_client.meta.region_name + "a",
3483+
)["Subnet"]["SubnetId"]
3484+
cleanups.append(lambda: ec2_client.delete_subnet(SubnetId=subnet_id))
3485+
3486+
sg_id = ec2_client.create_security_group(
3487+
GroupName=f"test-sg-{str(uuid4())[0:6]}",
3488+
Description="test",
3489+
VpcId=vpc_id,
3490+
)["GroupId"]
3491+
cleanups.append(lambda: ec2_client.delete_security_group(GroupId=sg_id))
3492+
3493+
lt_name = str(uuid4())
3494+
ec2_client.create_launch_template(
3495+
LaunchTemplateName=lt_name,
3496+
LaunchTemplateData={
3497+
"InstanceType": "t2.micro",
3498+
"NetworkInterfaces": [
3499+
{"DeviceIndex": 0, "SubnetId": subnet_id, "Groups": [sg_id]}
3500+
],
3501+
},
3502+
)
3503+
cleanups.append(
3504+
lambda: ec2_client.delete_launch_template(LaunchTemplateName=lt_name)
3505+
)
3506+
3507+
instance = self._run_instance_from_template(
3508+
ec2_client, template_name=lt_name, ImageId=valid_ami
3509+
)
3510+
instance_id = instance["InstanceId"]
3511+
_schedule_terminate_and_wait(ec2_client, instance_id, cleanups)
3512+
3513+
sg_ids = [g["GroupId"] for g in instance["SecurityGroups"]]
3514+
assert sg_id in sg_ids
3515+
3516+
described = ec2_client.describe_instances(InstanceIds=[instance_id])
3517+
sg_ids = [
3518+
g["GroupId"]
3519+
for g in described["Reservations"][0]["Instances"][0]["SecurityGroups"]
3520+
]
3521+
assert sg_id in sg_ids
3522+
34083523
@ec2_aws_verified()
34093524
@pytest.mark.aws_verified
34103525
def test_create_instance_from_launch_template_single_template_version(

0 commit comments

Comments
 (0)