Skip to content

Commit

Permalink
EC2: Streamline output of run_instances()/describe_instances() (#6906)
Browse files Browse the repository at this point in the history
  • Loading branch information
bblommers authored Oct 13, 2023
1 parent de714eb commit f59e178
Show file tree
Hide file tree
Showing 2 changed files with 77 additions and 179 deletions.
251 changes: 72 additions & 179 deletions moto/ec2/responses/instances.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ def describe_instances(self) -> str:
account_id=self.current_account,
reservations=reservations_resp,
next_token=next_token,
run_instances=False,
)
.replace("True", "true")
.replace("False", "false")
Expand Down Expand Up @@ -128,7 +129,9 @@ def run_instances(self) -> str:

template = self.response_template(EC2_RUN_INSTANCES)
return template.render(
account_id=self.current_account, reservation=new_reservation
account_id=self.current_account,
reservation=new_reservation,
run_instances=True,
)

def terminate_instances(self) -> str:
Expand Down Expand Up @@ -443,25 +446,20 @@ def _convert_to_bool(bool_str: Any) -> bool: # type: ignore[misc]
},
}

EC2_RUN_INSTANCES = """<RunInstancesResponse xmlns="http://ec2.amazonaws.com/doc/2013-10-15/">
<requestId>59dbff89-35bd-4eac-99ed-be587EXAMPLE</requestId>
<reservationId>{{ reservation.id }}</reservationId>
<ownerId>{{ account_id }}</ownerId>
<groupSet>
<item>
<groupId>sg-245f6a01</groupId>
<groupName>default</groupName>
</item>
</groupSet>
<instancesSet>
{% for instance in reservation.instances %}
<item>
INSTANCE_TEMPLATE = """<item>
<instanceId>{{ instance.id }}</instanceId>
<imageId>{{ instance.image_id }}</imageId>
{% if run_instances %}
<instanceState>
<code>0</code>
<name>pending</name>
</instanceState>
{% else %}
<instanceState>
<code>{{ instance._state.code }}</code>
<name>{{ instance._state.name }}</name>
</instanceState>
{% endif %}
<privateDnsName>{{ instance.private_dns }}</privateDnsName>
<publicDnsName>{{ instance.public_dns }}</publicDnsName>
<dnsName>{{ instance.public_dns }}</dnsName>
Expand Down Expand Up @@ -509,33 +507,60 @@ def _convert_to_bool(bool_str: Any) -> bool: # type: ignore[misc]
<groupSet>
{% for group in instance.dynamic_group_list %}
<item>
{% if group.id %}
<groupId>{{ group.id }}</groupId>
<groupName>{{ group.name }}</groupName>
{% else %}
<groupId>{{ group }}</groupId>
{% endif %}
</item>
{% endfor %}
</groupSet>
{% if instance.platform %}
<platform>{{ instance.platform }}</platform>
{% endif %}
<virtualizationType>{{ instance.virtualization_type }}</virtualizationType>
<stateReason>
<code>{{ instance._state_reason.code }}</code>
<message>{{ instance._state_reason.message }}</message>
</stateReason>
<architecture>{{ instance.architecture }}</architecture>
<kernelId>{{ instance.kernel }}</kernelId>
<clientToken/>
<rootDeviceType>ebs</rootDeviceType>
<rootDeviceName>/dev/sda1</rootDeviceName>
<blockDeviceMapping>
{% for device_name,deviceobject in instance.get_block_device_mapping %}
<item>
<deviceName>{{ device_name }}</deviceName>
<ebs>
<volumeId>{{ deviceobject.volume_id }}</volumeId>
<status>{{ deviceobject.status }}</status>
<attachTime>{{ deviceobject.attach_time }}</attachTime>
<deleteOnTermination>{{ deviceobject.delete_on_termination }}</deleteOnTermination>
<size>{{deviceobject.size}}</size>
</ebs>
</item>
{% endfor %}
</blockDeviceMapping>
<clientToken>ABCDE{{ account_id }}3</clientToken>
<hypervisor>xen</hypervisor>
<ebsOptimized>false</ebsOptimized>
{% if instance.hibernation_options %}
<hibernationOptions>
<configured>{{ instance.hibernation_options.get("Configured") }}</configured>
</hibernationOptions>
{% endif %}
{% if instance.get_tags() %}
<tagSet>
{% for tag in instance.get_tags() %}
<item>
<resourceId>{{ tag.resource_id }}</resourceId>
<resourceType>{{ tag.resource_type }}</resourceType>
<key>{{ tag.key }}</key>
<value>{{ tag.value }}</value>
</item>
{% endfor %}
</tagSet>
{% endif %}
<networkInterfaceSet>
{% for nic in instance.nics.values() %}
<item>
Expand All @@ -553,8 +578,12 @@ def _convert_to_bool(bool_str: Any) -> bool: # type: ignore[misc]
<groupSet>
{% for group in nic.group_set %}
<item>
<groupId>{{ group.id }}</groupId>
<groupName>{{ group.name }}</groupName>
{% if group.id %}
<groupId>{{ group.id }}</groupId>
<groupName>{{ group.name }}</groupName>
{% else %}
<groupId>{{ group }}</groupId>
{% endif %}
</item>
{% endfor %}
</groupSet>
Expand Down Expand Up @@ -586,12 +615,31 @@ def _convert_to_bool(bool_str: Any) -> bool: # type: ignore[misc]
</item>
{% endfor %}
</networkInterfaceSet>
</item>
</item>"""

EC2_RUN_INSTANCES = (
"""<RunInstancesResponse xmlns="http://ec2.amazonaws.com/doc/2013-10-15/">
<requestId>59dbff89-35bd-4eac-99ed-be587EXAMPLE</requestId>
<reservationId>{{ reservation.id }}</reservationId>
<ownerId>{{ account_id }}</ownerId>
<groupSet>
<item>
<groupId>sg-245f6a01</groupId>
<groupName>default</groupName>
</item>
</groupSet>
<instancesSet>
{% for instance in reservation.instances %}
"""
+ INSTANCE_TEMPLATE
+ """
{% endfor %}
</instancesSet>
</RunInstancesResponse>"""
)

EC2_DESCRIBE_INSTANCES = """<DescribeInstancesResponse xmlns="http://ec2.amazonaws.com/doc/2013-10-15/">
EC2_DESCRIBE_INSTANCES = (
"""<DescribeInstancesResponse xmlns="http://ec2.amazonaws.com/doc/2013-10-15/">
<requestId>fdcdcab1-ae5c-489e-9c33-4637c5dda355</requestId>
<reservationSet>
{% for reservation in reservations %}
Expand All @@ -612,165 +660,9 @@ def _convert_to_bool(bool_str: Any) -> bool: # type: ignore[misc]
</groupSet>
<instancesSet>
{% for instance in reservation.instances %}
<item>
<instanceId>{{ instance.id }}</instanceId>
<imageId>{{ instance.image_id }}</imageId>
<instanceState>
<code>{{ instance._state.code }}</code>
<name>{{ instance._state.name }}</name>
</instanceState>
<privateDnsName>{{ instance.private_dns }}</privateDnsName>
<publicDnsName>{{ instance.public_dns }}</publicDnsName>
<dnsName>{{ instance.public_dns }}</dnsName>
<reason>{{ instance._reason }}</reason>
{% if instance.key_name is not none %}
<keyName>{{ instance.key_name }}</keyName>
{% endif %}
<ebsOptimized>{{ instance.ebs_optimized }}</ebsOptimized>
<amiLaunchIndex>{{ instance.ami_launch_index }}</amiLaunchIndex>
<productCodes/>
<instanceType>{{ instance.instance_type }}</instanceType>
{% if instance.iam_instance_profile %}
<iamInstanceProfile>
<arn>{{ instance.iam_instance_profile['Arn'] }}</arn>
<id>{{ instance.iam_instance_profile['Id'] }}</id>
</iamInstanceProfile>
{% endif %}
<launchTime>{{ instance.launch_time }}</launchTime>
{% if instance.lifecycle %}
<instanceLifecycle>{{ instance.lifecycle }}</instanceLifecycle>
{% endif %}
<placement>
{% if instance.placement_hostid %}<hostId>{{ instance.placement_hostid }}</hostId>{% endif %}
<availabilityZone>{{ instance.placement }}</availabilityZone>
<groupName/>
<tenancy>default</tenancy>
</placement>
{% if instance.platform %}
<platform>{{ instance.platform }}</platform>
{% endif %}
<monitoring>
<state>{{ instance.monitoring_state }}</state>
</monitoring>
{% if instance.subnet_id %}
<subnetId>{{ instance.subnet_id }}</subnetId>
{% elif instance.nics[0].subnet.id %}
<subnetId>{{ instance.nics[0].subnet.id }}</subnetId>
{% endif %}
{% if instance.vpc_id %}
<vpcId>{{ instance.vpc_id }}</vpcId>
{% elif instance.nics[0].subnet.vpc_id %}
<vpcId>{{ instance.nics[0].subnet.vpc_id }}</vpcId>
{% endif %}
<privateIpAddress>{{ instance.private_ip }}</privateIpAddress>
{% if instance.nics[0].public_ip %}
<ipAddress>{{ instance.nics[0].public_ip }}</ipAddress>
{% endif %}
<sourceDestCheck>{{ instance.source_dest_check }}</sourceDestCheck>
<groupSet>
{% for group in instance.dynamic_group_list %}
<item>
{% if group.id %}
<groupId>{{ group.id }}</groupId>
<groupName>{{ group.name }}</groupName>
{% else %}
<groupId>{{ group }}</groupId>
{% endif %}
</item>
{% endfor %}
</groupSet>
<stateReason>
<code>{{ instance._state_reason.code }}</code>
<message>{{ instance._state_reason.message }}</message>
</stateReason>
<architecture>{{ instance.architecture }}</architecture>
<kernelId>{{ instance.kernel }}</kernelId>
<rootDeviceType>ebs</rootDeviceType>
<rootDeviceName>/dev/sda1</rootDeviceName>
<blockDeviceMapping>
{% for device_name,deviceobject in instance.get_block_device_mapping %}
<item>
<deviceName>{{ device_name }}</deviceName>
<ebs>
<volumeId>{{ deviceobject.volume_id }}</volumeId>
<status>{{ deviceobject.status }}</status>
<attachTime>{{ deviceobject.attach_time }}</attachTime>
<deleteOnTermination>{{ deviceobject.delete_on_termination }}</deleteOnTermination>
<size>{{deviceobject.size}}</size>
</ebs>
</item>
{% endfor %}
</blockDeviceMapping>
<virtualizationType>{{ instance.virtualization_type }}</virtualizationType>
<clientToken>ABCDE{{ account_id }}3</clientToken>
{% if instance.get_tags() %}
<tagSet>
{% for tag in instance.get_tags() %}
<item>
<resourceId>{{ tag.resource_id }}</resourceId>
<resourceType>{{ tag.resource_type }}</resourceType>
<key>{{ tag.key }}</key>
<value>{{ tag.value }}</value>
</item>
{% endfor %}
</tagSet>
{% endif %}
<hypervisor>xen</hypervisor>
<networkInterfaceSet>
{% for nic in instance.nics.values() %}
<item>
<networkInterfaceId>{{ nic.id }}</networkInterfaceId>
{% if nic.subnet %}
<subnetId>{{ nic.subnet.id }}</subnetId>
<vpcId>{{ nic.subnet.vpc_id }}</vpcId>
{% endif %}
<description>Primary network interface</description>
<ownerId>{{ account_id }}</ownerId>
<status>in-use</status>
<macAddress>1b:2b:3c:4d:5e:6f</macAddress>
<privateIpAddress>{{ nic.private_ip_address }}</privateIpAddress>
<sourceDestCheck>{{ instance.source_dest_check }}</sourceDestCheck>
<groupSet>
{% for group in nic.group_set %}
<item>
{% if group.id %}
<groupId>{{ group.id }}</groupId>
<groupName>{{ group.name }}</groupName>
{% else %}
<groupId>{{ group }}</groupId>
{% endif %}
</item>
{% endfor %}
</groupSet>
<attachment>
<attachmentId>{{ nic.attachment_id }}</attachmentId>
<deviceIndex>{{ nic.device_index }}</deviceIndex>
<status>attached</status>
<attachTime>2015-01-01T00:00:00Z</attachTime>
<deleteOnTermination>true</deleteOnTermination>
</attachment>
{% if nic.public_ip %}
<association>
<publicIp>{{ nic.public_ip }}</publicIp>
<ipOwnerId>{{ account_id }}</ipOwnerId>
</association>
{% endif %}
<privateIpAddressesSet>
<item>
<privateIpAddress>{{ nic.private_ip_address }}</privateIpAddress>
<primary>true</primary>
{% if nic.public_ip %}
<association>
<publicIp>{{ nic.public_ip }}</publicIp>
<ipOwnerId>{{ account_id }}</ipOwnerId>
</association>
{% endif %}
</item>
</privateIpAddressesSet>
</item>
{% endfor %}
</networkInterfaceSet>
</item>
"""
+ INSTANCE_TEMPLATE
+ """
{% endfor %}
</instancesSet>
</item>
Expand All @@ -780,6 +672,7 @@ def _convert_to_bool(bool_str: Any) -> bool: # type: ignore[misc]
<nextToken>{{ next_token }}</nextToken>
{% endif %}
</DescribeInstancesResponse>"""
)

