diff --git a/.test-infra/tools/stale_cleaner.py b/.test-infra/tools/stale_cleaner.py index 3c8b21c806d8..6557949a0545 100644 --- a/.test-infra/tools/stale_cleaner.py +++ b/.test-infra/tools/stale_cleaner.py @@ -350,8 +350,10 @@ def _delete_resource(self, resource_name: str) -> None: self.client = pubsub_v1.SubscriberClient() print(f"{self.clock()} - Deleting PubSub subscription {resource_name}") with self.client: - subscription_path = self.client.subscription_path(self.project_id, resource_name) - self.client.delete_subscription(request={"subscription": subscription_path}) + # resource_name from list_subscriptions() is already a fully-qualified + # path (e.g. "projects//subscriptions/"). Passing it + # to subscription_path() would double-prefix and cause a 400 error. + self.client.delete_subscription(request={"subscription": resource_name}) def clean_pubsub_topics(): """ Clean up stale PubSub topics in the specified GCP project. @@ -439,4 +441,4 @@ def clean_pubsub_subscriptions(): clean_pubsub_topics() # Clean up stale PubSub subscriptions - clean_pubsub_subscriptions() + clean_pubsub_subscriptions() \ No newline at end of file diff --git a/.test-infra/tools/test_stale_cleaner.py b/.test-infra/tools/test_stale_cleaner.py index 08cdab39b85f..8a43d8705823 100644 --- a/.test-infra/tools/test_stale_cleaner.py +++ b/.test-infra/tools/test_stale_cleaner.py @@ -484,17 +484,22 @@ def test_active_resources_detached_subscriptions(self): self.assertEqual(len(active), 1) def test_delete_resource(self): - """Test _delete_resource method.""" - sub_name = "test-sub-to-delete" - subscription_path = f"projects/{self.project_id}/subscriptions/{sub_name}" - self.mock_subscriber_client.subscription_path.return_value = subscription_path + """Test _delete_resource method. + + list_subscriptions() returns subscription names as fully-qualified + paths (e.g. "projects//subscriptions/"), so + _delete_resource must pass them directly to delete_subscription + without re-prefixing via subscription_path(). Double-prefixing + causes InvalidArgument 400 and is the cause of issue #39772. + """ + sub_path = f"projects/{self.project_id}/subscriptions/test-sub-to-delete" with SilencePrint(): - self.cleaner._delete_resource(sub_name) + self.cleaner._delete_resource(sub_path) - self.mock_subscriber_client.subscription_path.assert_called_once_with(self.project_id, sub_name) + self.mock_subscriber_client.subscription_path.assert_not_called() self.mock_subscriber_client.delete_subscription.assert_called_once_with( - request={'subscription': subscription_path} + request={'subscription': sub_path} )