Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

EC2 instance with predefined IP and SubnetId: network interface stays around after instance termination #7896

Closed
wandhydrant opened this issue Jul 26, 2024 · 1 comment · Fixed by #7954

Comments

@wandhydrant
Copy link
Contributor

Hello, and many thanks for Moto!

I ran Moto against some use cases and found a few discrepancies between AWS and Moto. This is my first issue. I hope to have found a real bug, and not to have made a mistake.

When deploying EC2 instances where an IP address has been manually specified, and DeleteOnTermination is set to True, I found that Moto keeps the Network Interface around even after termination. When deploying a new EC2 instance with the same IP, this results in an exception "IP already in use". Here is example code to demonstrate this behaviour, ran with AlmaLinux 8 / Python 3.9.19 in a venv with only moto[server] 5.0.11:

import boto3
from moto.server import ThreadedMotoServer

server = ThreadedMotoServer()
server.start()
server._server.passthrough_errors = True  # otherwise we just see a 500

ec2_client = boto3.client("ec2", endpoint_url="http://localhost:5000")

existing_subnet_id = ec2_client.describe_subnets()["Subnets"][0]["SubnetId"]

def create_instance():
    instance = ec2_client.run_instances(
        MaxCount=1,
        MinCount=1,
        ImageId="ami-bb9a6bc2",
        InstanceType="t3a.small",
        NetworkInterfaces=[{
            "DeleteOnTermination": True,
            "PrivateIpAddress": "172.31.0.5",
            "DeviceIndex": 0,
            "SubnetId": existing_subnet_id
        }]
    )["Instances"][0]    
    ec2_client.get_waiter("instance_running").wait(InstanceIds=[instance["InstanceId"]])
    return instance

def get_interfaces():
    return ec2_client.describe_network_interfaces(
        Filters=[{"Name": "subnet-id", "Values": [existing_subnet_id]}]
    )["NetworkInterfaces"]
    
assert len(get_interfaces()) == 0

first_instance = create_instance()
assert len(get_interfaces()) == 1
    
ec2_client.terminate_instances(InstanceIds=[first_instance["InstanceId"]])
ec2_client.get_waiter("instance_terminated").wait(InstanceIds=[first_instance["InstanceId"]])

print("After termination:")
print(get_interfaces())
# => a network interface still exists, with "DeleteOnTermination": False

create_instance()
# => Exception("IP already in use")

server.stop()

This happens with the existing subnet (as in the code above) or with a newly created subnet. However, if I do not specify the SubnetId in the run_instances() call, the same existing subnet is used, the network interface is still visible after termination, it still shows DeleteOnTermination=False, but the second run_instances() succeeds and there is no duplicate IP exception.

@bblommers
Copy link
Collaborator

Hi @wandhydrant, thanks for raising this and for providing the code sample! I've opened a PR with a fix - NIC's (with DeleteOnTermination=True) are now deleted after an instance is terminated.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants