Skip to content
Merged
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
8 changes: 6 additions & 2 deletions src/dstack/_internal/cli/commands/fleet.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,13 +95,17 @@ def _command(self, args: argparse.Namespace):
def _list(self, args: argparse.Namespace):
fleets = self.api.client.fleets.list(self.api.project)
if not args.watch:
print_fleets_table(fleets, verbose=args.verbose)
print_fleets_table(fleets, current_project=self.api.project, verbose=args.verbose)
return

try:
with Live(console=console, refresh_per_second=LIVE_TABLE_REFRESH_RATE_PER_SEC) as live:
while True:
live.update(get_fleets_table(fleets, verbose=args.verbose))
live.update(
get_fleets_table(
fleets, current_project=self.api.project, verbose=args.verbose
)
)
time.sleep(LIVE_TABLE_PROVISION_INTERVAL_SECS)
fleets = self.api.client.fleets.list(self.api.project)
except KeyboardInterrupt:
Expand Down
6 changes: 4 additions & 2 deletions src/dstack/_internal/cli/services/configurators/fleet.py
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ def _apply_plan(self, plan: FleetPlan, command_args: argparse.Namespace):
f"Provisioning [code]{fleet.name}[/]...", console=console
) as live:
while not _finished_provisioning(fleet):
table = get_fleets_table([fleet])
table = get_fleets_table([fleet], current_project=self.api.project)
live.update(table)
time.sleep(LIVE_TABLE_PROVISION_INTERVAL_SECS)
fleet = self.api.client.fleets.get(self.api.project, fleet.name)
Expand All @@ -159,6 +159,7 @@ def _apply_plan(self, plan: FleetPlan, command_args: argparse.Namespace):
[fleet],
verbose=_fleet_has_failed_instances(fleet),
format_date=local_time,
current_project=self.api.project,
)
)
if _fleet_has_failed_instances(fleet):
Expand Down Expand Up @@ -242,7 +243,7 @@ def _apply_plan_on_old_server(self, plan: FleetPlan, command_args: argparse.Name
f"Provisioning [code]{fleet.name}[/]...", console=console
) as live:
while not _finished_provisioning(fleet):
table = get_fleets_table([fleet])
table = get_fleets_table([fleet], current_project=self.api.project)
live.update(table)
time.sleep(LIVE_TABLE_PROVISION_INTERVAL_SECS)
fleet = self.api.client.fleets.get(self.api.project, fleet.name)
Expand All @@ -260,6 +261,7 @@ def _apply_plan_on_old_server(self, plan: FleetPlan, command_args: argparse.Name
[fleet],
verbose=_fleet_has_failed_instances(fleet),
format_date=local_time,
current_project=self.api.project,
)
)
if _fleet_has_failed_instances(fleet):
Expand Down
15 changes: 11 additions & 4 deletions src/dstack/_internal/cli/utils/fleet.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,16 @@
from dstack._internal.utils.common import DateFormatter, pretty_date


def print_fleets_table(fleets: List[Fleet], verbose: bool = False) -> None:
console.print(get_fleets_table(fleets, verbose=verbose))
def print_fleets_table(fleets: List[Fleet], current_project: str, verbose: bool = False) -> None:
console.print(get_fleets_table(fleets, current_project=current_project, verbose=verbose))
console.print()


def get_fleets_table(
fleets: List[Fleet], verbose: bool = False, format_date: DateFormatter = pretty_date
fleets: List[Fleet],
current_project: str,
verbose: bool = False,
format_date: DateFormatter = pretty_date,
) -> Table:
table = Table(box=None)

