You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用moto mock EC2时指定SecurityGroupIds启动实例报错

moto mock EC2 传SecurityGroupIds启动实例报'NoneType' object has no attribute 'id' 修复方案

问题根因

报错本质是mock环境资源不匹配:

  • moto的EC2 mock是完全隔离的临时环境,不会同步真实AWS账号下的任何资源,业务代码里硬编码的安全组IDsg-0dc0a634bc153adc8在mock环境中不存在
  • 测试代码中创建安全组时没有捕获moto动态生成的安全组ID,也没有让业务代码使用这个动态ID,run_instances根据传入的ID查不到对应安全组,返回None后取id属性就触发了报错
  • 注释掉SecurityGroupIds参数后,mock会自动给实例关联默认安全组,所以能正常运行

修复方案

推荐方案:重构业务代码支持配置注入(符合单元测试规范)

不要在业务代码里硬编码安全组ID、镜像ID、密钥名这类环境相关的配置,改成初始化时可传入的参数,测试时传入mock环境动态生成的资源ID即可。

修改后的被测业务代码

REGION_NAME = "eu-central-1"
INSTANCE_TYPE = "t2.micro"

class aws_manager:
    def __init__(self, security_group_id: str, image_id: str, key_name: str) -> None:
        self.ec2 = boto3.client('ec2', region_name=REGION_NAME)
        self.running_instances = []
        self.security_group_id = security_group_id
        self.image_id = image_id
        self.key_name = key_name

    def launchInstances(self, count: int):
        resp = self.ec2.run_instances(
            ImageId=self.image_id,
            MinCount=count,
            MaxCount=count,
            InstanceType=INSTANCE_TYPE,
            KeyName=self.key_name,
            SecurityGroupIds=[self.security_group_id]
        )
        instances = resp["Instances"]
        self.running_instances.extend(instances)
        return instances

修改后的测试代码

@mock_ec2
def test_create_instances_with_decorator(self):
    instance_count = 2
    test_image_id = 'ami-0df635fd7dea02388'
    test_key_name = 'test-keypair'
    client = boto3.client('ec2', region_name='eu-central-1')
    
    # 提前创建测试依赖的资源:密钥对
    client.create_key_pair(KeyName=test_key_name)
    # 创建安全组,捕获moto动态生成的安全组ID
    sg_resp = client.create_security_group(
        Description="my-test-group",
        GroupName="launch-wizard-1"
    )
    test_sg_id = sg_resp["GroupId"]

    # 初始化业务类时注入测试环境的资源配置
    mngr = aws_manager(
        security_group_id=test_sg_id,
        image_id=test_image_id,
        key_name=test_key_name
    )
    mngr.launchInstances(instance_count)

    instances = client.describe_instances()['Reservations'][0]['Instances']
    assert len(instances) == instance_count
    assert instances[0]['ImageId'] == test_image_id
    # 校验安全组关联正确
    assert instances[0]['SecurityGroups'][0]['GroupId'] == test_sg_id

临时方案:不修改业务硬编码,在mock环境创建ID匹配的安全组

如果暂时没法重构业务硬编码,moto 4.0+版本支持在创建安全组时手动指定GroupId,直接创建和业务代码硬编码ID完全一致的安全组即可。
测试代码中创建安全组的部分改成:

client.create_security_group(
    Description="my-test-group",
    GroupName="launch-wizard-1",
    GroupId="sg-0dc0a634bc153adc8" # 和业务代码硬编码的安全组ID完全一致
)

注意:还需要同步创建业务代码里硬编码的KeyName、确保用到的ImageId在mock环境中存在,否则会触发其他资源不存在的报错。

原有代码的其他问题

  • 业务代码中run_instances返回后只取了["Instances"][0],当启动多台实例时会丢失其他实例数据,属于逻辑bug
  • 原测试代码没有创建KeyName对应的密钥对,就算安全组问题修复,也会因为密钥对不存在报错
  • 原测试代码定义的instance_count = 1和实际传入launchInstances的参数2、断言的实例数2不一致,属于冗余无效变量

内容的提问来源于stack exchange,提问作者codedor

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 05:21:22