使用moto mock EC2时指定SecurityGroupIds启动实例报错
moto mock EC2 传SecurityGroupIds启动实例报'NoneType' object has no attribute 'id' 修复方案
问题根因
报错本质是mock环境资源不匹配:
- moto的EC2 mock是完全隔离的临时环境,不会同步真实AWS账号下的任何资源,业务代码里硬编码的安全组ID
sg-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
相关产品推荐
相关产品推荐

