diff --git a/src/together/lib/cli/api/beta/clusters/remediations/list.py b/src/together/lib/cli/api/beta/clusters/remediations/list.py index 1ee325e8..a25618b7 100644 --- a/src/together/lib/cli/api/beta/clusters/remediations/list.py +++ b/src/together/lib/cli/api/beta/clusters/remediations/list.py @@ -26,7 +26,9 @@ Parameter(help="Filter by remediation mode. Can be used multiple times."), ] RemediationStateParameter = Annotated[ - Optional[list[Literal["PENDING_APPROVAL", "PENDING", "RUNNING", "SUCCEEDED", "FAILED", "CANCELLED", "AUTO_RESOLVED"]]], + Optional[ + list[Literal["PENDING_APPROVAL", "PENDING", "RUNNING", "SUCCEEDED", "FAILED", "CANCELLED", "AUTO_RESOLVED"]] + ], Parameter(help="Filter by remediation state. Can be used multiple times."), ] RemediationTriggerParameter = Annotated[ @@ -88,7 +90,7 @@ async def list( for remediation in response.remediations: table.add_row( format_datetime(remediation.create_time) if remediation.create_time else "-", - remediation.instance_id, + _format_instance(remediation.instance_id, remediation.instance_name), remediation.mode.replace("REMEDIATION_MODE_", ""), _colorize(remediation.state), remediation.trigger.replace("REMEDIATION_TRIGGER_", ""), @@ -116,3 +118,9 @@ def _colorize(state: str) -> str: } color = state_colors[state] if state in state_colors else "white" return f"[{color}]{state}[/{color}]" + + +def _format_instance(instance_id: str, instance_name: str | None) -> str: + if not instance_name: + return instance_id + return f"{instance_name} ({instance_id})" diff --git a/tests/cli/test_beta_clusters.py b/tests/cli/test_beta_clusters.py index 892940c8..ead92334 100644 --- a/tests/cli/test_beta_clusters.py +++ b/tests/cli/test_beta_clusters.py @@ -321,6 +321,32 @@ def test_remediations_list_accepts_instance_id(self, respx_mock: MockRouter, cli assert cast(Call, route.calls[0]).request.url.path == "/compute/clusters/c1/instances/i1/remediations" assert result.exit_code == 0 + @pytest.mark.respx(base_url=base_url) + def test_remediations_list_table_uses_instance_name(self, respx_mock: MockRouter, cli_runner: CliRunner) -> None: + payload = _remediation_list_body(_remediation_body(instance_name="gpu-node-a")) + respx_mock.get("/compute/clusters/c1/instances/-/remediations").mock( + return_value=httpx.Response(200, json=payload) + ) + + result = cli_runner.invoke(["beta", "clusters", "remediations", "list", "c1"]) + + assert "gpu-node-a (i1)" in result.output + assert result.exit_code == 0 + + @pytest.mark.respx(base_url=base_url) + def test_remediations_list_table_falls_back_to_instance_id( + self, respx_mock: MockRouter, cli_runner: CliRunner + ) -> None: + payload = _remediation_list_body(_remediation_body()) + respx_mock.get("/compute/clusters/c1/instances/-/remediations").mock( + return_value=httpx.Response(200, json=payload) + ) + + result = cli_runner.invoke(["beta", "clusters", "remediations", "list", "c1"]) + + assert "i1" in result.output + assert result.exit_code == 0 + @pytest.mark.respx(base_url=base_url) def test_remediations_list_accepts_filters(self, respx_mock: MockRouter, cli_runner: CliRunner) -> None: payload = _remediation_list_body(_remediation_body())