Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 20 additions & 20 deletions moto/ec2/models/fleets.py
Original file line number Diff line number Diff line change
Expand Up @@ -171,31 +171,31 @@ def instances(self) -> Optional[list[dict[str, Any]]]:
"""
Return instances for instant fleets, None for other fleet types.
This is part of the CreateFleet response for instant fleets only.
AWS groups instances that share the same launch spec and lifecycle into
a single entry with aggregated InstanceIds.
"""
if self.fleet_type != "instant":
return None

instances = []

# Process on-demand instances
for item in self.on_demand_instances:
instance_data = self._build_instance_data(
instance=item["instance"],
lifecycle="on-demand",
launch_spec=item.get("launch_spec"),
)
instances.append(instance_data)

# Process spot instances
for spot_request in self.spot_requests:
instance_data = self._build_instance_data(
instance=spot_request.instance,
lifecycle="spot",
launch_spec=spot_request.launch_spec,
)
instances.append(instance_data)
# Flatten all launched instances into (lifecycle, launch_spec, instance) tuples.
# SpotFleetLaunchSpec is hashable and compares by content, so identical configs
# collapse into one group entry, matching AWS behaviour.
all_items: list[tuple[str, Optional[SpotFleetLaunchSpec], Any]] = [
("on-demand", item.get("launch_spec"), item["instance"])
for item in self.on_demand_instances
] + [("spot", req.launch_spec, req.instance) for req in self.spot_requests]

groups: dict[tuple[str, Optional[SpotFleetLaunchSpec]], dict[str, Any]] = {}
for lifecycle, launch_spec, instance in all_items:
key = (lifecycle, launch_spec)
if key not in groups:
groups[key] = self._build_instance_data(
instance, lifecycle, launch_spec
)
else:
groups[key]["InstanceIds"].append(instance.id)

return instances
return list(groups.values())

@staticmethod
def _build_instance_data(
Expand Down
25 changes: 16 additions & 9 deletions tests/test_ec2/test_fleets.py
Original file line number Diff line number Diff line change
Expand Up @@ -833,18 +833,25 @@ def test_create_fleet_api():
assert fleet_res["FleetId"].startswith("fleet-") is True

assert "Instances" in fleet_res
assert len(fleet_res["Instances"]) == 3

instance_ids = [i["InstanceIds"] for i in fleet_res["Instances"]]
for instance_id in instance_ids:
assert instance_id[0].startswith("i-") is True
instances = fleet_res["Instances"]

instance_types = [i["InstanceType"] for i in fleet_res["Instances"]]
assert instance_types == ["t2.micro", "t2.micro", "t2.micro"]
# AWS groups instances with the same config (lifecycle/template) into one entry;
# with 1 on-demand + 2 spot we expect 2 groups
assert len(instances) == 2

# Total instance IDs across all groups should equal the requested capacity
all_instance_ids = [iid for i in instances for iid in i["InstanceIds"]]
assert len(all_instance_ids) == 3
for instance_id in all_instance_ids:
assert instance_id.startswith("i-") is True

all_instance_types = {i["InstanceType"] for i in instances}
assert all_instance_types == {"t2.micro"}

lifecycle = [i["Lifecycle"] for i in fleet_res["Instances"]]
assert "spot" in lifecycle
assert "on-demand" in lifecycle
lifecycles = {i["Lifecycle"] for i in instances}
assert "spot" in lifecycles
assert "on-demand" in lifecycles


@mock_aws
Expand Down
Loading