EC2_TERMINATE_INSTANCES = """
<TerminateInstancesResponse xmlns="http://ec2.amazonaws.com/doc/2013-10-15/">
Expand Down
5 changes: 5 additions & 0 deletions tests/test_ec2/test_instances.py
Original file line number Diff line number Diff line change
Expand Up @@ -1192,6 +1192,7 @@ def test_run_instance_with_security_group_id():
@mock_ec2
@pytest.mark.parametrize("hibernate", [True, False])
def test_run_instance_with_additional_args(hibernate):
client = boto3.client("ec2", region_name="us-east-1")
ec2 = boto3.resource("ec2", region_name="us-east-1")
instance = ec2.create_instances(
ImageId=EXAMPLE_AMI_ID,
Expand All @@ -1206,6 +1207,10 @@ def test_run_instance_with_additional_args(hibernate):
assert instance.placement["AvailabilityZone"] == "us-east-1b"
assert instance.hibernation_options == {"Configured": hibernate}

reservations = client.describe_instances(InstanceIds=[instance.id])["Reservations"]
instance = reservations[0]["Instances"][0]
assert instance["HibernationOptions"] == {"Configured": hibernate}


@mock_ec2
def test_run_instance_with_default_placement():
Expand Down

0 comments on commit f59e178

Please sign in to comment.