From 7143f48e1e583d2d0e403c54b20298fb6e64c08f Mon Sep 17 00:00:00 2001 From: Daniel Sanche Date: Tue, 2 Jun 2026 11:46:41 -0700 Subject: [PATCH 1/7] chore(bigtable): prevent test leaks --- .../system/admin_overlay/test_system_async.py | 81 ++++++++++++++----- .../admin_overlay/test_system_autogen.py | 62 ++++++++++---- .../tests/system/data/__init__.py | 10 +-- 3 files changed, 112 insertions(+), 41 deletions(-) diff --git a/packages/google-cloud-bigtable/tests/system/admin_overlay/test_system_async.py b/packages/google-cloud-bigtable/tests/system/admin_overlay/test_system_async.py index 343ebcabb616..7faec59f50c7 100644 --- a/packages/google-cloud-bigtable/tests/system/admin_overlay/test_system_async.py +++ b/packages/google-cloud-bigtable/tests/system/admin_overlay/test_system_async.py @@ -89,32 +89,32 @@ async def instance_admin_client(admin_overlay_project_id): @CrossSync.convert -@CrossSync.pytest_fixture(scope="session") +@CrossSync.pytest_fixture(scope="function") async def instances_to_delete(instance_admin_client): instances = [] try: yield instances finally: - for instance in instances: + for instance in reversed(instances): try: await instance_admin_client.delete_instance(name=instance.name) - except exceptions.NotFound: + except Exception: pass @CrossSync.convert -@CrossSync.pytest_fixture(scope="session") +@CrossSync.pytest_fixture(scope="function") async def backups_to_delete(table_admin_client): backups = [] try: yield backups finally: - for backup in backups: + for backup in reversed(backups): try: await table_admin_client.delete_backup(name=backup.name) - except exceptions.NotFound: + except Exception: pass @@ -169,14 +169,45 @@ async def create_instance( # add to cleanup list before waiting for result, in case of timeout instance_name = instance_admin_client.instance_path(project_id, instance_id) - instances_to_delete.append(admin_v2.Instance(name=instance_name)) + instance_placeholder = admin_v2.Instance(name=instance_name) + instances_to_delete.append(instance_placeholder) + + try: + instance = await operation.result() + + # replace with full instance object + instances_to_delete[-1] = instance + + # Create a table within the instance + create_table_request = admin_v2.CreateTableRequest( + parent=instance_admin_client.instance_path(project_id, instance_id), + table_id=TEST_TABLE_NAME, + table=admin_v2.Table( + column_families={ + TEST_COLUMMN_FAMILY_NAME: admin_v2.ColumnFamily(), + } + ), + ) - instance = await operation.result() + table = await table_admin_client.create_table(create_table_request) - # replace with full instance object - instances_to_delete[-1] = instance + # Populate with dummy data + await populate_table( + table_admin_client, data_client, instance, table, INITIAL_CELL_VALUE + ) - # Create a table within the instance + return instance, table + except Exception: + # cleanup immediately on failure + try: + await instance_admin_client.delete_instance(name=instance_name) + except Exception: + pass + if instance_placeholder in instances_to_delete: + instances_to_delete.remove(instance_placeholder) + raise + + # Create a table within the instance (emulator case) create_table_request = admin_v2.CreateTableRequest( parent=instance_admin_client.instance_path(project_id, instance_id), table_id=TEST_TABLE_NAME, @@ -260,16 +291,26 @@ async def create_backup( ) # add to cleanup list before waiting for result, in case of timeout - backups_to_delete.append( - admin_v2.Backup(name=f"{cluster_name}/backups/{backup_id}") - ) - - backup = await operation.result() + backup_name = f"{cluster_name}/backups/{backup_id}" + backup_placeholder = admin_v2.Backup(name=backup_name) + backups_to_delete.append(backup_placeholder) - # replace with full backup object - backups_to_delete[-1] = backup - - return backup + try: + backup = await operation.result() + + # replace with full backup object + backups_to_delete[-1] = backup + + return backup + except Exception: + # cleanup immediately on failure + try: + await table_admin_client.delete_backup(name=backup_name) + except Exception: + pass + if backup_placeholder in backups_to_delete: + backups_to_delete.remove(backup_placeholder) + raise @CrossSync.convert diff --git a/packages/google-cloud-bigtable/tests/system/admin_overlay/test_system_autogen.py b/packages/google-cloud-bigtable/tests/system/admin_overlay/test_system_autogen.py index 20c5b2c277eb..5ef070ba7e65 100644 --- a/packages/google-cloud-bigtable/tests/system/admin_overlay/test_system_autogen.py +++ b/packages/google-cloud-bigtable/tests/system/admin_overlay/test_system_autogen.py @@ -20,7 +20,6 @@ from typing import Tuple import pytest -from google.api_core import exceptions from google.api_core import operation as api_core_operation from google.cloud.environment_vars import BIGTABLE_EMULATOR @@ -73,29 +72,29 @@ def instance_admin_client(admin_overlay_project_id): yield client -@pytest.fixture(scope="session") +@pytest.fixture(scope="function") def instances_to_delete(instance_admin_client): instances = [] try: yield instances finally: - for instance in instances: + for instance in reversed(instances): try: instance_admin_client.delete_instance(name=instance.name) - except exceptions.NotFound: + except Exception: pass -@pytest.fixture(scope="session") +@pytest.fixture(scope="function") def backups_to_delete(table_admin_client): backups = [] try: yield backups finally: - for backup in backups: + for backup in reversed(backups): try: table_admin_client.delete_backup(name=backup.name) - except exceptions.NotFound: + except Exception: pass @@ -135,9 +134,31 @@ def create_instance( ) operation = instance_admin_client.create_instance(create_instance_request) instance_name = instance_admin_client.instance_path(project_id, instance_id) - instances_to_delete.append(admin_v2.Instance(name=instance_name)) - instance = operation.result() - instances_to_delete[-1] = instance + instance_placeholder = admin_v2.Instance(name=instance_name) + instances_to_delete.append(instance_placeholder) + try: + instance = operation.result() + instances_to_delete[-1] = instance + create_table_request = admin_v2.CreateTableRequest( + parent=instance_admin_client.instance_path(project_id, instance_id), + table_id=TEST_TABLE_NAME, + table=admin_v2.Table( + column_families={TEST_COLUMMN_FAMILY_NAME: admin_v2.ColumnFamily()} + ), + ) + table = table_admin_client.create_table(create_table_request) + populate_table( + table_admin_client, data_client, instance, table, INITIAL_CELL_VALUE + ) + return (instance, table) + except Exception: + try: + instance_admin_client.delete_instance(name=instance_name) + except Exception: + pass + if instance_placeholder in instances_to_delete: + instances_to_delete.remove(instance_placeholder) + raise create_table_request = admin_v2.CreateTableRequest( parent=instance_admin_client.instance_path(project_id, instance_id), table_id=TEST_TABLE_NAME, @@ -198,12 +219,21 @@ def create_backup( ), ) ) - backups_to_delete.append( - admin_v2.Backup(name=f"{cluster_name}/backups/{backup_id}") - ) - backup = operation.result() - backups_to_delete[-1] = backup - return backup + backup_name = f"{cluster_name}/backups/{backup_id}" + backup_placeholder = admin_v2.Backup(name=backup_name) + backups_to_delete.append(backup_placeholder) + try: + backup = operation.result() + backups_to_delete[-1] = backup + return backup + except Exception: + try: + table_admin_client.delete_backup(name=backup_name) + except Exception: + pass + if backup_placeholder in backups_to_delete: + backups_to_delete.remove(backup_placeholder) + raise def assert_table_cell_value_equal_to( diff --git a/packages/google-cloud-bigtable/tests/system/data/__init__.py b/packages/google-cloud-bigtable/tests/system/data/__init__.py index 939955635979..bfa7b6f73432 100644 --- a/packages/google-cloud-bigtable/tests/system/data/__init__.py +++ b/packages/google-cloud-bigtable/tests/system/data/__init__.py @@ -128,7 +128,7 @@ def instance_id(self, admin_client, project_id, cluster_config): admin_client.instance_admin_client.delete_instance( name=f"projects/{project_id}/instances/{instance_id}" ) - except exceptions.NotFound: + except Exception: pass @pytest.fixture(scope="session") @@ -195,8 +195,8 @@ def table_id( admin_client.table_admin_client.delete_table( name=f"{parent_path}/tables/{init_table_id}" ) - except exceptions.NotFound: - print(f"Table {init_table_id} not found, skipping deletion") + except Exception: + print(f"Failed to delete table {init_table_id}") @pytest.fixture(scope="session") def authorized_view_id( @@ -256,8 +256,8 @@ def authorized_view_id( admin_client.table_admin_client.delete_authorized_view( name=new_path ) - except exceptions.NotFound: - print(f"View {new_view_id} not found, skipping deletion") + except Exception: + print(f"Failed to delete view {new_view_id}") @pytest.fixture(scope="session") def project_id(self, client): From 0025c54fa56e967955431407d0cde860abe0f2d3 Mon Sep 17 00:00:00 2001 From: Daniel Sanche Date: Tue, 2 Jun 2026 12:58:02 -0700 Subject: [PATCH 2/7] removed duplicate code --- .../system/admin_overlay/test_system_async.py | 60 ++++--------------- .../admin_overlay/test_system_autogen.py | 40 ++----------- 2 files changed, 15 insertions(+), 85 deletions(-) diff --git a/packages/google-cloud-bigtable/tests/system/admin_overlay/test_system_async.py b/packages/google-cloud-bigtable/tests/system/admin_overlay/test_system_async.py index 7faec59f50c7..46fcb0e75eb1 100644 --- a/packages/google-cloud-bigtable/tests/system/admin_overlay/test_system_async.py +++ b/packages/google-cloud-bigtable/tests/system/admin_overlay/test_system_async.py @@ -172,42 +172,12 @@ async def create_instance( instance_placeholder = admin_v2.Instance(name=instance_name) instances_to_delete.append(instance_placeholder) - try: - instance = await operation.result() - - # replace with full instance object - instances_to_delete[-1] = instance - - # Create a table within the instance - create_table_request = admin_v2.CreateTableRequest( - parent=instance_admin_client.instance_path(project_id, instance_id), - table_id=TEST_TABLE_NAME, - table=admin_v2.Table( - column_families={ - TEST_COLUMMN_FAMILY_NAME: admin_v2.ColumnFamily(), - } - ), - ) - - table = await table_admin_client.create_table(create_table_request) + instance = await operation.result() - # Populate with dummy data - await populate_table( - table_admin_client, data_client, instance, table, INITIAL_CELL_VALUE - ) - - return instance, table - except Exception: - # cleanup immediately on failure - try: - await instance_admin_client.delete_instance(name=instance_name) - except Exception: - pass - if instance_placeholder in instances_to_delete: - instances_to_delete.remove(instance_placeholder) - raise + # replace with full instance object + instances_to_delete[-1] = instance - # Create a table within the instance (emulator case) + # Create a table within the instance create_table_request = admin_v2.CreateTableRequest( parent=instance_admin_client.instance_path(project_id, instance_id), table_id=TEST_TABLE_NAME, @@ -295,22 +265,12 @@ async def create_backup( backup_placeholder = admin_v2.Backup(name=backup_name) backups_to_delete.append(backup_placeholder) - try: - backup = await operation.result() - - # replace with full backup object - backups_to_delete[-1] = backup - - return backup - except Exception: - # cleanup immediately on failure - try: - await table_admin_client.delete_backup(name=backup_name) - except Exception: - pass - if backup_placeholder in backups_to_delete: - backups_to_delete.remove(backup_placeholder) - raise + backup = await operation.result() + + # replace with full backup object + backups_to_delete[-1] = backup + + return backup @CrossSync.convert diff --git a/packages/google-cloud-bigtable/tests/system/admin_overlay/test_system_autogen.py b/packages/google-cloud-bigtable/tests/system/admin_overlay/test_system_autogen.py index 5ef070ba7e65..ec31aeb2ce34 100644 --- a/packages/google-cloud-bigtable/tests/system/admin_overlay/test_system_autogen.py +++ b/packages/google-cloud-bigtable/tests/system/admin_overlay/test_system_autogen.py @@ -136,29 +136,8 @@ def create_instance( instance_name = instance_admin_client.instance_path(project_id, instance_id) instance_placeholder = admin_v2.Instance(name=instance_name) instances_to_delete.append(instance_placeholder) - try: - instance = operation.result() - instances_to_delete[-1] = instance - create_table_request = admin_v2.CreateTableRequest( - parent=instance_admin_client.instance_path(project_id, instance_id), - table_id=TEST_TABLE_NAME, - table=admin_v2.Table( - column_families={TEST_COLUMMN_FAMILY_NAME: admin_v2.ColumnFamily()} - ), - ) - table = table_admin_client.create_table(create_table_request) - populate_table( - table_admin_client, data_client, instance, table, INITIAL_CELL_VALUE - ) - return (instance, table) - except Exception: - try: - instance_admin_client.delete_instance(name=instance_name) - except Exception: - pass - if instance_placeholder in instances_to_delete: - instances_to_delete.remove(instance_placeholder) - raise + instance = operation.result() + instances_to_delete[-1] = instance create_table_request = admin_v2.CreateTableRequest( parent=instance_admin_client.instance_path(project_id, instance_id), table_id=TEST_TABLE_NAME, @@ -222,18 +201,9 @@ def create_backup( backup_name = f"{cluster_name}/backups/{backup_id}" backup_placeholder = admin_v2.Backup(name=backup_name) backups_to_delete.append(backup_placeholder) - try: - backup = operation.result() - backups_to_delete[-1] = backup - return backup - except Exception: - try: - table_admin_client.delete_backup(name=backup_name) - except Exception: - pass - if backup_placeholder in backups_to_delete: - backups_to_delete.remove(backup_placeholder) - raise + backup = operation.result() + backups_to_delete[-1] = backup + return backup def assert_table_cell_value_equal_to( From 6adb279a833fdc16decb3e8a6cb4e0dfb03cac8a Mon Sep 17 00:00:00 2001 From: Daniel Sanche Date: Tue, 2 Jun 2026 13:14:22 -0700 Subject: [PATCH 3/7] Apply suggestions from code review Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> --- .../tests/system/admin_overlay/test_system_async.py | 8 ++++---- .../system/admin_overlay/test_system_autogen.py | 8 ++++---- .../tests/system/data/__init__.py | 12 ++++++------ 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/packages/google-cloud-bigtable/tests/system/admin_overlay/test_system_async.py b/packages/google-cloud-bigtable/tests/system/admin_overlay/test_system_async.py index 46fcb0e75eb1..b8c68fa8c133 100644 --- a/packages/google-cloud-bigtable/tests/system/admin_overlay/test_system_async.py +++ b/packages/google-cloud-bigtable/tests/system/admin_overlay/test_system_async.py @@ -99,8 +99,8 @@ async def instances_to_delete(instance_admin_client): for instance in reversed(instances): try: await instance_admin_client.delete_instance(name=instance.name) - except Exception: - pass + except Exception as e: + print(f"Failed to delete instance {instance.name}: {e}") @CrossSync.convert @@ -114,8 +114,8 @@ async def backups_to_delete(table_admin_client): for backup in reversed(backups): try: await table_admin_client.delete_backup(name=backup.name) - except Exception: - pass + except Exception as e: + print(f"Failed to delete backup {backup.name}: {e}") @CrossSync.convert diff --git a/packages/google-cloud-bigtable/tests/system/admin_overlay/test_system_autogen.py b/packages/google-cloud-bigtable/tests/system/admin_overlay/test_system_autogen.py index ec31aeb2ce34..01c067aa24b0 100644 --- a/packages/google-cloud-bigtable/tests/system/admin_overlay/test_system_autogen.py +++ b/packages/google-cloud-bigtable/tests/system/admin_overlay/test_system_autogen.py @@ -81,8 +81,8 @@ def instances_to_delete(instance_admin_client): for instance in reversed(instances): try: instance_admin_client.delete_instance(name=instance.name) - except Exception: - pass + except Exception as e: + print(f"Failed to delete instance {instance.name}: {e}") @pytest.fixture(scope="function") @@ -94,8 +94,8 @@ def backups_to_delete(table_admin_client): for backup in reversed(backups): try: table_admin_client.delete_backup(name=backup.name) - except Exception: - pass + except Exception as e: + print(f"Failed to delete backup {backup.name}: {e}") def create_instance( diff --git a/packages/google-cloud-bigtable/tests/system/data/__init__.py b/packages/google-cloud-bigtable/tests/system/data/__init__.py index bfa7b6f73432..fbae6e496132 100644 --- a/packages/google-cloud-bigtable/tests/system/data/__init__.py +++ b/packages/google-cloud-bigtable/tests/system/data/__init__.py @@ -128,8 +128,8 @@ def instance_id(self, admin_client, project_id, cluster_config): admin_client.instance_admin_client.delete_instance( name=f"projects/{project_id}/instances/{instance_id}" ) - except Exception: - pass + except Exception as e: + print(f"Failed to delete instance {instance_id}: {e}") @pytest.fixture(scope="session") def column_split_config(self): @@ -195,8 +195,8 @@ def table_id( admin_client.table_admin_client.delete_table( name=f"{parent_path}/tables/{init_table_id}" ) - except Exception: - print(f"Failed to delete table {init_table_id}") + except Exception as e: + print(f"Failed to delete table {init_table_id}: {e}") @pytest.fixture(scope="session") def authorized_view_id( @@ -256,8 +256,8 @@ def authorized_view_id( admin_client.table_admin_client.delete_authorized_view( name=new_path ) - except Exception: - print(f"Failed to delete view {new_view_id}") + except Exception as e: + print(f"Failed to delete view {new_view_id}: {e}") @pytest.fixture(scope="session") def project_id(self, client): From 7bbb6856b8a495104d667aaf36933c8c67fa1e6d Mon Sep 17 00:00:00 2001 From: Daniel Sanche Date: Tue, 2 Jun 2026 13:14:38 -0700 Subject: [PATCH 4/7] fix lint --- .../tests/system/admin_overlay/test_system_async.py | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/google-cloud-bigtable/tests/system/admin_overlay/test_system_async.py b/packages/google-cloud-bigtable/tests/system/admin_overlay/test_system_async.py index b8c68fa8c133..f7a2270fee44 100644 --- a/packages/google-cloud-bigtable/tests/system/admin_overlay/test_system_async.py +++ b/packages/google-cloud-bigtable/tests/system/admin_overlay/test_system_async.py @@ -17,7 +17,6 @@ from typing import Tuple import pytest -from google.api_core import exceptions from google.cloud.environment_vars import BIGTABLE_EMULATOR from google.cloud import bigtable_admin_v2 as admin_v2 From b59dc08cb1f4c6e3a7f7dce2bcc9601f584631ce Mon Sep 17 00:00:00 2001 From: Daniel Sanche Date: Tue, 2 Jun 2026 13:30:37 -0700 Subject: [PATCH 5/7] added logic to clear out old test data --- .../system/admin_overlay/test_system_async.py | 15 +++++ .../tests/system/data/__init__.py | 8 +++ .../tests/system/utils.py | 58 +++++++++++++++++++ 3 files changed, 81 insertions(+) create mode 100644 packages/google-cloud-bigtable/tests/system/utils.py diff --git a/packages/google-cloud-bigtable/tests/system/admin_overlay/test_system_async.py b/packages/google-cloud-bigtable/tests/system/admin_overlay/test_system_async.py index f7a2270fee44..a9d384d39b7f 100644 --- a/packages/google-cloud-bigtable/tests/system/admin_overlay/test_system_async.py +++ b/packages/google-cloud-bigtable/tests/system/admin_overlay/test_system_async.py @@ -87,6 +87,21 @@ async def instance_admin_client(admin_overlay_project_id): yield client +@CrossSync.convert +@CrossSync.pytest_fixture(scope="session", autouse=True) +async def cleanup_old_instances(admin_overlay_project_id): + """ + Automatically deletes any test instances older than 1 day. + + This fixture runs once per test session and helps prevent resource leakage + by cleaning up instances that failed to be deleted during previous test runs. + """ + from tests.system.utils import clear_stale_instances + from .conftest import INSTANCE_PREFIX + + clear_stale_instances(admin_overlay_project_id, INSTANCE_PREFIX, older_than_days=1) + + @CrossSync.convert @CrossSync.pytest_fixture(scope="function") async def instances_to_delete(instance_admin_client): diff --git a/packages/google-cloud-bigtable/tests/system/data/__init__.py b/packages/google-cloud-bigtable/tests/system/data/__init__.py index fbae6e496132..a40c248df94f 100644 --- a/packages/google-cloud-bigtable/tests/system/data/__init__.py +++ b/packages/google-cloud-bigtable/tests/system/data/__init__.py @@ -34,6 +34,14 @@ class SystemTestRunner: used by standard system tests, and metrics tests """ + @pytest.fixture(scope="session", autouse=True) + def cleanup_old_instances(self, project_id): + """ + Automatically deletes any test instances older than 1 day. + """ + from tests.system.utils import clear_stale_instances + clear_stale_instances(project_id, "python-bigtable-tests", older_than_days=1) + @pytest.fixture(scope="session") def init_table_id(self): """ diff --git a/packages/google-cloud-bigtable/tests/system/utils.py b/packages/google-cloud-bigtable/tests/system/utils.py new file mode 100644 index 000000000000..b9259dbf0f29 --- /dev/null +++ b/packages/google-cloud-bigtable/tests/system/utils.py @@ -0,0 +1,58 @@ +# Copyright 2026 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import os +from datetime import datetime, timedelta, timezone + +from google.api_core.exceptions import NotFound +from google.cloud import bigtable_admin_v2 as admin_v2 + + +def clear_stale_instances(project_id: str, prefix: str, older_than_days: int = 1): + """ + Synchronously deletes any instances in the given project that are older + than older_than_days and whose name or display name matches the given prefix. + """ + client = admin_v2.BigtableInstanceAdminClient( + client_options={"quota_project_id": project_id} + ) + parent = client.common_project_path(project_id) + next_page_token = "" + + while True: + try: + response = client.list_instances( + request={"parent": parent, "page_token": next_page_token} + ) + except Exception: + # Cannot list instances, skip cleanup + break + + for instance in response.instances: + # Check if instance matches the prefix + display_name_matches = instance.display_name.startswith(prefix) + name_matches = instance.name.split("/")[-1].startswith(prefix) + + if display_name_matches or name_matches: + if instance.create_time: + now = datetime.now(timezone.utc) + if now - instance.create_time > timedelta(days=older_than_days): + try: + client.delete_instance(name=instance.name) + except NotFound: + pass + + next_page_token = response.next_page_token + if not next_page_token: + break From ecdaa41f92393d69a9e24b3da6bcb3244aa13c32 Mon Sep 17 00:00:00 2001 From: Daniel Sanche Date: Tue, 2 Jun 2026 13:33:54 -0700 Subject: [PATCH 6/7] fix lint --- .../tests/system/admin_overlay/test_system_async.py | 1 + packages/google-cloud-bigtable/tests/system/data/__init__.py | 1 + packages/google-cloud-bigtable/tests/system/utils.py | 2 +- 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/google-cloud-bigtable/tests/system/admin_overlay/test_system_async.py b/packages/google-cloud-bigtable/tests/system/admin_overlay/test_system_async.py index a9d384d39b7f..bca591d0a11e 100644 --- a/packages/google-cloud-bigtable/tests/system/admin_overlay/test_system_async.py +++ b/packages/google-cloud-bigtable/tests/system/admin_overlay/test_system_async.py @@ -97,6 +97,7 @@ async def cleanup_old_instances(admin_overlay_project_id): by cleaning up instances that failed to be deleted during previous test runs. """ from tests.system.utils import clear_stale_instances + from .conftest import INSTANCE_PREFIX clear_stale_instances(admin_overlay_project_id, INSTANCE_PREFIX, older_than_days=1) diff --git a/packages/google-cloud-bigtable/tests/system/data/__init__.py b/packages/google-cloud-bigtable/tests/system/data/__init__.py index a40c248df94f..2dce4850d547 100644 --- a/packages/google-cloud-bigtable/tests/system/data/__init__.py +++ b/packages/google-cloud-bigtable/tests/system/data/__init__.py @@ -40,6 +40,7 @@ def cleanup_old_instances(self, project_id): Automatically deletes any test instances older than 1 day. """ from tests.system.utils import clear_stale_instances + clear_stale_instances(project_id, "python-bigtable-tests", older_than_days=1) @pytest.fixture(scope="session") diff --git a/packages/google-cloud-bigtable/tests/system/utils.py b/packages/google-cloud-bigtable/tests/system/utils.py index b9259dbf0f29..5ea1fedd1189 100644 --- a/packages/google-cloud-bigtable/tests/system/utils.py +++ b/packages/google-cloud-bigtable/tests/system/utils.py @@ -12,10 +12,10 @@ # See the License for the specific language governing permissions and # limitations under the License. -import os from datetime import datetime, timedelta, timezone from google.api_core.exceptions import NotFound + from google.cloud import bigtable_admin_v2 as admin_v2 From c06fbaffc256d7b317932d0fd0aa6a113c35bf2a Mon Sep 17 00:00:00 2001 From: Daniel Sanche Date: Tue, 2 Jun 2026 13:40:52 -0700 Subject: [PATCH 7/7] regenerated sync code --- .../system/admin_overlay/test_system_autogen.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/packages/google-cloud-bigtable/tests/system/admin_overlay/test_system_autogen.py b/packages/google-cloud-bigtable/tests/system/admin_overlay/test_system_autogen.py index 01c067aa24b0..16628121e687 100644 --- a/packages/google-cloud-bigtable/tests/system/admin_overlay/test_system_autogen.py +++ b/packages/google-cloud-bigtable/tests/system/admin_overlay/test_system_autogen.py @@ -72,6 +72,19 @@ def instance_admin_client(admin_overlay_project_id): yield client +@pytest.fixture(scope="session", autouse=True) +def cleanup_old_instances(admin_overlay_project_id): + """Automatically deletes any test instances older than 1 day. + + This fixture runs once per test session and helps prevent resource leakage + by cleaning up instances that failed to be deleted during previous test runs.""" + from tests.system.utils import clear_stale_instances + + from .conftest import INSTANCE_PREFIX + + clear_stale_instances(admin_overlay_project_id, INSTANCE_PREFIX, older_than_days=1) + + @pytest.fixture(scope="function") def instances_to_delete(instance_admin_client): instances = []