Expand All @@ -40,6 +43,10 @@ def get_fleets_table(
config = fleet.spec.configuration
merged_profile = fleet.spec.merged_profile

name = fleet.name
if fleet.project_name != current_project:
name = f"{fleet.project_name}/{fleet.name}"

# Detect SSH fleet vs backend fleet
if config.ssh_config is not None:
# SSH fleet: fixed number of hosts, no cloud billing
Expand All @@ -65,7 +72,7 @@ def get_fleets_table(
nodes = f"{nodes} (cluster)"

fleet_row: Dict[Union[str, int], Any] = {
"NAME": fleet.name,
"NAME": name,
"NODES": nodes,
"BACKEND": backend,
"PRICE": max_price,
Expand Down
46 changes: 34 additions & 12 deletions src/tests/_internal/cli/utils/test_fleet.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ def create_backend_fleet(
gpu_count_max: int = 0,
instances: Optional[List[Instance]] = None,
status: FleetStatus = FleetStatus.ACTIVE,
project_name: str = "test-project",
) -> Fleet:
nodes = FleetNodesSpec(min=nodes_min, target=nodes_min, max=nodes_max)

Expand Down Expand Up @@ -154,7 +155,7 @@ def create_backend_fleet(
return Fleet(
id=uuid4(),
name=name,
project_name="test-project",
project_name=project_name,
spec=spec,
created_at=datetime(2023, 1, 2, 3, 4, 5, tzinfo=timezone.utc),
status=status,
Expand Down Expand Up @@ -222,7 +223,7 @@ def test_backend_fleet_without_verbose(self):
instances=[instance],
)

table = get_fleets_table([fleet], verbose=False)
table = get_fleets_table([fleet], current_project="test-project", verbose=False)
cells = get_table_cells(table)

assert len(cells) == 2 # 1 fleet row + 1 instance row
Expand Down Expand Up @@ -262,7 +263,7 @@ def test_backend_fleet_with_verbose(self):
instances=[instance],
)

table = get_fleets_table([fleet], verbose=True)
table = get_fleets_table([fleet], current_project="test-project", verbose=True)
cells = get_table_cells(table)

assert len(cells) == 2
Expand Down Expand Up @@ -310,7 +311,7 @@ def test_ssh_fleet_without_verbose(self):
instances=[instance1, instance2],
)

table = get_fleets_table([fleet], verbose=False)
table = get_fleets_table([fleet], current_project="test-project", verbose=False)
cells = get_table_cells(table)

assert len(cells) == 3 # 1 fleet row + 2 instance rows
Expand Down Expand Up @@ -345,7 +346,7 @@ def test_ssh_fleet_with_verbose(self):
instances=[instance],
)

table = get_fleets_table([fleet], verbose=True)
table = get_fleets_table([fleet], current_project="test-project", verbose=True)
cells = get_table_cells(table)

assert len(cells) == 2
Expand Down Expand Up @@ -395,7 +396,9 @@ def test_mixed_fleets(self):
instances=[ssh_instance],
)

table = get_fleets_table([backend_fleet, ssh_fleet], verbose=False)
table = get_fleets_table(
[backend_fleet, ssh_fleet], current_project="test-project", verbose=False
)
cells = get_table_cells(table)

assert len(cells) == 4 # 2 fleet rows + 2 instance rows
Expand Down Expand Up @@ -433,7 +436,9 @@ def test_fleet_status_colors(self):
name="terminating", status=FleetStatus.TERMINATING, instances=[terminating_instance]
)

table = get_fleets_table([active_fleet, terminating_fleet], verbose=False)
table = get_fleets_table(
[active_fleet, terminating_fleet], current_project="test-project", verbose=False
)

active_style = get_table_cell_style(table, "STATUS", 0)
assert active_style == "bold white"
Expand All @@ -451,7 +456,7 @@ def test_instance_status_colors(self):
instances=[idle_instance, busy_instance],
)

table = get_fleets_table([fleet], verbose=False)
table = get_fleets_table([fleet], current_project="test-project", verbose=False)

idle_style = get_table_cell_style(table, "STATUS", 1)
assert idle_style == "bold sea_green3"
Expand All @@ -462,7 +467,7 @@ def test_instance_status_colors(self):
def test_empty_fleet(self):
fleet = create_backend_fleet(name="empty-fleet", instances=[])

table = get_fleets_table([fleet], verbose=False)
table = get_fleets_table([fleet], current_project="test-project", verbose=False)
cells = get_table_cells(table)

assert len(cells) == 1
Expand All @@ -474,7 +479,7 @@ def test_fleet_with_max_price(self):
max_price=5.0,
)

table = get_fleets_table([fleet], verbose=False)
table = get_fleets_table([fleet], current_project="test-project", verbose=False)
cells = get_table_cells(table)

assert cells[0]["PRICE"] == "$0..$5"
Expand All @@ -485,7 +490,7 @@ def test_fleet_with_multiple_backends(self):
backends=[BackendType.AWS, BackendType.GCP, BackendType.AZURE],
)

table = get_fleets_table([fleet], verbose=False)
table = get_fleets_table([fleet], current_project="test-project", verbose=False)
cells = get_table_cells(table)

assert cells[0]["BACKEND"] == "aws, gcp, azure"
Expand All @@ -496,7 +501,24 @@ def test_fleet_with_any_backend(self):
backends=None,
)

table = get_fleets_table([fleet], verbose=False)
table = get_fleets_table([fleet], current_project="test-project", verbose=False)
cells = get_table_cells(table)

assert cells[0]["BACKEND"] == "*"

def test_with_imported_fleet(self):
current_project_fleet = create_backend_fleet(
name="current-fleet", project_name="current-project"
)
other_project_fleet = create_backend_fleet(
name="other-fleet", project_name="other-project"
)
table = get_fleets_table(
[current_project_fleet, other_project_fleet],
verbose=False,
current_project="current-project",
)
cells = get_table_cells(table)
assert len(cells) == 2
assert cells[0]["NAME"] == "current-fleet"
assert cells[1]["NAME"] == "other-project/other-fleet"