From 9b02da025efa9a1627dcbd34d9fe3af287b7a135 Mon Sep 17 00:00:00 2001 From: Sneha Sahu Date: Mon, 2 Nov 2026 09:58:04 +0000 Subject: [PATCH 1/3] feat(storagecontrol): add Rapid Cache samples Adds samples for the Storage Control Rapid Cache CRUD APIs: storage_control_create_rapid_cache, storage_control_get_rapid_cache, storage_control_list_rapid_caches, storage_control_update_rapid_cache, storage_control_disable_rapid_cache. Bumps google-cloud-storage-control to 1.17.0. Refs b/482946642 [Generated-by: AI] --- storagecontrol/conftest.py | 40 ++++++++++++ storagecontrol/create_rapid_cache.py | 56 +++++++++++++++++ storagecontrol/disable_rapid_cache.py | 52 ++++++++++++++++ storagecontrol/get_rapid_cache.py | 47 ++++++++++++++ storagecontrol/list_rapid_caches.py | 45 ++++++++++++++ storagecontrol/rapid_cache_test.py | 89 +++++++++++++++++++++++++++ storagecontrol/requirements.txt | 2 +- storagecontrol/update_rapid_cache.py | 69 +++++++++++++++++++++ 8 files changed, 399 insertions(+), 1 deletion(-) create mode 100644 storagecontrol/create_rapid_cache.py create mode 100644 storagecontrol/disable_rapid_cache.py create mode 100644 storagecontrol/get_rapid_cache.py create mode 100644 storagecontrol/list_rapid_caches.py create mode 100644 storagecontrol/rapid_cache_test.py create mode 100644 storagecontrol/update_rapid_cache.py diff --git a/storagecontrol/conftest.py b/storagecontrol/conftest.py index d5958a74a22..a661faadc70 100644 --- a/storagecontrol/conftest.py +++ b/storagecontrol/conftest.py @@ -12,9 +12,12 @@ # See the License for the specific language governing permissions and # limitations under the License. +import logging import os import uuid +from google.api_core import exceptions +from google.api_core import retry from google.cloud import storage import pytest @@ -66,6 +69,43 @@ def hns_enabled_bucket(project_id: str, bucket_name: str) -> storage.Bucket: bucket.delete(force=True) +@pytest.fixture(scope="module") +def rapid_cache_zone() -> str: + yield os.environ.get("RAPID_CACHE_ZONE", "us-central1-a") + + +@pytest.fixture(scope="function") +def regional_hns_enabled_bucket( + project_id: str, bucket_name: str, rapid_cache_zone: str +) -> storage.Bucket: + """ + Yields and auto-cleans up a regional HNS enabled bucket located in the + region of the Rapid Cache zone. + """ + + storage_client = storage.Client(project=project_id) + bucket = storage_client.bucket(bucket_name) + bucket.iam_configuration.uniform_bucket_level_access_enabled = True + bucket.hierarchical_namespace_enabled = True + region = rapid_cache_zone.rsplit("-", 1)[0] + bucket = storage_client.create_bucket(bucket, location=region) + + yield bucket + + # A bucket cannot be deleted until its disabled Rapid Cache has been fully + # removed, which can take more than 30 minutes. Cleanup is best-effort: a + # short retry, after which the bucket is left behind for later cleanup. + try: + retry.Retry( + predicate=retry.if_exception_type(exceptions.Conflict), + initial=10.0, + maximum=30.0, + timeout=120.0, + )(bucket.delete)(force=True) + except exceptions.GoogleAPIError as e: + logging.warning("Could not delete bucket %s: %s", bucket.name, e) + + @pytest.fixture(scope="function") def ubla_enabled_bucket(project_id: str, bucket_name: str) -> storage.Bucket: """ diff --git a/storagecontrol/create_rapid_cache.py b/storagecontrol/create_rapid_cache.py new file mode 100644 index 00000000000..2663f55be6f --- /dev/null +++ b/storagecontrol/create_rapid_cache.py @@ -0,0 +1,56 @@ +# 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 +# +# https://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 sys + +# [START storage_control_create_rapid_cache] +from google.cloud import storage_control_v2 + + +def create_rapid_cache(bucket_name: str, zone: str) -> None: + # The ID of your GCS bucket + # bucket_name = "your-unique-bucket-name" + + # The zone in which to create the Rapid Cache. The zone must be in the + # same region as the bucket. + # zone = "us-central1-a" + + storage_control_client = storage_control_v2.StorageControlClient() + # The storage bucket path uses the global access pattern, in which the "_" + # denotes this bucket exists in the global namespace. + project_path = storage_control_client.common_project_path("_") + bucket_path = f"{project_path}/buckets/{bucket_name}" + + # Rapid Cache Ultra requires a bucket with hierarchical namespace enabled. + rapid_cache = storage_control_v2.RapidCache( + zone=zone, + cache_type="rapid-cache-ultra", + ) + request = storage_control_v2.CreateRapidCacheRequest( + parent=bucket_path, + rapid_cache=rapid_cache, + ) + + # Creating a Rapid Cache is a long-running operation. + operation = storage_control_client.create_rapid_cache(request=request) + response = operation.result() + + print(f"Created Rapid Cache: {response.name}") + + +# [END storage_control_create_rapid_cache] + + +if __name__ == "__main__": + create_rapid_cache(bucket_name=sys.argv[1], zone=sys.argv[2]) diff --git a/storagecontrol/disable_rapid_cache.py b/storagecontrol/disable_rapid_cache.py new file mode 100644 index 00000000000..d705e06d5b4 --- /dev/null +++ b/storagecontrol/disable_rapid_cache.py @@ -0,0 +1,52 @@ +# 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 +# +# https://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 sys + +# [START storage_control_disable_rapid_cache] +from google.cloud import storage_control_v2 + + +def disable_rapid_cache(bucket_name: str, cache_id: str) -> None: + # The ID of your GCS bucket + # bucket_name = "your-unique-bucket-name" + + # The ID of the Rapid Cache + # cache_id = "your-rapid-cache-id" + + storage_control_client = storage_control_v2.StorageControlClient() + # The storage bucket path uses the global access pattern, in which the "_" + # denotes this bucket exists in the global namespace. + rapid_cache_path = storage_control_client.rapid_cache_path( + project="_", bucket=bucket_name, rapid_cache=cache_id + ) + + request = storage_control_v2.DisableRapidCacheRequest(name=rapid_cache_path) + + # Disabling a Rapid Cache starts a long-running operation. The cache moves + # to the "disabled" state within seconds, but the operation only completes + # once the cache has been fully removed, which can take more than 30 + # minutes. This sample does not wait for the operation to complete; use + # get_rapid_cache to check the cache state. + operation = storage_control_client.disable_rapid_cache(request=request) + + print(f"Disabling Rapid Cache: {rapid_cache_path}") + print(f"Operation: {operation.operation.name}") + + +# [END storage_control_disable_rapid_cache] + + +if __name__ == "__main__": + disable_rapid_cache(bucket_name=sys.argv[1], cache_id=sys.argv[2]) diff --git a/storagecontrol/get_rapid_cache.py b/storagecontrol/get_rapid_cache.py new file mode 100644 index 00000000000..42bb159ef5a --- /dev/null +++ b/storagecontrol/get_rapid_cache.py @@ -0,0 +1,47 @@ +# 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 +# +# https://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 sys + +# [START storage_control_get_rapid_cache] +from google.cloud import storage_control_v2 + + +def get_rapid_cache(bucket_name: str, cache_id: str) -> None: + # The ID of your GCS bucket + # bucket_name = "your-unique-bucket-name" + + # The ID of the Rapid Cache + # cache_id = "your-rapid-cache-id" + + storage_control_client = storage_control_v2.StorageControlClient() + # The storage bucket path uses the global access pattern, in which the "_" + # denotes this bucket exists in the global namespace. + rapid_cache_path = storage_control_client.rapid_cache_path( + project="_", bucket=bucket_name, rapid_cache=cache_id + ) + + request = storage_control_v2.GetRapidCacheRequest(name=rapid_cache_path) + response = storage_control_client.get_rapid_cache(request=request) + + print(f"Got Rapid Cache: {response.name}") + print(f"Zone: {response.zone}") + print(f"State: {response.state}") + + +# [END storage_control_get_rapid_cache] + + +if __name__ == "__main__": + get_rapid_cache(bucket_name=sys.argv[1], cache_id=sys.argv[2]) diff --git a/storagecontrol/list_rapid_caches.py b/storagecontrol/list_rapid_caches.py new file mode 100644 index 00000000000..4ed9eb9a150 --- /dev/null +++ b/storagecontrol/list_rapid_caches.py @@ -0,0 +1,45 @@ +# 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 +# +# https://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 sys + +# [START storage_control_list_rapid_caches] +from google.cloud import storage_control_v2 + + +def list_rapid_caches(bucket_name: str) -> None: + # The ID of your GCS bucket + # bucket_name = "your-unique-bucket-name" + + storage_control_client = storage_control_v2.StorageControlClient() + # The storage bucket path uses the global access pattern, in which the "_" + # denotes this bucket exists in the global namespace. + project_path = storage_control_client.common_project_path("_") + bucket_path = f"{project_path}/buckets/{bucket_name}" + + request = storage_control_v2.ListRapidCachesRequest(parent=bucket_path) + + # The returned pager handles pagination automatically. + page_result = storage_control_client.list_rapid_caches(request=request) + for rapid_cache in page_result: + print(rapid_cache.name) + + print(f"Listed Rapid Caches in bucket {bucket_name}") + + +# [END storage_control_list_rapid_caches] + + +if __name__ == "__main__": + list_rapid_caches(bucket_name=sys.argv[1]) diff --git a/storagecontrol/rapid_cache_test.py b/storagecontrol/rapid_cache_test.py new file mode 100644 index 00000000000..2ba3e5698d9 --- /dev/null +++ b/storagecontrol/rapid_cache_test.py @@ -0,0 +1,89 @@ +# 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 +# +# https://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 time + +from google.cloud import storage +from google.cloud import storage_control_v2 +import pytest + +import create_rapid_cache +import disable_rapid_cache +import get_rapid_cache +import list_rapid_caches +import update_rapid_cache + +# Poll for up to 2 minutes for the cache to reach the "disabled" state. +_DISABLE_POLL_ATTEMPTS = 12 +_DISABLE_POLL_INTERVAL_SECONDS = 10 + + +def test_rapid_cache_create_get_list_update_disable( + capsys: pytest.CaptureFixture[str], + regional_hns_enabled_bucket: storage.Bucket, + rapid_cache_zone: str, +) -> None: + bucket_name = regional_hns_enabled_bucket.name + + # Test create Rapid Cache + create_rapid_cache.create_rapid_cache( + bucket_name=bucket_name, zone=rapid_cache_zone + ) + out, _ = capsys.readouterr() + prefix = "Created Rapid Cache: " + assert prefix in out + cache_name = out.split(prefix, 1)[1].split()[0] + cache_id = storage_control_v2.StorageControlClient.parse_rapid_cache_path( + cache_name + )["rapid_cache"] + assert cache_id + + try: + # Test get Rapid Cache + get_rapid_cache.get_rapid_cache(bucket_name=bucket_name, cache_id=cache_id) + out, _ = capsys.readouterr() + assert cache_name in out + assert f"Zone: {rapid_cache_zone}" in out + + # Test list Rapid Caches + list_rapid_caches.list_rapid_caches(bucket_name=bucket_name) + out, _ = capsys.readouterr() + assert cache_name in out + + # Test update Rapid Cache + update_rapid_cache.update_rapid_cache( + bucket_name=bucket_name, cache_id=cache_id, ttl_seconds=172800 + ) + out, _ = capsys.readouterr() + assert f"Updated Rapid Cache: {cache_name}" in out + assert "TTL: 172800 seconds" in out + finally: + # Disable the Rapid Cache. This also cleans up the cache so that the + # bucket can be deleted once the cache has been fully removed. + disable_rapid_cache.disable_rapid_cache( + bucket_name=bucket_name, cache_id=cache_id + ) + disable_out, _ = capsys.readouterr() + + # Test disable Rapid Cache + assert f"Disabling Rapid Cache: {cache_name}" in disable_out + + # The cache moves to the "disabled" state shortly after the request. + for _ in range(_DISABLE_POLL_ATTEMPTS): + get_rapid_cache.get_rapid_cache(bucket_name=bucket_name, cache_id=cache_id) + out, _ = capsys.readouterr() + if "State: disabled" in out: + break + time.sleep(_DISABLE_POLL_INTERVAL_SECONDS) + assert "State: disabled" in out diff --git a/storagecontrol/requirements.txt b/storagecontrol/requirements.txt index e7b93b6c245..e2bea4c66d0 100644 --- a/storagecontrol/requirements.txt +++ b/storagecontrol/requirements.txt @@ -1 +1 @@ -google-cloud-storage-control==1.11.0 \ No newline at end of file +google-cloud-storage-control==1.17.0 \ No newline at end of file diff --git a/storagecontrol/update_rapid_cache.py b/storagecontrol/update_rapid_cache.py new file mode 100644 index 00000000000..ac0061f792b --- /dev/null +++ b/storagecontrol/update_rapid_cache.py @@ -0,0 +1,69 @@ +# 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 +# +# https://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 sys + +# [START storage_control_update_rapid_cache] +from google.cloud import storage_control_v2 +from google.protobuf import duration_pb2 +from google.protobuf import field_mask_pb2 + + +def update_rapid_cache(bucket_name: str, cache_id: str, ttl_seconds: int) -> None: + # The ID of your GCS bucket + # bucket_name = "your-unique-bucket-name" + + # The ID of the Rapid Cache + # cache_id = "your-rapid-cache-id" + + # The new time to live (TTL) for cache entries, in seconds. The TTL must + # be between 1 hour and 7 days. + # ttl_seconds = 172800 # 2 days + + storage_control_client = storage_control_v2.StorageControlClient() + # The storage bucket path uses the global access pattern, in which the "_" + # denotes this bucket exists in the global namespace. + rapid_cache_path = storage_control_client.rapid_cache_path( + project="_", bucket=bucket_name, rapid_cache=cache_id + ) + + rapid_cache = storage_control_v2.RapidCache( + name=rapid_cache_path, + ttl=duration_pb2.Duration(seconds=ttl_seconds), + ) + # Only the fields listed in the update mask are changed. + update_mask = field_mask_pb2.FieldMask(paths=["ttl"]) + request = storage_control_v2.UpdateRapidCacheRequest( + rapid_cache=rapid_cache, + update_mask=update_mask, + ) + + # Updating a Rapid Cache is a long-running operation. + operation = storage_control_client.update_rapid_cache(request=request) + response = operation.result() + + print(f"Updated Rapid Cache: {response.name}") + # The client library returns the TTL as a datetime.timedelta. + print(f"TTL: {response.ttl.total_seconds():.0f} seconds") + + +# [END storage_control_update_rapid_cache] + + +if __name__ == "__main__": + update_rapid_cache( + bucket_name=sys.argv[1], + cache_id=sys.argv[2], + ttl_seconds=int(sys.argv[3]), + ) From 9716a32681d00f3ba31ffed1da355bf23378b0c7 Mon Sep 17 00:00:00 2001 From: Sneha Sahu Date: Mon, 2 Nov 2026 18:23:03 +0000 Subject: [PATCH 2/3] docs(storage): align rapid cache samples with reference integration test Set the explicit cache name (ID = zone), cache_type rapid-cache-ultra and ttl 24h on create; wait for the disable LRO; tests assert running state, cache type, list membership and the updated ttl, and bound the disable wait. Aligns with googleapis/google-cloud-java#14529. b/482946642 [Generated-by: AI] --- storagecontrol/conftest.py | 6 +- storagecontrol/create_rapid_cache.py | 10 +++ storagecontrol/disable_rapid_cache.py | 24 ++++--- storagecontrol/get_rapid_cache.py | 5 +- storagecontrol/rapid_cache_test.py | 91 ++++++++++++++++----------- storagecontrol/update_rapid_cache.py | 4 +- 6 files changed, 90 insertions(+), 50 deletions(-) diff --git a/storagecontrol/conftest.py b/storagecontrol/conftest.py index a661faadc70..a43b0d5b980 100644 --- a/storagecontrol/conftest.py +++ b/storagecontrol/conftest.py @@ -14,6 +14,7 @@ import logging import os +import time import uuid from google.api_core import exceptions @@ -89,11 +90,14 @@ def regional_hns_enabled_bucket( bucket.hierarchical_namespace_enabled = True region = rapid_cache_zone.rsplit("-", 1)[0] bucket = storage_client.create_bucket(bucket, location=region) + # Wait for the new bucket's metadata to propagate before creating a Rapid + # Cache on it. + time.sleep(20) yield bucket # A bucket cannot be deleted until its disabled Rapid Cache has been fully - # removed, which can take more than 30 minutes. Cleanup is best-effort: a + # removed, which can take more than an hour. Cleanup is best-effort: a # short retry, after which the bucket is left behind for later cleanup. try: retry.Retry( diff --git a/storagecontrol/create_rapid_cache.py b/storagecontrol/create_rapid_cache.py index 2663f55be6f..81d80601b39 100644 --- a/storagecontrol/create_rapid_cache.py +++ b/storagecontrol/create_rapid_cache.py @@ -16,6 +16,7 @@ # [START storage_control_create_rapid_cache] from google.cloud import storage_control_v2 +from google.protobuf import duration_pb2 def create_rapid_cache(bucket_name: str, zone: str) -> None: @@ -31,11 +32,18 @@ def create_rapid_cache(bucket_name: str, zone: str) -> None: # denotes this bucket exists in the global namespace. project_path = storage_control_client.common_project_path("_") bucket_path = f"{project_path}/buckets/{bucket_name}" + # The Rapid Cache ID must be the same as the zone. + rapid_cache_path = storage_control_client.rapid_cache_path( + project="_", bucket=bucket_name, rapid_cache=zone + ) # Rapid Cache Ultra requires a bucket with hierarchical namespace enabled. rapid_cache = storage_control_v2.RapidCache( + name=rapid_cache_path, zone=zone, cache_type="rapid-cache-ultra", + # The time to live (TTL) for cache entries: 86400 seconds (24 hours). + ttl=duration_pb2.Duration(seconds=86400), ) request = storage_control_v2.CreateRapidCacheRequest( parent=bucket_path, @@ -47,6 +55,8 @@ def create_rapid_cache(bucket_name: str, zone: str) -> None: response = operation.result() print(f"Created Rapid Cache: {response.name}") + print(f"State: {response.state}") + print(f"Cache type: {response.cache_type}") # [END storage_control_create_rapid_cache] diff --git a/storagecontrol/disable_rapid_cache.py b/storagecontrol/disable_rapid_cache.py index d705e06d5b4..f5b89d10f0e 100644 --- a/storagecontrol/disable_rapid_cache.py +++ b/storagecontrol/disable_rapid_cache.py @@ -18,12 +18,18 @@ from google.cloud import storage_control_v2 -def disable_rapid_cache(bucket_name: str, cache_id: str) -> None: +def disable_rapid_cache( + bucket_name: str, cache_id: str, timeout: float | None = None +) -> None: # The ID of your GCS bucket # bucket_name = "your-unique-bucket-name" - # The ID of the Rapid Cache - # cache_id = "your-rapid-cache-id" + # The ID of the Rapid Cache, which is the same as its zone + # cache_id = "us-central1-a" + + # (Optional) The maximum time to wait for the operation, in seconds. + # None waits until the operation completes. + # timeout = None storage_control_client = storage_control_v2.StorageControlClient() # The storage bucket path uses the global access pattern, in which the "_" @@ -34,15 +40,13 @@ def disable_rapid_cache(bucket_name: str, cache_id: str) -> None: request = storage_control_v2.DisableRapidCacheRequest(name=rapid_cache_path) - # Disabling a Rapid Cache starts a long-running operation. The cache moves - # to the "disabled" state within seconds, but the operation only completes - # once the cache has been fully removed, which can take more than 30 - # minutes. This sample does not wait for the operation to complete; use - # get_rapid_cache to check the cache state. + # Disabling a Rapid Cache is a long-running operation. This sample waits + # for the operation to complete. operation = storage_control_client.disable_rapid_cache(request=request) + response = operation.result(timeout=timeout) - print(f"Disabling Rapid Cache: {rapid_cache_path}") - print(f"Operation: {operation.operation.name}") + print(f"Disabled Rapid Cache: {response.name}") + print(f"State: {response.state}") # [END storage_control_disable_rapid_cache] diff --git a/storagecontrol/get_rapid_cache.py b/storagecontrol/get_rapid_cache.py index 42bb159ef5a..eeb8cfe2e1a 100644 --- a/storagecontrol/get_rapid_cache.py +++ b/storagecontrol/get_rapid_cache.py @@ -22,8 +22,8 @@ def get_rapid_cache(bucket_name: str, cache_id: str) -> None: # The ID of your GCS bucket # bucket_name = "your-unique-bucket-name" - # The ID of the Rapid Cache - # cache_id = "your-rapid-cache-id" + # The ID of the Rapid Cache, which is the same as its zone + # cache_id = "us-central1-a" storage_control_client = storage_control_v2.StorageControlClient() # The storage bucket path uses the global access pattern, in which the "_" @@ -38,6 +38,7 @@ def get_rapid_cache(bucket_name: str, cache_id: str) -> None: print(f"Got Rapid Cache: {response.name}") print(f"Zone: {response.zone}") print(f"State: {response.state}") + print(f"Cache type: {response.cache_type}") # [END storage_control_get_rapid_cache] diff --git a/storagecontrol/rapid_cache_test.py b/storagecontrol/rapid_cache_test.py index 2ba3e5698d9..43283da1e7d 100644 --- a/storagecontrol/rapid_cache_test.py +++ b/storagecontrol/rapid_cache_test.py @@ -12,8 +12,10 @@ # See the License for the specific language governing permissions and # limitations under the License. -import time +import concurrent.futures +import logging +from google.api_core import exceptions from google.cloud import storage from google.cloud import storage_control_v2 import pytest @@ -24,9 +26,10 @@ import list_rapid_caches import update_rapid_cache -# Poll for up to 2 minutes for the cache to reach the "disabled" state. -_DISABLE_POLL_ATTEMPTS = 12 -_DISABLE_POLL_INTERVAL_SECONDS = 10 +# The disable operation only completes once the cache has been fully removed, +# which can take more than an hour. The cache reaches the "disabled" state +# within seconds, so the test bounds the wait and then checks the state. +_DISABLE_TIMEOUT_SECONDS = 600 def test_rapid_cache_create_get_list_update_disable( @@ -35,26 +38,30 @@ def test_rapid_cache_create_get_list_update_disable( rapid_cache_zone: str, ) -> None: bucket_name = regional_hns_enabled_bucket.name - - # Test create Rapid Cache - create_rapid_cache.create_rapid_cache( - bucket_name=bucket_name, zone=rapid_cache_zone + # The Rapid Cache ID is the same as its zone. + cache_id = rapid_cache_zone + cache_name = storage_control_v2.StorageControlClient.rapid_cache_path( + project="_", bucket=bucket_name, rapid_cache=cache_id ) - out, _ = capsys.readouterr() - prefix = "Created Rapid Cache: " - assert prefix in out - cache_name = out.split(prefix, 1)[1].split()[0] - cache_id = storage_control_v2.StorageControlClient.parse_rapid_cache_path( - cache_name - )["rapid_cache"] - assert cache_id + disable_requested = False try: + # Test create Rapid Cache + create_rapid_cache.create_rapid_cache( + bucket_name=bucket_name, zone=rapid_cache_zone + ) + out, _ = capsys.readouterr() + assert f"Created Rapid Cache: {cache_name}" in out + assert "State: running" in out + assert "Cache type: rapid-cache-ultra" in out + # Test get Rapid Cache get_rapid_cache.get_rapid_cache(bucket_name=bucket_name, cache_id=cache_id) out, _ = capsys.readouterr() - assert cache_name in out + assert f"Got Rapid Cache: {cache_name}" in out assert f"Zone: {rapid_cache_zone}" in out + assert "State: running" in out + assert "Cache type: rapid-cache-ultra" in out # Test list Rapid Caches list_rapid_caches.list_rapid_caches(bucket_name=bucket_name) @@ -68,22 +75,36 @@ def test_rapid_cache_create_get_list_update_disable( out, _ = capsys.readouterr() assert f"Updated Rapid Cache: {cache_name}" in out assert "TTL: 172800 seconds" in out - finally: - # Disable the Rapid Cache. This also cleans up the cache so that the - # bucket can be deleted once the cache has been fully removed. - disable_rapid_cache.disable_rapid_cache( - bucket_name=bucket_name, cache_id=cache_id - ) - disable_out, _ = capsys.readouterr() - - # Test disable Rapid Cache - assert f"Disabling Rapid Cache: {cache_name}" in disable_out - # The cache moves to the "disabled" state shortly after the request. - for _ in range(_DISABLE_POLL_ATTEMPTS): - get_rapid_cache.get_rapid_cache(bucket_name=bucket_name, cache_id=cache_id) - out, _ = capsys.readouterr() - if "State: disabled" in out: - break - time.sleep(_DISABLE_POLL_INTERVAL_SECONDS) - assert "State: disabled" in out + # Test disable Rapid Cache + disable_requested = True + try: + disable_rapid_cache.disable_rapid_cache( + bucket_name=bucket_name, + cache_id=cache_id, + timeout=_DISABLE_TIMEOUT_SECONDS, + ) + out, _ = capsys.readouterr() + assert f"Disabled Rapid Cache: {cache_name}" in out + assert "State: disabled" in out + except concurrent.futures.TimeoutError: + logging.warning( + "Disable operation for %s did not complete within %s seconds; " + "checking the cache state instead.", + cache_name, + _DISABLE_TIMEOUT_SECONDS, + ) + capsys.readouterr() + get_rapid_cache.get_rapid_cache(bucket_name=bucket_name, cache_id=cache_id) + out, _ = capsys.readouterr() + assert "State: disabled" in out + finally: + if not disable_requested: + # Disable the Rapid Cache so that the bucket can be deleted once the + # cache has been fully removed. Errors are ignored. + try: + storage_control_v2.StorageControlClient().disable_rapid_cache( + name=cache_name + ) + except exceptions.GoogleAPIError as e: + logging.warning("Could not disable %s: %s", cache_name, e) diff --git a/storagecontrol/update_rapid_cache.py b/storagecontrol/update_rapid_cache.py index ac0061f792b..2267a1e7bdd 100644 --- a/storagecontrol/update_rapid_cache.py +++ b/storagecontrol/update_rapid_cache.py @@ -24,8 +24,8 @@ def update_rapid_cache(bucket_name: str, cache_id: str, ttl_seconds: int) -> Non # The ID of your GCS bucket # bucket_name = "your-unique-bucket-name" - # The ID of the Rapid Cache - # cache_id = "your-rapid-cache-id" + # The ID of the Rapid Cache, which is the same as its zone + # cache_id = "us-central1-a" # The new time to live (TTL) for cache entries, in seconds. The TTL must # be between 1 hour and 7 days. From 8a8bf9126de6c79abe80bc50c52dbdf1010908fa Mon Sep 17 00:00:00 2001 From: Sneha Sahu Date: Mon, 2 Nov 2026 18:41:24 +0000 Subject: [PATCH 3/3] fix(storagecontrol): resolve flake8 import-order lint failures Reorder imports in storagecontrol/conftest.py and storagecontrol/rapid_cache_test.py to match the repository-root flake8-import-order configuration used by the GitHub Actions Lint workflow. [Generated-by: AI] --- storagecontrol/conftest.py | 3 ++- storagecontrol/rapid_cache_test.py | 12 ++++++------ 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/storagecontrol/conftest.py b/storagecontrol/conftest.py index a43b0d5b980..5a350b3003c 100644 --- a/storagecontrol/conftest.py +++ b/storagecontrol/conftest.py @@ -12,7 +12,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -import logging import os import time import uuid @@ -23,6 +22,8 @@ import pytest +import logging + @pytest.fixture(scope="module") def project_id() -> str: diff --git a/storagecontrol/rapid_cache_test.py b/storagecontrol/rapid_cache_test.py index 43283da1e7d..605930e1477 100644 --- a/storagecontrol/rapid_cache_test.py +++ b/storagecontrol/rapid_cache_test.py @@ -13,19 +13,19 @@ # limitations under the License. import concurrent.futures -import logging - -from google.api_core import exceptions -from google.cloud import storage -from google.cloud import storage_control_v2 -import pytest import create_rapid_cache import disable_rapid_cache import get_rapid_cache +from google.api_core import exceptions +from google.cloud import storage +from google.cloud import storage_control_v2 import list_rapid_caches +import pytest import update_rapid_cache +import logging + # The disable operation only completes once the cache has been fully removed, # which can take more than an hour. The cache reaches the "disabled" state # within seconds, so the test bounds the wait and then checks the state.