From 52a971ea116f8813a9ffd2fe0f9ccb2441945c74 Mon Sep 17 00:00:00 2001 From: shin0343 Date: Mon, 19 Oct 2026 19:38:17 -0700 Subject: [PATCH] fix: clarify cluster-stack listing behavior hyp list cluster-stack and HpClusterStack.list() return all CloudFormation stacks in the account and Region, but the help text, output header, and documentation described them as HyperPod-only. Clarify the behavior in the CLI help, SDK docstring, output header, README, Read the Docs, and example notebooks. Also fix the invalid `hyp list hyp-cluster` usage example and add regression tests covering mixed HyperPod and unrelated stacks plus help/behavior consistency. Refs #348 --- README.md | 10 +++-- .../cli_cluster_management.md | 8 +++- doc/getting_started/cluster_management.rst | 7 +++- .../cluster_creation_init_experience.ipynb | 8 ++-- .../cluster_creation_sdk_experience.ipynb | 18 ++++---- .../hyperpod/cli/commands/cluster_stack.py | 17 ++++---- .../cluster_management/hp_cluster_stack.py | 5 +++ test/unit_tests/cli/test_cluster_stack.py | 41 ++++++++++++++++++- 8 files changed, 86 insertions(+), 28 deletions(-) diff --git a/README.md b/README.md index 8d596ed7..b3519cab 100644 --- a/README.md +++ b/README.md @@ -181,12 +181,14 @@ hyp create --region **Note**: The region flag is optional. If not provided, the command will use the default region from your AWS credentials configuration. -#### List Cluster Stacks +#### List CloudFormation Stacks ```bash hyp list cluster-stack ``` +**Note**: This command lists all CloudFormation stacks in the current AWS account and Region. It does not currently filter the results to HyperPod-created stacks. + | Option | Type | Description | |--------|------|-------------| | `--region ` | Optional | The AWS region to list stacks from. | @@ -1002,14 +1004,16 @@ cluster_stack = HpClusterStack( response = cluster_stack.create(region="us-east-2") ``` -#### Listing Cluster Stacks +#### Listing CloudFormation Stacks ```python -# List all cluster stacks +# List all CloudFormation stacks in the Region stacks = HpClusterStack.list(region="us-east-2") print(f"Found {len(stacks['StackSummaries'])} stacks") ``` +**Note**: `HpClusterStack.list()` returns all CloudFormation stacks in the current account and Region; it does not filter to HyperPod cluster stacks. + #### Describing a Cluster Stack ```python diff --git a/doc/cli/cluster_management/cli_cluster_management.md b/doc/cli/cluster_management/cli_cluster_management.md index 59c27d37..5f9ae90a 100644 --- a/doc/cli/cluster_management/cli_cluster_management.md +++ b/doc/cli/cluster_management/cli_cluster_management.md @@ -194,7 +194,11 @@ hyp update cluster [OPTIONS] ## hyp list cluster-stack -List all HyperPod cluster stacks (CloudFormation stacks). +List all CloudFormation stacks in the current AWS account and Region. + +```{note} +This command does not currently filter the results to HyperPod-created stacks. +``` #### Syntax @@ -426,7 +430,7 @@ hyp update cluster \ ### List and Describe ```bash -# List all cluster stacks +# List all CloudFormation stacks in the Region hyp list cluster-stack --region us-west-2 # Describe specific cluster stack diff --git a/doc/getting_started/cluster_management.rst b/doc/getting_started/cluster_management.rst index 23377d7f..85ef481d 100644 --- a/doc/getting_started/cluster_management.rst +++ b/doc/getting_started/cluster_management.rst @@ -127,7 +127,12 @@ Check the status of your cluster: **Region-Specific Stack Names**: Cluster stack names are unique within each AWS region. When describing a stack, ensure you specify the correct region where the stack was created, or the command will fail to find the stack. -List all clusters: +List CloudFormation stacks: + +.. note:: + ``hyp list cluster-stack`` lists all CloudFormation stacks in the current + account and Region. It does not currently filter the results to HyperPod + cluster stacks. .. tab-set:: diff --git a/examples/cluster_management/cluster_creation_init_experience.ipynb b/examples/cluster_management/cluster_creation_init_experience.ipynb index db01dcc6..52a15b1b 100644 --- a/examples/cluster_management/cluster_creation_init_experience.ipynb +++ b/examples/cluster_management/cluster_creation_init_experience.ipynb @@ -231,12 +231,12 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## Step 8: List All Cluster Stacks\n", + "## Step 8: List CloudFormation Stacks\n", "\n", - "The `hyp list cluster-stack` command shows all HyperPod cluster stacks in your account. This is useful for managing multiple clusters and getting an overview of your infrastructure.\n", + "The `hyp list cluster-stack` command lists all CloudFormation stacks in your account and Region. This is useful for getting an overview of your infrastructure; it does not currently filter the results to HyperPod cluster stacks.\n", "\n", "**Displays:**\n", - "- All cluster stacks in the current region\n", + "- All CloudFormation stacks in the current Region\n", "- Stack names and creation timestamps\n", "- Current status of each stack\n", "- Resource counts and types\n" @@ -246,7 +246,7 @@ "cell_type": "code", "metadata": {}, "source": [ - "# List all cluster stacks in your account\n", + "# List all CloudFormation stacks in your account and Region\n", "!hyp list cluster-stack" ], "outputs": [], diff --git a/examples/cluster_management/cluster_creation_sdk_experience.ipynb b/examples/cluster_management/cluster_creation_sdk_experience.ipynb index b21718fa..35847891 100644 --- a/examples/cluster_management/cluster_creation_sdk_experience.ipynb +++ b/examples/cluster_management/cluster_creation_sdk_experience.ipynb @@ -355,12 +355,12 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## Step 6: List All Cluster Stacks\n", + "## Step 6: List CloudFormation Stacks\n", "\n", - "List all HyperPod cluster stacks in your account using SDK methods. This is equivalent to `hyp list cluster-stack` in the CLI.\n", + "List CloudFormation stacks in your account using SDK methods. This is equivalent to `hyp list cluster-stack` in the CLI. The SDK returns all stacks in the Region; the name-based filter below is only an illustration and is not a reliable way to identify HyperPod stacks.\n", "\n", "**Displays:**\n", - "- All cluster stacks in the current region\n", + "- All CloudFormation stacks in the current Region\n", "- Stack names and creation timestamps\n", "- Current status of each stack\n", "- Resource counts and types" @@ -372,11 +372,11 @@ "metadata": {}, "outputs": [], "source": [ - "# List all cluster stacks (equivalent to hyp list cluster-stack)\n", + "# List all CloudFormation stacks (equivalent to hyp list cluster-stack)\n", "def list_cluster_stacks(region=\"us-east-2\"):\n", - " \"\"\"List all cluster stacks in the account\"\"\"\n", + " \"\"\"List CloudFormation stacks in the account and Region\"\"\"\n", " try:\n", - " print(f\"Listing cluster stacks in region: {region}\")\n", + " print(f\"Listing CloudFormation stacks in region: {region}\")\n", " \n", " # Get list of stacks\n", " response = HpClusterStack.list(region=region)\n", @@ -396,7 +396,7 @@ " created = str(stack.get('CreationTime', 'N/A'))[:19]\n", " print(f\"{name:<40} {status:<25} {created:<20}\")\n", " else:\n", - " print(\"No cluster stacks found.\")\n", + " print(\"No CloudFormation stacks found.\")\n", " \n", " return response\n", " \n", @@ -404,10 +404,10 @@ " print(f\"Error listing stacks: {str(e)}\")\n", " return None\n", "\n", - "# List all cluster stacks\n", + "# List all CloudFormation stacks\n", "list_response = list_cluster_stacks()\n", "\n", - "# Filter for HyperPod-related stacks\n", + "# Optional name-based heuristic; not a reliable HyperPod identifier\n", "if list_response and 'StackSummaries' in list_response:\n", " hyperpod_stacks = [\n", " stack for stack in list_response['StackSummaries']\n", diff --git a/src/sagemaker/hyperpod/cli/commands/cluster_stack.py b/src/sagemaker/hyperpod/cli/commands/cluster_stack.py index f53d544e..62025038 100644 --- a/src/sagemaker/hyperpod/cli/commands/cluster_stack.py +++ b/src/sagemaker/hyperpod/cli/commands/cluster_stack.py @@ -210,21 +210,22 @@ def describe_cluster_stack(stack_name: str, debug: bool, region: str) -> None: help="Filter by stack status. Format: \"['CREATE_COMPLETE', 'UPDATE_COMPLETE']\"") @_hyperpod_telemetry_emitter(Feature.HYPERPOD_CLI, "list_cluster_stack_cli") def list_cluster_stacks(region, debug, status): - """List all HyperPod cluster stacks. + """List CloudFormation stacks in the current AWS account and Region. - Displays a summary of all CloudFormation stacks related to HyperPod clusters - in the specified region or default region. + Returns all CloudFormation stacks in the current account and Region. This + command does not currently filter results to stacks created for HyperPod + clusters. Use --status to filter by stack status. .. dropdown:: Usage Examples :open: .. code-block:: bash - # List all cluster stacks - hyp list hyp-cluster + # List CloudFormation stacks + hyp list cluster-stack - # List stacks in specific region - hyp list hyp-cluster --region us-east-1 + # List stacks in a specific region + hyp list cluster-stack --region us-east-1 """ logger = setup_logging(logging.getLogger(__name__), debug) @@ -242,7 +243,7 @@ def list_cluster_stacks(region, debug, status): logger.debug(f"Listing stacks in region: {region or 'default'}") - click.echo(f"📋 HyperPod Cluster Stacks ({len(stack_summaries)} found)") + click.echo(f"📋 CloudFormation Stacks ({len(stack_summaries)} found)") if stack_summaries: for i, stack in enumerate(stack_summaries, 1): diff --git a/src/sagemaker/hyperpod/cluster_management/hp_cluster_stack.py b/src/sagemaker/hyperpod/cluster_management/hp_cluster_stack.py index db4ac36f..f241a9fd 100644 --- a/src/sagemaker/hyperpod/cluster_management/hp_cluster_stack.py +++ b/src/sagemaker/hyperpod/cluster_management/hp_cluster_stack.py @@ -355,6 +355,11 @@ def list(region: Optional[str] = None, stack_status_filter: Optional[List[str]] .. note:: Stack listings are region-specific. If no region is provided, uses the default region from your AWS configuration. + .. note:: + This method does not filter results to HyperPod-related stacks. The + CloudFormation ListStacks API does not return stack tags, so callers + that need a specific subset must make additional API calls. + **Parameters:** .. list-table:: diff --git a/test/unit_tests/cli/test_cluster_stack.py b/test/unit_tests/cli/test_cluster_stack.py index 48fa3c72..21df39cd 100644 --- a/test/unit_tests/cli/test_cluster_stack.py +++ b/test/unit_tests/cli/test_cluster_stack.py @@ -86,11 +86,50 @@ def test_list_cluster_stacks_success(self, mock_setup_logging, mock_hp_cluster_l # Assert assert result.exit_code == 0 - assert 'HyperPod Cluster Stacks (1 found)' in result.output + assert 'CloudFormation Stacks (1 found)' in result.output assert 'test-stack' in result.output assert 'CREATE_COMPLETE' in result.output mock_hp_cluster_list.assert_called_once_with(region=None, stack_status_filter=None) + @patch('sagemaker.hyperpod.cli.commands.cluster_stack.HpClusterStack.list') + @patch('sagemaker.hyperpod.cli.commands.cluster_stack.setup_logging') + def test_list_cluster_stacks_includes_unrelated_cloudformation_stacks(self, mock_setup_logging, mock_hp_cluster_list): + """The command returns all CloudFormation stacks, not only HyperPod stacks.""" + mock_setup_logging.return_value = Mock() + mock_hp_cluster_list.return_value = { + 'StackSummaries': [ + { + 'StackName': 'hyperpod-cluster-stack', + 'StackStatus': 'CREATE_COMPLETE', + 'CreationTime': datetime(2024, 1, 1, 12, 0, 0), + }, + { + 'StackName': 'unrelated-application-stack', + 'StackStatus': 'UPDATE_COMPLETE', + 'CreationTime': datetime(2024, 1, 2, 12, 0, 0), + }, + ] + } + + result = CliRunner().invoke(list_cluster_stacks, []) + + assert result.exit_code == 0 + assert 'CloudFormation Stacks (2 found)' in result.output + assert 'HyperPod Cluster Stacks' not in result.output + assert 'hyperpod-cluster-stack' in result.output + assert 'unrelated-application-stack' in result.output + + def test_list_cluster_stacks_help_describes_all_stacks(self): + """The help output does not claim that results are limited to HyperPod stacks.""" + result = CliRunner().invoke(list_cluster_stacks, ['--help']) + + assert result.exit_code == 0 + help_text = ' '.join(result.output.split()) + assert 'List CloudFormation stacks in the current AWS account and Region.' in help_text + assert 'Returns all CloudFormation stacks in the current account and Region.' in help_text + assert 'does not currently filter results to stacks created for HyperPod clusters.' in help_text + assert 'HyperPod Cluster Stacks' not in help_text + @patch('sagemaker.hyperpod.cli.commands.cluster_stack.HpClusterStack.list') @patch('sagemaker.hyperpod.cli.commands.cluster_stack.setup_logging') def test_list_cluster_stacks_with_region(self, mock_setup_logging, mock_hp_cluster_list):