Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions storagecontrol/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,17 @@
# limitations under the License.

import os
import time
import uuid

from google.api_core import exceptions
from google.api_core import retry
from google.cloud import storage

import pytest

import logging


@pytest.fixture(scope="module")
def project_id() -> str:
Expand Down Expand Up @@ -66,6 +71,46 @@ 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)
# 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 an hour. 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:
"""
Expand Down
66 changes: 66 additions & 0 deletions storagecontrol/create_rapid_cache.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# 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
from google.protobuf import duration_pb2


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}"
# 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,
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}")
print(f"State: {response.state}")
print(f"Cache type: {response.cache_type}")


# [END storage_control_create_rapid_cache]


if __name__ == "__main__":
create_rapid_cache(bucket_name=sys.argv[1], zone=sys.argv[2])
56 changes: 56 additions & 0 deletions storagecontrol/disable_rapid_cache.py
Original file line number Diff line number Diff line change
@@ -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_disable_rapid_cache]
from google.cloud import storage_control_v2


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, 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 "_"
# 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 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"Disabled Rapid Cache: {response.name}")
print(f"State: {response.state}")


# [END storage_control_disable_rapid_cache]


if __name__ == "__main__":
disable_rapid_cache(bucket_name=sys.argv[1], cache_id=sys.argv[2])
48 changes: 48 additions & 0 deletions storagecontrol/get_rapid_cache.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# 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, 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 "_"
# 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}")
print(f"Cache type: {response.cache_type}")


# [END storage_control_get_rapid_cache]


if __name__ == "__main__":
get_rapid_cache(bucket_name=sys.argv[1], cache_id=sys.argv[2])
45 changes: 45 additions & 0 deletions storagecontrol/list_rapid_caches.py
Original file line number Diff line number Diff line change
@@ -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])
110 changes: 110 additions & 0 deletions storagecontrol/rapid_cache_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
# 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 concurrent.futures

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.
_DISABLE_TIMEOUT_SECONDS = 600


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
# 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
)
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 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)
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

# 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)
2 changes: 1 addition & 1 deletion storagecontrol/requirements.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
google-cloud-storage-control==1.11.0
google-cloud-storage-control==1.17.0
Loading
Loading