From 6b5b90583c2421b605ef3a4c24d774a9b841b184 Mon Sep 17 00:00:00 2001 From: raj-prince Date: Tue, 17 Mar 2026 06:14:09 +0000 Subject: [PATCH 01/13] feat(control-client-stall): adding stall feature for control client --- STALL_VALIDATION.md | 156 ++ google/iam/v1/iam_policy_pb2.py | 14 - google/iam/v1/iam_policy_pb2_grpc.py | 14 - google/iam/v1/options_pb2.py | 14 - google/iam/v1/options_pb2_grpc.py | 14 - google/iam/v1/policy_pb2.py | 14 - google/iam/v1/policy_pb2_grpc.py | 14 - google/storage/control/__init__.py | 13 + google/storage/control/v2/__init__.py | 13 + .../storage/control/v2/storage_control_pb2.py | 418 ++++++ .../control/v2/storage_control_pb2_grpc.py | 1275 +++++++++++++++++ google/storage/v2/storage_pb2.py | 14 - google/storage/v2/storage_pb2_grpc.py | 14 - testbench/database.py | 63 +- testbench/grpc_server.py | 79 + tests/test_storage_control_stall.py | 195 +++ update-protos.sh | 1 + 17 files changed, 2212 insertions(+), 113 deletions(-) create mode 100644 STALL_VALIDATION.md create mode 100644 google/storage/control/__init__.py create mode 100644 google/storage/control/v2/__init__.py create mode 100644 google/storage/control/v2/storage_control_pb2.py create mode 100644 google/storage/control/v2/storage_control_pb2_grpc.py create mode 100644 tests/test_storage_control_stall.py diff --git a/STALL_VALIDATION.md b/STALL_VALIDATION.md new file mode 100644 index 00000000..c2727af6 --- /dev/null +++ b/STALL_VALIDATION.md @@ -0,0 +1,156 @@ +# Storage Control API Stall Implementation - Validation Guide + +## Overview + +This document describes how to validate the stall functionality for the Storage Control API that was implemented in this branch. + +## What Was Implemented + +1. **Protobuf Generation**: Added `google/storage/control/v2/storage_control.proto` to the proto generation pipeline +2. **Database Layer**: Added folder storage **capabilities** to `testbench/database.py` +3. **gRPC Service**: Implemented `StorageControlServicer` in `testbench/grpc_server.py` with stall support +4. **Stall Functionality**: Added `_apply_stall()` method that intercepts folder API calls and applies delays based on `x-goog-emulator-instructions` metadata + +## Stall Instructions Supported + +The implementation supports the following stall instructions via gRPC metadata header `x-goog-emulator-instructions`: + +- `stall-always`: Stalls for 10 seconds at the beginning of the request +- `stall-for-Ns`: Stalls for N seconds (e.g., `stall-for-3s` stalls for 3 seconds) + +## Supported Operations + +All Storage Control API folder operations support stall: +- `CreateFolder` +- `DeleteFolder` +- `GetFolder` +- `ListFolders` +- `RenameFolder` + +## Running the Tests + +### Prerequisites + +1. Ensure you're in a virtual environment: +```bash +source ../venv-storage-testbench/bin/activate +``` + +### Quick Validation (Non-Stall Test) + +Run this test to verify basic functionality (~1 second): +```bash +python -m unittest tests.test_storage_control_stall.TestStorageControlStall.test_create_folder_no_stall -v +``` + +### Stall Functionality Tests + +Run these tests to verify stall functionality (each takes several seconds due to intentional delays): + +**Custom duration stall (3 seconds):** +```bash +python -m unittest tests.test_storage_control_stall.TestStorageControlStall.test_create_folder_stall_custom_duration -v +``` + +**Delete with stall (2 seconds):** +```bash +python -m unittest tests.test_storage_control_stall.TestStorageControlStall.test_delete_folder_stall -v +``` + +**Get with stall (5 seconds):** +```bash +python -m unittest tests.test_storage_control_stall.TestStorageControlStall.test_get_folder_stall -v +``` + +**List with stall (4 seconds):** +```bash +python -m unittest tests.test_storage_control_stall.TestStorageControlStall.test_list_folders_stall -v +``` + +**Rename with stall (6 seconds):** +```bash +python -m unittest tests.test_storage_control_stall.TestStorageControlStall.test_rename_folder_stall -v +``` + +### Full Test Suite + +Run all tests at once (takes ~30+ seconds): +```bash +python -m unittest tests.test_storage_control_stall -v +``` + +**Note:** The `test_create_folder_stall_always` test takes 10+ seconds as it uses the default stall duration. + +## Manual Testing with gRPC Client + +You can also test using a Python gRPC client: + +```python +import grpc +from google.storage.control.v2 import storage_control_pb2, storage_control_pb2_grpc +import time + +# Connect to testbench +channel = grpc.insecure_channel('localhost:9099') # Use actual gRPC port +stub = storage_control_pb2_grpc.StorageControlStub(channel) + +# Test with stall-for-5s instruction +metadata = [('x-goog-emulator-instructions', 'stall-for-5s')] +request = storage_control_pb2.CreateFolderRequest() +request.parent = "projects/_/buckets/test-bucket" +request.folder_id = "my-test-folder" + +start = time.time() +folder = stub.CreateFolder(request, metadata=metadata) +elapsed = time.time() - start + +print(f"Request took {elapsed:.2f} seconds") +print(f"Created folder: {folder.name}") +``` + +## Expected Test Results + +✅ **Success Criteria:** +- Non-stall tests complete in < 1 second +- `stall-always` tests take >= 10 seconds +- `stall-for-Ns` tests take >= N seconds (within 1-2 seconds tolerance) +- Folder operations (create, delete, get, list, rename) all work correctly +- Stall applies before the operation executes + +## Integration with Existing Tests + +The implementation doesn't affect existing Storage API tests. You can verify this by running: + +```bash +python -m unittest discover -s tests/ -p "test_grpc_server.py" -v +``` + +This should pass without any issues. + +## Files Modified + +- `update-protos.sh`: Added storage_control.proto to the generation list +- `testbench/database.py`: Added folder storage methods +- `testbench/grpc_server.py`: Added StorageControlServicer with stall support +- `tests/test_storage_control_stall.py`: Comprehensive test suite for stall functionality +- `google/storage/control/v2/`: Generated protobuf files (storage_control_pb2.py, storage_control_pb2_grpc.py) + +## Troubleshooting + +**Issue: ImportError for storage_control_pb2** +- Solution: Regenerate protobuf files: `source ../venv-storage-testbench/bin/activate && bash update-protos.sh` + +**Issue: Tests timing out** +- Solution: Increase timeout values in test runner, stall tests are intentionally slow + +**Issue: "Protocol message Folder has no field 'bucket'"** +- Solution: This was fixed - Folder only has fields: name, metageneration, create_time, update_time, pending_rename_info + +## Architecture Notes + +The stall implementation uses `time.sleep()` which blocks the current thread. This is appropriate for: +- Testing timeout handling +- Simulating slow backends +- Reproducing transient network conditions + +The implementation extracts stall instructions from gRPC metadata via `testbench.common.extract_instruction()`, which checks for the `x-goog-emulator-instructions` header in the invocation metadata. diff --git a/google/iam/v1/iam_policy_pb2.py b/google/iam/v1/iam_policy_pb2.py index 03eda47c..a294d174 100644 --- a/google/iam/v1/iam_policy_pb2.py +++ b/google/iam/v1/iam_policy_pb2.py @@ -1,18 +1,4 @@ # -*- coding: utf-8 -*- -# Copyright 2024 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. - # Generated by the protocol buffer compiler. DO NOT EDIT! # NO CHECKED-IN PROTOBUF GENCODE # source: google/iam/v1/iam_policy.proto diff --git a/google/iam/v1/iam_policy_pb2_grpc.py b/google/iam/v1/iam_policy_pb2_grpc.py index 28ed5951..0d2513db 100644 --- a/google/iam/v1/iam_policy_pb2_grpc.py +++ b/google/iam/v1/iam_policy_pb2_grpc.py @@ -1,18 +1,4 @@ # Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! -# Copyright 2024 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. - """Client and server classes corresponding to protobuf-defined services.""" import grpc import warnings diff --git a/google/iam/v1/options_pb2.py b/google/iam/v1/options_pb2.py index 75e2a2b3..65739b0d 100644 --- a/google/iam/v1/options_pb2.py +++ b/google/iam/v1/options_pb2.py @@ -1,18 +1,4 @@ # -*- coding: utf-8 -*- -# Copyright 2024 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. - # Generated by the protocol buffer compiler. DO NOT EDIT! # NO CHECKED-IN PROTOBUF GENCODE # source: google/iam/v1/options.proto diff --git a/google/iam/v1/options_pb2_grpc.py b/google/iam/v1/options_pb2_grpc.py index baae5aa9..8c96ff94 100644 --- a/google/iam/v1/options_pb2_grpc.py +++ b/google/iam/v1/options_pb2_grpc.py @@ -1,18 +1,4 @@ # Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! -# Copyright 2024 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. - """Client and server classes corresponding to protobuf-defined services.""" import grpc import warnings diff --git a/google/iam/v1/policy_pb2.py b/google/iam/v1/policy_pb2.py index 2c976271..83cee4c1 100644 --- a/google/iam/v1/policy_pb2.py +++ b/google/iam/v1/policy_pb2.py @@ -1,18 +1,4 @@ # -*- coding: utf-8 -*- -# Copyright 2024 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. - # Generated by the protocol buffer compiler. DO NOT EDIT! # NO CHECKED-IN PROTOBUF GENCODE # source: google/iam/v1/policy.proto diff --git a/google/iam/v1/policy_pb2_grpc.py b/google/iam/v1/policy_pb2_grpc.py index af0dbb7f..b9faf955 100644 --- a/google/iam/v1/policy_pb2_grpc.py +++ b/google/iam/v1/policy_pb2_grpc.py @@ -1,18 +1,4 @@ # Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! -# Copyright 2024 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. - """Client and server classes corresponding to protobuf-defined services.""" import grpc import warnings diff --git a/google/storage/control/__init__.py b/google/storage/control/__init__.py new file mode 100644 index 00000000..c6334245 --- /dev/null +++ b/google/storage/control/__init__.py @@ -0,0 +1,13 @@ +# Copyright 2020 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. diff --git a/google/storage/control/v2/__init__.py b/google/storage/control/v2/__init__.py new file mode 100644 index 00000000..c6334245 --- /dev/null +++ b/google/storage/control/v2/__init__.py @@ -0,0 +1,13 @@ +# Copyright 2020 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. diff --git a/google/storage/control/v2/storage_control_pb2.py b/google/storage/control/v2/storage_control_pb2.py new file mode 100644 index 00000000..5f9bc472 --- /dev/null +++ b/google/storage/control/v2/storage_control_pb2.py @@ -0,0 +1,418 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: google/storage/control/v2/storage_control.proto +# Protobuf Python Version: 5.29.0 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 5, + 29, + 0, + '', + 'google/storage/control/v2/storage_control.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from google.api import annotations_pb2 as google_dot_api_dot_annotations__pb2 +from google.api import client_pb2 as google_dot_api_dot_client__pb2 +from google.api import field_behavior_pb2 as google_dot_api_dot_field__behavior__pb2 +from google.api import field_info_pb2 as google_dot_api_dot_field__info__pb2 +from google.api import resource_pb2 as google_dot_api_dot_resource__pb2 +from google.api import routing_pb2 as google_dot_api_dot_routing__pb2 +from google.iam.v1 import iam_policy_pb2 as google_dot_iam_dot_v1_dot_iam__policy__pb2 +from google.iam.v1 import policy_pb2 as google_dot_iam_dot_v1_dot_policy__pb2 +from google.longrunning import operations_pb2 as google_dot_longrunning_dot_operations__pb2 +from google.protobuf import duration_pb2 as google_dot_protobuf_dot_duration__pb2 +from google.protobuf import empty_pb2 as google_dot_protobuf_dot_empty__pb2 +from google.protobuf import field_mask_pb2 as google_dot_protobuf_dot_field__mask__pb2 +from google.protobuf import timestamp_pb2 as google_dot_protobuf_dot_timestamp__pb2 + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n/google/storage/control/v2/storage_control.proto\x12\x19google.storage.control.v2\x1a\x1cgoogle/api/annotations.proto\x1a\x17google/api/client.proto\x1a\x1fgoogle/api/field_behavior.proto\x1a\x1bgoogle/api/field_info.proto\x1a\x19google/api/resource.proto\x1a\x18google/api/routing.proto\x1a\x1egoogle/iam/v1/iam_policy.proto\x1a\x1agoogle/iam/v1/policy.proto\x1a#google/longrunning/operations.proto\x1a\x1egoogle/protobuf/duration.proto\x1a\x1bgoogle/protobuf/empty.proto\x1a google/protobuf/field_mask.proto\x1a\x1fgoogle/protobuf/timestamp.proto\"+\n\x11PendingRenameInfo\x12\x16\n\toperation\x18\x01 \x01(\tB\x03\xe0\x41\x03\"\xe2\x02\n\x06\x46older\x12\x11\n\x04name\x18\x01 \x01(\tB\x03\xe0\x41\x08\x12\x1b\n\x0emetageneration\x18\x03 \x01(\x03\x42\x03\xe0\x41\x03\x12\x34\n\x0b\x63reate_time\x18\x04 \x01(\x0b\x32\x1a.google.protobuf.TimestampB\x03\xe0\x41\x03\x12\x34\n\x0bupdate_time\x18\x05 \x01(\x0b\x32\x1a.google.protobuf.TimestampB\x03\xe0\x41\x03\x12N\n\x13pending_rename_info\x18\x07 \x01(\x0b\x32,.google.storage.control.v2.PendingRenameInfoB\x03\xe0\x41\x03:l\xea\x41i\n\x1dstorage.googleapis.com/Folder\x12\x37projects/{project}/buckets/{bucket}/folders/{folder=**}*\x07\x66olders2\x06\x66older\"\xf4\x01\n\x10GetFolderRequest\x12\x33\n\x04name\x18\x06 \x01(\tB%\xe0\x41\x02\xfa\x41\x1f\n\x1dstorage.googleapis.com/Folder\x12$\n\x17if_metageneration_match\x18\x03 \x01(\x03H\x00\x88\x01\x01\x12(\n\x1bif_metageneration_not_match\x18\x04 \x01(\x03H\x01\x88\x01\x01\x12\x1f\n\nrequest_id\x18\x05 \x01(\tB\x0b\xe0\x41\x01\xe2\x8c\xcf\xd7\x08\x02\x08\x01\x42\x1a\n\x18_if_metageneration_matchB\x1e\n\x1c_if_metageneration_not_match\"\xd5\x01\n\x13\x43reateFolderRequest\x12\x35\n\x06parent\x18\x01 \x01(\tB%\xe0\x41\x02\xfa\x41\x1f\x12\x1dstorage.googleapis.com/Folder\x12\x36\n\x06\x66older\x18\x02 \x01(\x0b\x32!.google.storage.control.v2.FolderB\x03\xe0\x41\x02\x12\x16\n\tfolder_id\x18\x03 \x01(\tB\x03\xe0\x41\x02\x12\x16\n\trecursive\x18\x04 \x01(\x08\x42\x03\xe0\x41\x01\x12\x1f\n\nrequest_id\x18\x05 \x01(\tB\x0b\xe0\x41\x01\xe2\x8c\xcf\xd7\x08\x02\x08\x01\"\xf7\x01\n\x13\x44\x65leteFolderRequest\x12\x33\n\x04name\x18\x06 \x01(\tB%\xe0\x41\x02\xfa\x41\x1f\n\x1dstorage.googleapis.com/Folder\x12$\n\x17if_metageneration_match\x18\x03 \x01(\x03H\x00\x88\x01\x01\x12(\n\x1bif_metageneration_not_match\x18\x04 \x01(\x03H\x01\x88\x01\x01\x12\x1f\n\nrequest_id\x18\x05 \x01(\tB\x0b\xe0\x41\x01\xe2\x8c\xcf\xd7\x08\x02\x08\x01\x42\x1a\n\x18_if_metageneration_matchB\x1e\n\x1c_if_metageneration_not_match\"\x8c\x02\n\x12ListFoldersRequest\x12\x35\n\x06parent\x18\x01 \x01(\tB%\xe0\x41\x02\xfa\x41\x1f\x12\x1dstorage.googleapis.com/Folder\x12\x16\n\tpage_size\x18\x02 \x01(\x05\x42\x03\xe0\x41\x01\x12\x17\n\npage_token\x18\x03 \x01(\tB\x03\xe0\x41\x01\x12\x13\n\x06prefix\x18\x04 \x01(\tB\x03\xe0\x41\x01\x12\x16\n\tdelimiter\x18\x08 \x01(\tB\x03\xe0\x41\x01\x12 \n\x13lexicographic_start\x18\x06 \x01(\tB\x03\xe0\x41\x01\x12\x1e\n\x11lexicographic_end\x18\x07 \x01(\tB\x03\xe0\x41\x01\x12\x1f\n\nrequest_id\x18\t \x01(\tB\x0b\xe0\x41\x01\xe2\x8c\xcf\xd7\x08\x02\x08\x01\"b\n\x13ListFoldersResponse\x12\x32\n\x07\x66olders\x18\x01 \x03(\x0b\x32!.google.storage.control.v2.Folder\x12\x17\n\x0fnext_page_token\x18\x02 \x01(\t\"\x9b\x02\n\x13RenameFolderRequest\x12\x33\n\x04name\x18\x07 \x01(\tB%\xe0\x41\x02\xfa\x41\x1f\n\x1dstorage.googleapis.com/Folder\x12\"\n\x15\x64\x65stination_folder_id\x18\x08 \x01(\tB\x03\xe0\x41\x02\x12$\n\x17if_metageneration_match\x18\x04 \x01(\x03H\x00\x88\x01\x01\x12(\n\x1bif_metageneration_not_match\x18\x05 \x01(\x03H\x01\x88\x01\x01\x12\x1f\n\nrequest_id\x18\x06 \x01(\tB\x0b\xe0\x41\x01\xe2\x8c\xcf\xd7\x08\x02\x08\x01\x42\x1a\n\x18_if_metageneration_matchB\x1e\n\x1c_if_metageneration_not_match\"\x8a\x02\n\x1c\x44\x65leteFolderRecursiveRequest\x12\x33\n\x04name\x18\x01 \x01(\tB%\xe0\x41\x02\xfa\x41\x1f\n\x1dstorage.googleapis.com/Folder\x12)\n\x17if_metageneration_match\x18\x02 \x01(\x03\x42\x03\xe0\x41\x01H\x00\x88\x01\x01\x12-\n\x1bif_metageneration_not_match\x18\x03 \x01(\x03\x42\x03\xe0\x41\x01H\x01\x88\x01\x01\x12\x1f\n\nrequest_id\x18\x04 \x01(\tB\x0b\xe0\x41\x01\xe2\x8c\xcf\xd7\x08\x02\x08\x01\x42\x1a\n\x18_if_metageneration_matchB\x1e\n\x1c_if_metageneration_not_match\"\x9a\x02\n\"CommonLongRunningOperationMetadata\x12\x34\n\x0b\x63reate_time\x18\x01 \x01(\x0b\x32\x1a.google.protobuf.TimestampB\x03\xe0\x41\x03\x12\x31\n\x08\x65nd_time\x18\x02 \x01(\x0b\x32\x1a.google.protobuf.TimestampB\x03\xe0\x41\x03\x12\x34\n\x0bupdate_time\x18\x03 \x01(\x0b\x32\x1a.google.protobuf.TimestampB\x03\xe0\x41\x03\x12\x11\n\x04type\x18\x04 \x01(\tB\x03\xe0\x41\x03\x12#\n\x16requested_cancellation\x18\x05 \x01(\x08\x42\x03\xe0\x41\x03\x12\x1d\n\x10progress_percent\x18\x06 \x01(\x05\x42\x03\xe0\x41\x03\"\xa7\x01\n\x14RenameFolderMetadata\x12V\n\x0f\x63ommon_metadata\x18\x01 \x01(\x0b\x32=.google.storage.control.v2.CommonLongRunningOperationMetadata\x12\x18\n\x10source_folder_id\x18\x02 \x01(\t\x12\x1d\n\x15\x64\x65stination_folder_id\x18\x03 \x01(\t\"\x8a\x01\n\x1d\x44\x65leteFolderRecursiveMetadata\x12V\n\x0f\x63ommon_metadata\x18\x01 \x01(\x0b\x32=.google.storage.control.v2.CommonLongRunningOperationMetadata\x12\x11\n\tfolder_id\x18\x02 \x01(\t\"\xf8\x03\n\rStorageLayout\x12\x11\n\x04name\x18\x01 \x01(\tB\x03\xe0\x41\x03\x12\x15\n\x08location\x18\x02 \x01(\tB\x03\xe0\x41\x03\x12\x1a\n\rlocation_type\x18\x03 \x01(\tB\x03\xe0\x41\x03\x12\x64\n\x17\x63ustom_placement_config\x18\x04 \x01(\x0b\x32>.google.storage.control.v2.StorageLayout.CustomPlacementConfigB\x03\xe0\x41\x03\x12\x63\n\x16hierarchical_namespace\x18\x05 \x01(\x0b\x32>.google.storage.control.v2.StorageLayout.HierarchicalNamespaceB\x03\xe0\x41\x03\x1a/\n\x15\x43ustomPlacementConfig\x12\x16\n\x0e\x64\x61ta_locations\x18\x01 \x03(\t\x1a(\n\x15HierarchicalNamespace\x12\x0f\n\x07\x65nabled\x18\x01 \x01(\x08:{\xea\x41x\n$storage.googleapis.com/StorageLayout\x12\x31projects/{project}/buckets/{bucket}/storageLayout*\x0estorageLayouts2\rstorageLayout\"\x86\x01\n\x17GetStorageLayoutRequest\x12:\n\x04name\x18\x01 \x01(\tB,\xe0\x41\x02\xfa\x41&\n$storage.googleapis.com/StorageLayout\x12\x0e\n\x06prefix\x18\x02 \x01(\t\x12\x1f\n\nrequest_id\x18\x03 \x01(\tB\x0b\xe0\x41\x01\xe2\x8c\xcf\xd7\x08\x02\x08\x01\"\xbf\x02\n\rManagedFolder\x12\x11\n\x04name\x18\x01 \x01(\tB\x03\xe0\x41\x08\x12\x1b\n\x0emetageneration\x18\x03 \x01(\x03\x42\x03\xe0\x41\x03\x12\x34\n\x0b\x63reate_time\x18\x04 \x01(\x0b\x32\x1a.google.protobuf.TimestampB\x03\xe0\x41\x03\x12\x34\n\x0bupdate_time\x18\x05 \x01(\x0b\x32\x1a.google.protobuf.TimestampB\x03\xe0\x41\x03:\x91\x01\xea\x41\x8d\x01\n$storage.googleapis.com/ManagedFolder\x12\x46projects/{project}/buckets/{bucket}/managedFolders/{managed_folder=**}*\x0emanagedFolders2\rmanagedFolder\"\x82\x02\n\x17GetManagedFolderRequest\x12:\n\x04name\x18\x06 \x01(\tB,\xe0\x41\x02\xfa\x41&\n$storage.googleapis.com/ManagedFolder\x12$\n\x17if_metageneration_match\x18\x03 \x01(\x03H\x00\x88\x01\x01\x12(\n\x1bif_metageneration_not_match\x18\x04 \x01(\x03H\x01\x88\x01\x01\x12\x1f\n\nrequest_id\x18\x05 \x01(\tB\x0b\xe0\x41\x01\xe2\x8c\xcf\xd7\x08\x02\x08\x01\x42\x1a\n\x18_if_metageneration_matchB\x1e\n\x1c_if_metageneration_not_match\"\xe2\x01\n\x1a\x43reateManagedFolderRequest\x12<\n\x06parent\x18\x01 \x01(\tB,\xe0\x41\x02\xfa\x41&\x12$storage.googleapis.com/ManagedFolder\x12\x45\n\x0emanaged_folder\x18\x02 \x01(\x0b\x32(.google.storage.control.v2.ManagedFolderB\x03\xe0\x41\x02\x12\x1e\n\x11managed_folder_id\x18\x03 \x01(\tB\x03\xe0\x41\x02\x12\x1f\n\nrequest_id\x18\x04 \x01(\tB\x0b\xe0\x41\x01\xe2\x8c\xcf\xd7\x08\x02\x08\x01\"\x9e\x02\n\x1a\x44\x65leteManagedFolderRequest\x12:\n\x04name\x18\x07 \x01(\tB,\xe0\x41\x02\xfa\x41&\n$storage.googleapis.com/ManagedFolder\x12$\n\x17if_metageneration_match\x18\x03 \x01(\x03H\x00\x88\x01\x01\x12(\n\x1bif_metageneration_not_match\x18\x04 \x01(\x03H\x01\x88\x01\x01\x12\x17\n\x0f\x61llow_non_empty\x18\x05 \x01(\x08\x12\x1f\n\nrequest_id\x18\x06 \x01(\tB\x0b\xe0\x41\x01\xe2\x8c\xcf\xd7\x08\x02\x08\x01\x42\x1a\n\x18_if_metageneration_matchB\x1e\n\x1c_if_metageneration_not_match\"\xc0\x01\n\x19ListManagedFoldersRequest\x12<\n\x06parent\x18\x01 \x01(\tB,\xe0\x41\x02\xfa\x41&\x12$storage.googleapis.com/ManagedFolder\x12\x16\n\tpage_size\x18\x02 \x01(\x05\x42\x03\xe0\x41\x01\x12\x17\n\npage_token\x18\x03 \x01(\tB\x03\xe0\x41\x01\x12\x13\n\x06prefix\x18\x04 \x01(\tB\x03\xe0\x41\x01\x12\x1f\n\nrequest_id\x18\x05 \x01(\tB\x0b\xe0\x41\x01\xe2\x8c\xcf\xd7\x08\x02\x08\x01\"x\n\x1aListManagedFoldersResponse\x12\x41\n\x0fmanaged_folders\x18\x01 \x03(\x0b\x32(.google.storage.control.v2.ManagedFolder\x12\x17\n\x0fnext_page_token\x18\x02 \x01(\t\"\xb0\x02\n\x1b\x43reateAnywhereCacheMetadata\x12V\n\x0f\x63ommon_metadata\x18\x01 \x01(\x0b\x32=.google.storage.control.v2.CommonLongRunningOperationMetadata\x12\x1e\n\x11\x61nywhere_cache_id\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x11\n\x04zone\x18\x06 \x01(\tH\x01\x88\x01\x01\x12+\n\x03ttl\x18\x03 \x01(\x0b\x32\x19.google.protobuf.DurationH\x02\x88\x01\x01\x12\x1d\n\x10\x61\x64mission_policy\x18\x05 \x01(\tH\x03\x88\x01\x01\x42\x14\n\x12_anywhere_cache_idB\x07\n\x05_zoneB\x06\n\x04_ttlB\x13\n\x11_admission_policy\"\xb0\x02\n\x1bUpdateAnywhereCacheMetadata\x12V\n\x0f\x63ommon_metadata\x18\x01 \x01(\x0b\x32=.google.storage.control.v2.CommonLongRunningOperationMetadata\x12\x1e\n\x11\x61nywhere_cache_id\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x11\n\x04zone\x18\x05 \x01(\tH\x01\x88\x01\x01\x12+\n\x03ttl\x18\x03 \x01(\x0b\x32\x19.google.protobuf.DurationH\x02\x88\x01\x01\x12\x1d\n\x10\x61\x64mission_policy\x18\x04 \x01(\tH\x03\x88\x01\x01\x42\x14\n\x12_anywhere_cache_idB\x07\n\x05_zoneB\x06\n\x04_ttlB\x13\n\x11_admission_policy\"\xa5\x03\n\rAnywhereCache\x12\x11\n\x04name\x18\x01 \x01(\tB\x03\xe0\x41\x05\x12\x11\n\x04zone\x18\n \x01(\tB\x03\xe0\x41\x05\x12&\n\x03ttl\x18\x03 \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x18\n\x10\x61\x64mission_policy\x18\t \x01(\t\x12\x12\n\x05state\x18\x05 \x01(\tB\x03\xe0\x41\x03\x12\x34\n\x0b\x63reate_time\x18\x06 \x01(\x0b\x32\x1a.google.protobuf.TimestampB\x03\xe0\x41\x03\x12\x34\n\x0bupdate_time\x18\x07 \x01(\x0b\x32\x1a.google.protobuf.TimestampB\x03\xe0\x41\x03\x12\x1b\n\x0epending_update\x18\x08 \x01(\x08\x42\x03\xe0\x41\x03:\x8e\x01\xea\x41\x8a\x01\n$storage.googleapis.com/AnywhereCache\x12\x43projects/{project}/buckets/{bucket}/anywhereCaches/{anywhere_cache}*\x0e\x61nywhereCaches2\ranywhereCache\"\xc2\x01\n\x1a\x43reateAnywhereCacheRequest\x12<\n\x06parent\x18\x01 \x01(\tB,\xe0\x41\x02\xfa\x41&\x12$storage.googleapis.com/AnywhereCache\x12\x45\n\x0e\x61nywhere_cache\x18\x03 \x01(\x0b\x32(.google.storage.control.v2.AnywhereCacheB\x03\xe0\x41\x02\x12\x1f\n\nrequest_id\x18\x04 \x01(\tB\x0b\xe0\x41\x01\xe2\x8c\xcf\xd7\x08\x02\x08\x01\"\xba\x01\n\x1aUpdateAnywhereCacheRequest\x12\x45\n\x0e\x61nywhere_cache\x18\x01 \x01(\x0b\x32(.google.storage.control.v2.AnywhereCacheB\x03\xe0\x41\x02\x12\x34\n\x0bupdate_mask\x18\x02 \x01(\x0b\x32\x1a.google.protobuf.FieldMaskB\x03\xe0\x41\x02\x12\x1f\n\nrequest_id\x18\x03 \x01(\tB\x0b\xe0\x41\x01\xe2\x8c\xcf\xd7\x08\x02\x08\x01\"z\n\x1b\x44isableAnywhereCacheRequest\x12:\n\x04name\x18\x01 \x01(\tB,\xe0\x41\x02\xfa\x41&\n$storage.googleapis.com/AnywhereCache\x12\x1f\n\nrequest_id\x18\x02 \x01(\tB\x0b\xe0\x41\x01\xe2\x8c\xcf\xd7\x08\x02\x08\x01\"x\n\x19PauseAnywhereCacheRequest\x12:\n\x04name\x18\x01 \x01(\tB,\xe0\x41\x02\xfa\x41&\n$storage.googleapis.com/AnywhereCache\x12\x1f\n\nrequest_id\x18\x02 \x01(\tB\x0b\xe0\x41\x01\xe2\x8c\xcf\xd7\x08\x02\x08\x01\"y\n\x1aResumeAnywhereCacheRequest\x12:\n\x04name\x18\x01 \x01(\tB,\xe0\x41\x02\xfa\x41&\n$storage.googleapis.com/AnywhereCache\x12\x1f\n\nrequest_id\x18\x02 \x01(\tB\x0b\xe0\x41\x01\xe2\x8c\xcf\xd7\x08\x02\x08\x01\"v\n\x17GetAnywhereCacheRequest\x12:\n\x04name\x18\x01 \x01(\tB,\xe0\x41\x02\xfa\x41&\n$storage.googleapis.com/AnywhereCache\x12\x1f\n\nrequest_id\x18\x02 \x01(\tB\x0b\xe0\x41\x01\xe2\x8c\xcf\xd7\x08\x02\x08\x01\"\xa1\x01\n\x19ListAnywhereCachesRequest\x12<\n\x06parent\x18\x01 \x01(\tB,\xe0\x41\x02\xfa\x41&\x12$storage.googleapis.com/AnywhereCache\x12\x11\n\tpage_size\x18\x02 \x01(\x05\x12\x12\n\npage_token\x18\x03 \x01(\t\x12\x1f\n\nrequest_id\x18\x04 \x01(\tB\x0b\xe0\x41\x01\xe2\x8c\xcf\xd7\x08\x02\x08\x01\"x\n\x1aListAnywhereCachesResponse\x12\x41\n\x0f\x61nywhere_caches\x18\x01 \x03(\x0b\x32(.google.storage.control.v2.AnywhereCache\x12\x17\n\x0fnext_page_token\x18\x02 \x01(\t\"\x8e\x0e\n\x12IntelligenceConfig\x12\x11\n\x04name\x18\x01 \x01(\tB\x03\xe0\x41\x08\x12X\n\x0e\x65\x64ition_config\x18\x02 \x01(\x0e\x32;.google.storage.control.v2.IntelligenceConfig.EditionConfigB\x03\xe0\x41\x01\x12\x34\n\x0bupdate_time\x18\x03 \x01(\x0b\x32\x1a.google.protobuf.TimestampB\x03\xe0\x41\x03\x12I\n\x06\x66ilter\x18\x04 \x01(\x0b\x32\x34.google.storage.control.v2.IntelligenceConfig.FilterB\x03\xe0\x41\x01\x12u\n\x1d\x65\x66\x66\x65\x63tive_intelligence_config\x18\x05 \x01(\x0b\x32I.google.storage.control.v2.IntelligenceConfig.EffectiveIntelligenceConfigB\x03\xe0\x41\x03\x12O\n\x0ctrial_config\x18\x07 \x01(\x0b\x32\x39.google.storage.control.v2.IntelligenceConfig.TrialConfig\x1a\xfc\x04\n\x06\x46ilter\x12v\n included_cloud_storage_locations\x18\x01 \x01(\x0b\x32J.google.storage.control.v2.IntelligenceConfig.Filter.CloudStorageLocationsH\x00\x12v\n excluded_cloud_storage_locations\x18\x02 \x01(\x0b\x32J.google.storage.control.v2.IntelligenceConfig.Filter.CloudStorageLocationsH\x00\x12r\n\x1eincluded_cloud_storage_buckets\x18\x03 \x01(\x0b\x32H.google.storage.control.v2.IntelligenceConfig.Filter.CloudStorageBucketsH\x01\x12r\n\x1e\x65xcluded_cloud_storage_buckets\x18\x04 \x01(\x0b\x32H.google.storage.control.v2.IntelligenceConfig.Filter.CloudStorageBucketsH\x01\x1a/\n\x15\x43loudStorageLocations\x12\x16\n\tlocations\x18\x01 \x03(\tB\x03\xe0\x41\x01\x1a\x35\n\x13\x43loudStorageBuckets\x12\x1e\n\x11\x62ucket_id_regexes\x18\x01 \x03(\tB\x03\xe0\x41\x01\x42\x19\n\x17\x63loud_storage_locationsB\x17\n\x15\x63loud_storage_buckets\x1a\x8a\x02\n\x1b\x45\x66\x66\x65\x63tiveIntelligenceConfig\x12z\n\x11\x65\x66\x66\x65\x63tive_edition\x18\x01 \x01(\x0e\x32Z.google.storage.control.v2.IntelligenceConfig.EffectiveIntelligenceConfig.EffectiveEditionB\x03\xe0\x41\x03\x12 \n\x13intelligence_config\x18\x02 \x01(\tB\x03\xe0\x41\x03\"M\n\x10\x45\x66\x66\x65\x63tiveEdition\x12!\n\x1d\x45\x46\x46\x45\x43TIVE_EDITION_UNSPECIFIED\x10\x00\x12\x08\n\x04NONE\x10\x01\x12\x0c\n\x08STANDARD\x10\x02\x1a\x43\n\x0bTrialConfig\x12\x34\n\x0b\x65xpire_time\x18\x03 \x01(\x0b\x32\x1a.google.protobuf.TimestampB\x03\xe0\x41\x03\"c\n\rEditionConfig\x12\x1e\n\x1a\x45\x44ITION_CONFIG_UNSPECIFIED\x10\x00\x12\x0b\n\x07INHERIT\x10\x01\x12\x0c\n\x08\x44ISABLED\x10\x02\x12\x0c\n\x08STANDARD\x10\x03\x12\t\n\x05TRIAL\x10\x05:\x8b\x02\xea\x41\x87\x02\n)storage.googleapis.com/IntelligenceConfig\x12\x38\x66olders/{folder}/locations/{location}/intelligenceConfig\x12;organizations/{org}/locations/{location}/intelligenceConfig\x12:projects/{project}/locations/{location}/intelligenceConfig*\x13intelligenceConfigs2\x12intelligenceConfig\"\xd5\x01\n+UpdateOrganizationIntelligenceConfigRequest\x12O\n\x13intelligence_config\x18\x01 \x01(\x0b\x32-.google.storage.control.v2.IntelligenceConfigB\x03\xe0\x41\x02\x12\x34\n\x0bupdate_mask\x18\x02 \x01(\x0b\x32\x1a.google.protobuf.FieldMaskB\x03\xe0\x41\x02\x12\x1f\n\nrequest_id\x18\x03 \x01(\tB\x0b\xe0\x41\x01\xe2\x8c\xcf\xd7\x08\x02\x08\x01\"\xcf\x01\n%UpdateFolderIntelligenceConfigRequest\x12O\n\x13intelligence_config\x18\x01 \x01(\x0b\x32-.google.storage.control.v2.IntelligenceConfigB\x03\xe0\x41\x02\x12\x34\n\x0bupdate_mask\x18\x02 \x01(\x0b\x32\x1a.google.protobuf.FieldMaskB\x03\xe0\x41\x02\x12\x1f\n\nrequest_id\x18\x03 \x01(\tB\x0b\xe0\x41\x01\xe2\x8c\xcf\xd7\x08\x02\x08\x01\"\xd0\x01\n&UpdateProjectIntelligenceConfigRequest\x12O\n\x13intelligence_config\x18\x01 \x01(\x0b\x32-.google.storage.control.v2.IntelligenceConfigB\x03\xe0\x41\x02\x12\x34\n\x0bupdate_mask\x18\x02 \x01(\x0b\x32\x1a.google.protobuf.FieldMaskB\x03\xe0\x41\x02\x12\x1f\n\nrequest_id\x18\x03 \x01(\tB\x0b\xe0\x41\x01\xe2\x8c\xcf\xd7\x08\x02\x08\x01\"k\n(GetOrganizationIntelligenceConfigRequest\x12?\n\x04name\x18\x01 \x01(\tB1\xe0\x41\x02\xfa\x41+\n)storage.googleapis.com/IntelligenceConfig\"e\n\"GetFolderIntelligenceConfigRequest\x12?\n\x04name\x18\x01 \x01(\tB1\xe0\x41\x02\xfa\x41+\n)storage.googleapis.com/IntelligenceConfig\"f\n#GetProjectIntelligenceConfigRequest\x12?\n\x04name\x18\x01 \x01(\tB1\xe0\x41\x02\xfa\x41+\n)storage.googleapis.com/IntelligenceConfig2\xd2,\n\x0eStorageControl\x12\x9a\x01\n\x0c\x43reateFolder\x12..google.storage.control.v2.CreateFolderRequest\x1a!.google.storage.control.v2.Folder\"7\xda\x41\x17parent,folder,folder_id\x8a\xd3\xe4\x93\x02\x17\x12\x15\n\x06parent\x12\x0b{bucket=**}\x12\x8f\x01\n\x0c\x44\x65leteFolder\x12..google.storage.control.v2.DeleteFolderRequest\x1a\x16.google.protobuf.Empty\"7\xda\x41\x04name\x8a\xd3\xe4\x93\x02*\x12(\n\x04name\x12 {bucket=projects/*/buckets/*}/**\x12\x94\x01\n\tGetFolder\x12+.google.storage.control.v2.GetFolderRequest\x1a!.google.storage.control.v2.Folder\"7\xda\x41\x04name\x8a\xd3\xe4\x93\x02*\x12(\n\x04name\x12 {bucket=projects/*/buckets/*}/**\x12\x94\x01\n\x0bListFolders\x12-.google.storage.control.v2.ListFoldersRequest\x1a..google.storage.control.v2.ListFoldersResponse\"&\xda\x41\x06parent\x8a\xd3\xe4\x93\x02\x17\x12\x15\n\x06parent\x12\x0b{bucket=**}\x12\xcd\x01\n\x0cRenameFolder\x12..google.storage.control.v2.RenameFolderRequest\x1a\x1d.google.longrunning.Operation\"n\xca\x41\x1e\n\x06\x46older\x12\x14RenameFolderMetadata\xda\x41\x1aname,destination_folder_id\x8a\xd3\xe4\x93\x02*\x12(\n\x04name\x12 {bucket=projects/*/buckets/*}/**\x12\xe1\x01\n\x15\x44\x65leteFolderRecursive\x12\x37.google.storage.control.v2.DeleteFolderRecursiveRequest\x1a\x1d.google.longrunning.Operation\"p\xca\x41\x36\n\x15google.protobuf.Empty\x12\x1d\x44\x65leteFolderRecursiveMetadata\xda\x41\x04name\x8a\xd3\xe4\x93\x02*\x12(\n\x04name\x12 {bucket=projects/*/buckets/*}/**\x12\xa9\x01\n\x10GetStorageLayout\x12\x32.google.storage.control.v2.GetStorageLayoutRequest\x1a(.google.storage.control.v2.StorageLayout\"7\xda\x41\x04name\x8a\xd3\xe4\x93\x02*\x12(\n\x04name\x12 {bucket=projects/*/buckets/*}/**\x12\xbf\x01\n\x13\x43reateManagedFolder\x12\x35.google.storage.control.v2.CreateManagedFolderRequest\x1a(.google.storage.control.v2.ManagedFolder\"G\xda\x41\'parent,managed_folder,managed_folder_id\x8a\xd3\xe4\x93\x02\x17\x12\x15\n\x06parent\x12\x0b{bucket=**}\x12\x9d\x01\n\x13\x44\x65leteManagedFolder\x12\x35.google.storage.control.v2.DeleteManagedFolderRequest\x1a\x16.google.protobuf.Empty\"7\xda\x41\x04name\x8a\xd3\xe4\x93\x02*\x12(\n\x04name\x12 {bucket=projects/*/buckets/*}/**\x12\xa9\x01\n\x10GetManagedFolder\x12\x32.google.storage.control.v2.GetManagedFolderRequest\x1a(.google.storage.control.v2.ManagedFolder\"7\xda\x41\x04name\x8a\xd3\xe4\x93\x02*\x12(\n\x04name\x12 {bucket=projects/*/buckets/*}/**\x12\xa9\x01\n\x12ListManagedFolders\x12\x34.google.storage.control.v2.ListManagedFoldersRequest\x1a\x35.google.storage.control.v2.ListManagedFoldersResponse\"&\xda\x41\x06parent\x8a\xd3\xe4\x93\x02\x17\x12\x15\n\x06parent\x12\x0b{bucket=**}\x12\xd1\x01\n\x13\x43reateAnywhereCache\x12\x35.google.storage.control.v2.CreateAnywhereCacheRequest\x1a\x1d.google.longrunning.Operation\"d\xca\x41,\n\rAnywhereCache\x12\x1b\x43reateAnywhereCacheMetadata\xda\x41\x15parent,anywhere_cache\x8a\xd3\xe4\x93\x02\x17\x12\x15\n\x06parent\x12\x0b{bucket=**}\x12\xf9\x01\n\x13UpdateAnywhereCache\x12\x35.google.storage.control.v2.UpdateAnywhereCacheRequest\x1a\x1d.google.longrunning.Operation\"\x8b\x01\xca\x41,\n\rAnywhereCache\x12\x1bUpdateAnywhereCacheMetadata\xda\x41\x1a\x61nywhere_cache,update_mask\x8a\xd3\xe4\x93\x02\x39\x12\x37\n\x13\x61nywhere_cache.name\x12 {bucket=projects/*/buckets/*}/**\x12\xb1\x01\n\x14\x44isableAnywhereCache\x12\x36.google.storage.control.v2.DisableAnywhereCacheRequest\x1a(.google.storage.control.v2.AnywhereCache\"7\xda\x41\x04name\x8a\xd3\xe4\x93\x02*\x12(\n\x04name\x12 {bucket=projects/*/buckets/*}/**\x12\xad\x01\n\x12PauseAnywhereCache\x12\x34.google.storage.control.v2.PauseAnywhereCacheRequest\x1a(.google.storage.control.v2.AnywhereCache\"7\xda\x41\x04name\x8a\xd3\xe4\x93\x02*\x12(\n\x04name\x12 {bucket=projects/*/buckets/*}/**\x12\xaf\x01\n\x13ResumeAnywhereCache\x12\x35.google.storage.control.v2.ResumeAnywhereCacheRequest\x1a(.google.storage.control.v2.AnywhereCache\"7\xda\x41\x04name\x8a\xd3\xe4\x93\x02*\x12(\n\x04name\x12 {bucket=projects/*/buckets/*}/**\x12\xa9\x01\n\x10GetAnywhereCache\x12\x32.google.storage.control.v2.GetAnywhereCacheRequest\x1a(.google.storage.control.v2.AnywhereCache\"7\xda\x41\x04name\x8a\xd3\xe4\x93\x02*\x12(\n\x04name\x12 {bucket=projects/*/buckets/*}/**\x12\xa9\x01\n\x12ListAnywhereCaches\x12\x34.google.storage.control.v2.ListAnywhereCachesRequest\x1a\x35.google.storage.control.v2.ListAnywhereCachesResponse\"&\xda\x41\x06parent\x8a\xd3\xe4\x93\x02\x17\x12\x15\n\x06parent\x12\x0b{bucket=**}\x12\xd2\x01\n\x1cGetProjectIntelligenceConfig\x12>.google.storage.control.v2.GetProjectIntelligenceConfigRequest\x1a-.google.storage.control.v2.IntelligenceConfig\"C\xda\x41\x04name\x82\xd3\xe4\x93\x02\x36\x12\x34/v2/{name=projects/*/locations/*/intelligenceConfig}\x12\x9d\x02\n\x1fUpdateProjectIntelligenceConfig\x12\x41.google.storage.control.v2.UpdateProjectIntelligenceConfigRequest\x1a-.google.storage.control.v2.IntelligenceConfig\"\x87\x01\xda\x41\x1fintelligence_config,update_mask\x82\xd3\xe4\x93\x02_2H/v2/{intelligence_config.name=projects/*/locations/*/intelligenceConfig}:\x13intelligence_config\x12\xcf\x01\n\x1bGetFolderIntelligenceConfig\x12=.google.storage.control.v2.GetFolderIntelligenceConfigRequest\x1a-.google.storage.control.v2.IntelligenceConfig\"B\xda\x41\x04name\x82\xd3\xe4\x93\x02\x35\x12\x33/v2/{name=folders/*/locations/*/intelligenceConfig}\x12\x9a\x02\n\x1eUpdateFolderIntelligenceConfig\x12@.google.storage.control.v2.UpdateFolderIntelligenceConfigRequest\x1a-.google.storage.control.v2.IntelligenceConfig\"\x86\x01\xda\x41\x1fintelligence_config,update_mask\x82\xd3\xe4\x93\x02^2G/v2/{intelligence_config.name=folders/*/locations/*/intelligenceConfig}:\x13intelligence_config\x12\xe1\x01\n!GetOrganizationIntelligenceConfig\x12\x43.google.storage.control.v2.GetOrganizationIntelligenceConfigRequest\x1a-.google.storage.control.v2.IntelligenceConfig\"H\xda\x41\x04name\x82\xd3\xe4\x93\x02;\x12\x39/v2/{name=organizations/*/locations/*/intelligenceConfig}\x12\xac\x02\n$UpdateOrganizationIntelligenceConfig\x12\x46.google.storage.control.v2.UpdateOrganizationIntelligenceConfigRequest\x1a-.google.storage.control.v2.IntelligenceConfig\"\x8c\x01\xda\x41\x1fintelligence_config,update_mask\x82\xd3\xe4\x93\x02\x64\x32M/v2/{intelligence_config.name=organizations/*/locations/*/intelligenceConfig}:\x13intelligence_config\x12\xa3\x01\n\x0cGetIamPolicy\x12\".google.iam.v1.GetIamPolicyRequest\x1a\x15.google.iam.v1.Policy\"X\xda\x41\x08resource\x8a\xd3\xe4\x93\x02G\x12\x17\n\x08resource\x12\x0b{bucket=**}\x12,\n\x08resource\x12 {bucket=projects/*/buckets/*}/**\x12\xaa\x01\n\x0cSetIamPolicy\x12\".google.iam.v1.SetIamPolicyRequest\x1a\x15.google.iam.v1.Policy\"_\xda\x41\x0fresource,policy\x8a\xd3\xe4\x93\x02G\x12\x17\n\x08resource\x12\x0b{bucket=**}\x12,\n\x08resource\x12 {bucket=projects/*/buckets/*}/**\x12\x96\x02\n\x12TestIamPermissions\x12(.google.iam.v1.TestIamPermissionsRequest\x1a).google.iam.v1.TestIamPermissionsResponse\"\xaa\x01\xda\x41\x14resource,permissions\x8a\xd3\xe4\x93\x02\x8c\x01\x12\x17\n\x08resource\x12\x0b{bucket=**}\x12\x34\n\x08resource\x12({bucket=projects/*/buckets/*}/objects/**\x12;\n\x08resource\x12/{bucket=projects/*/buckets/*}/managedFolders/**\x1a\xa7\x02\xca\x41\x16storage.googleapis.com\xd2\x41\x8a\x02https://www.googleapis.com/auth/cloud-platform,https://www.googleapis.com/auth/cloud-platform.read-only,https://www.googleapis.com/auth/devstorage.full_control,https://www.googleapis.com/auth/devstorage.read_only,https://www.googleapis.com/auth/devstorage.read_writeB\xa6\x02\n\x1d\x63om.google.storage.control.v2B\x13StorageControlProtoP\x01Z=cloud.google.com/go/storage/control/apiv2/controlpb;controlpb\xaa\x02\x1fGoogle.Cloud.Storage.Control.V2\xca\x02\x1fGoogle\\Cloud\\Storage\\Control\\V2\xea\x02#Google::Cloud::Storage::Control::V2\xea\x41\x44\n\x1dstorage.googleapis.com/Bucket\x12#projects/{project}/buckets/{bucket}b\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'google.storage.control.v2.storage_control_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\035com.google.storage.control.v2B\023StorageControlProtoP\001Z=cloud.google.com/go/storage/control/apiv2/controlpb;controlpb\252\002\037Google.Cloud.Storage.Control.V2\312\002\037Google\\Cloud\\Storage\\Control\\V2\352\002#Google::Cloud::Storage::Control::V2\352AD\n\035storage.googleapis.com/Bucket\022#projects/{project}/buckets/{bucket}' + _globals['_PENDINGRENAMEINFO'].fields_by_name['operation']._loaded_options = None + _globals['_PENDINGRENAMEINFO'].fields_by_name['operation']._serialized_options = b'\340A\003' + _globals['_FOLDER'].fields_by_name['name']._loaded_options = None + _globals['_FOLDER'].fields_by_name['name']._serialized_options = b'\340A\010' + _globals['_FOLDER'].fields_by_name['metageneration']._loaded_options = None + _globals['_FOLDER'].fields_by_name['metageneration']._serialized_options = b'\340A\003' + _globals['_FOLDER'].fields_by_name['create_time']._loaded_options = None + _globals['_FOLDER'].fields_by_name['create_time']._serialized_options = b'\340A\003' + _globals['_FOLDER'].fields_by_name['update_time']._loaded_options = None + _globals['_FOLDER'].fields_by_name['update_time']._serialized_options = b'\340A\003' + _globals['_FOLDER'].fields_by_name['pending_rename_info']._loaded_options = None + _globals['_FOLDER'].fields_by_name['pending_rename_info']._serialized_options = b'\340A\003' + _globals['_FOLDER']._loaded_options = None + _globals['_FOLDER']._serialized_options = b'\352Ai\n\035storage.googleapis.com/Folder\0227projects/{project}/buckets/{bucket}/folders/{folder=**}*\007folders2\006folder' + _globals['_GETFOLDERREQUEST'].fields_by_name['name']._loaded_options = None + _globals['_GETFOLDERREQUEST'].fields_by_name['name']._serialized_options = b'\340A\002\372A\037\n\035storage.googleapis.com/Folder' + _globals['_GETFOLDERREQUEST'].fields_by_name['request_id']._loaded_options = None + _globals['_GETFOLDERREQUEST'].fields_by_name['request_id']._serialized_options = b'\340A\001\342\214\317\327\010\002\010\001' + _globals['_CREATEFOLDERREQUEST'].fields_by_name['parent']._loaded_options = None + _globals['_CREATEFOLDERREQUEST'].fields_by_name['parent']._serialized_options = b'\340A\002\372A\037\022\035storage.googleapis.com/Folder' + _globals['_CREATEFOLDERREQUEST'].fields_by_name['folder']._loaded_options = None + _globals['_CREATEFOLDERREQUEST'].fields_by_name['folder']._serialized_options = b'\340A\002' + _globals['_CREATEFOLDERREQUEST'].fields_by_name['folder_id']._loaded_options = None + _globals['_CREATEFOLDERREQUEST'].fields_by_name['folder_id']._serialized_options = b'\340A\002' + _globals['_CREATEFOLDERREQUEST'].fields_by_name['recursive']._loaded_options = None + _globals['_CREATEFOLDERREQUEST'].fields_by_name['recursive']._serialized_options = b'\340A\001' + _globals['_CREATEFOLDERREQUEST'].fields_by_name['request_id']._loaded_options = None + _globals['_CREATEFOLDERREQUEST'].fields_by_name['request_id']._serialized_options = b'\340A\001\342\214\317\327\010\002\010\001' + _globals['_DELETEFOLDERREQUEST'].fields_by_name['name']._loaded_options = None + _globals['_DELETEFOLDERREQUEST'].fields_by_name['name']._serialized_options = b'\340A\002\372A\037\n\035storage.googleapis.com/Folder' + _globals['_DELETEFOLDERREQUEST'].fields_by_name['request_id']._loaded_options = None + _globals['_DELETEFOLDERREQUEST'].fields_by_name['request_id']._serialized_options = b'\340A\001\342\214\317\327\010\002\010\001' + _globals['_LISTFOLDERSREQUEST'].fields_by_name['parent']._loaded_options = None + _globals['_LISTFOLDERSREQUEST'].fields_by_name['parent']._serialized_options = b'\340A\002\372A\037\022\035storage.googleapis.com/Folder' + _globals['_LISTFOLDERSREQUEST'].fields_by_name['page_size']._loaded_options = None + _globals['_LISTFOLDERSREQUEST'].fields_by_name['page_size']._serialized_options = b'\340A\001' + _globals['_LISTFOLDERSREQUEST'].fields_by_name['page_token']._loaded_options = None + _globals['_LISTFOLDERSREQUEST'].fields_by_name['page_token']._serialized_options = b'\340A\001' + _globals['_LISTFOLDERSREQUEST'].fields_by_name['prefix']._loaded_options = None + _globals['_LISTFOLDERSREQUEST'].fields_by_name['prefix']._serialized_options = b'\340A\001' + _globals['_LISTFOLDERSREQUEST'].fields_by_name['delimiter']._loaded_options = None + _globals['_LISTFOLDERSREQUEST'].fields_by_name['delimiter']._serialized_options = b'\340A\001' + _globals['_LISTFOLDERSREQUEST'].fields_by_name['lexicographic_start']._loaded_options = None + _globals['_LISTFOLDERSREQUEST'].fields_by_name['lexicographic_start']._serialized_options = b'\340A\001' + _globals['_LISTFOLDERSREQUEST'].fields_by_name['lexicographic_end']._loaded_options = None + _globals['_LISTFOLDERSREQUEST'].fields_by_name['lexicographic_end']._serialized_options = b'\340A\001' + _globals['_LISTFOLDERSREQUEST'].fields_by_name['request_id']._loaded_options = None + _globals['_LISTFOLDERSREQUEST'].fields_by_name['request_id']._serialized_options = b'\340A\001\342\214\317\327\010\002\010\001' + _globals['_RENAMEFOLDERREQUEST'].fields_by_name['name']._loaded_options = None + _globals['_RENAMEFOLDERREQUEST'].fields_by_name['name']._serialized_options = b'\340A\002\372A\037\n\035storage.googleapis.com/Folder' + _globals['_RENAMEFOLDERREQUEST'].fields_by_name['destination_folder_id']._loaded_options = None + _globals['_RENAMEFOLDERREQUEST'].fields_by_name['destination_folder_id']._serialized_options = b'\340A\002' + _globals['_RENAMEFOLDERREQUEST'].fields_by_name['request_id']._loaded_options = None + _globals['_RENAMEFOLDERREQUEST'].fields_by_name['request_id']._serialized_options = b'\340A\001\342\214\317\327\010\002\010\001' + _globals['_DELETEFOLDERRECURSIVEREQUEST'].fields_by_name['name']._loaded_options = None + _globals['_DELETEFOLDERRECURSIVEREQUEST'].fields_by_name['name']._serialized_options = b'\340A\002\372A\037\n\035storage.googleapis.com/Folder' + _globals['_DELETEFOLDERRECURSIVEREQUEST'].fields_by_name['if_metageneration_match']._loaded_options = None + _globals['_DELETEFOLDERRECURSIVEREQUEST'].fields_by_name['if_metageneration_match']._serialized_options = b'\340A\001' + _globals['_DELETEFOLDERRECURSIVEREQUEST'].fields_by_name['if_metageneration_not_match']._loaded_options = None + _globals['_DELETEFOLDERRECURSIVEREQUEST'].fields_by_name['if_metageneration_not_match']._serialized_options = b'\340A\001' + _globals['_DELETEFOLDERRECURSIVEREQUEST'].fields_by_name['request_id']._loaded_options = None + _globals['_DELETEFOLDERRECURSIVEREQUEST'].fields_by_name['request_id']._serialized_options = b'\340A\001\342\214\317\327\010\002\010\001' + _globals['_COMMONLONGRUNNINGOPERATIONMETADATA'].fields_by_name['create_time']._loaded_options = None + _globals['_COMMONLONGRUNNINGOPERATIONMETADATA'].fields_by_name['create_time']._serialized_options = b'\340A\003' + _globals['_COMMONLONGRUNNINGOPERATIONMETADATA'].fields_by_name['end_time']._loaded_options = None + _globals['_COMMONLONGRUNNINGOPERATIONMETADATA'].fields_by_name['end_time']._serialized_options = b'\340A\003' + _globals['_COMMONLONGRUNNINGOPERATIONMETADATA'].fields_by_name['update_time']._loaded_options = None + _globals['_COMMONLONGRUNNINGOPERATIONMETADATA'].fields_by_name['update_time']._serialized_options = b'\340A\003' + _globals['_COMMONLONGRUNNINGOPERATIONMETADATA'].fields_by_name['type']._loaded_options = None + _globals['_COMMONLONGRUNNINGOPERATIONMETADATA'].fields_by_name['type']._serialized_options = b'\340A\003' + _globals['_COMMONLONGRUNNINGOPERATIONMETADATA'].fields_by_name['requested_cancellation']._loaded_options = None + _globals['_COMMONLONGRUNNINGOPERATIONMETADATA'].fields_by_name['requested_cancellation']._serialized_options = b'\340A\003' + _globals['_COMMONLONGRUNNINGOPERATIONMETADATA'].fields_by_name['progress_percent']._loaded_options = None + _globals['_COMMONLONGRUNNINGOPERATIONMETADATA'].fields_by_name['progress_percent']._serialized_options = b'\340A\003' + _globals['_STORAGELAYOUT'].fields_by_name['name']._loaded_options = None + _globals['_STORAGELAYOUT'].fields_by_name['name']._serialized_options = b'\340A\003' + _globals['_STORAGELAYOUT'].fields_by_name['location']._loaded_options = None + _globals['_STORAGELAYOUT'].fields_by_name['location']._serialized_options = b'\340A\003' + _globals['_STORAGELAYOUT'].fields_by_name['location_type']._loaded_options = None + _globals['_STORAGELAYOUT'].fields_by_name['location_type']._serialized_options = b'\340A\003' + _globals['_STORAGELAYOUT'].fields_by_name['custom_placement_config']._loaded_options = None + _globals['_STORAGELAYOUT'].fields_by_name['custom_placement_config']._serialized_options = b'\340A\003' + _globals['_STORAGELAYOUT'].fields_by_name['hierarchical_namespace']._loaded_options = None + _globals['_STORAGELAYOUT'].fields_by_name['hierarchical_namespace']._serialized_options = b'\340A\003' + _globals['_STORAGELAYOUT']._loaded_options = None + _globals['_STORAGELAYOUT']._serialized_options = b'\352Ax\n$storage.googleapis.com/StorageLayout\0221projects/{project}/buckets/{bucket}/storageLayout*\016storageLayouts2\rstorageLayout' + _globals['_GETSTORAGELAYOUTREQUEST'].fields_by_name['name']._loaded_options = None + _globals['_GETSTORAGELAYOUTREQUEST'].fields_by_name['name']._serialized_options = b'\340A\002\372A&\n$storage.googleapis.com/StorageLayout' + _globals['_GETSTORAGELAYOUTREQUEST'].fields_by_name['request_id']._loaded_options = None + _globals['_GETSTORAGELAYOUTREQUEST'].fields_by_name['request_id']._serialized_options = b'\340A\001\342\214\317\327\010\002\010\001' + _globals['_MANAGEDFOLDER'].fields_by_name['name']._loaded_options = None + _globals['_MANAGEDFOLDER'].fields_by_name['name']._serialized_options = b'\340A\010' + _globals['_MANAGEDFOLDER'].fields_by_name['metageneration']._loaded_options = None + _globals['_MANAGEDFOLDER'].fields_by_name['metageneration']._serialized_options = b'\340A\003' + _globals['_MANAGEDFOLDER'].fields_by_name['create_time']._loaded_options = None + _globals['_MANAGEDFOLDER'].fields_by_name['create_time']._serialized_options = b'\340A\003' + _globals['_MANAGEDFOLDER'].fields_by_name['update_time']._loaded_options = None + _globals['_MANAGEDFOLDER'].fields_by_name['update_time']._serialized_options = b'\340A\003' + _globals['_MANAGEDFOLDER']._loaded_options = None + _globals['_MANAGEDFOLDER']._serialized_options = b'\352A\215\001\n$storage.googleapis.com/ManagedFolder\022Fprojects/{project}/buckets/{bucket}/managedFolders/{managed_folder=**}*\016managedFolders2\rmanagedFolder' + _globals['_GETMANAGEDFOLDERREQUEST'].fields_by_name['name']._loaded_options = None + _globals['_GETMANAGEDFOLDERREQUEST'].fields_by_name['name']._serialized_options = b'\340A\002\372A&\n$storage.googleapis.com/ManagedFolder' + _globals['_GETMANAGEDFOLDERREQUEST'].fields_by_name['request_id']._loaded_options = None + _globals['_GETMANAGEDFOLDERREQUEST'].fields_by_name['request_id']._serialized_options = b'\340A\001\342\214\317\327\010\002\010\001' + _globals['_CREATEMANAGEDFOLDERREQUEST'].fields_by_name['parent']._loaded_options = None + _globals['_CREATEMANAGEDFOLDERREQUEST'].fields_by_name['parent']._serialized_options = b'\340A\002\372A&\022$storage.googleapis.com/ManagedFolder' + _globals['_CREATEMANAGEDFOLDERREQUEST'].fields_by_name['managed_folder']._loaded_options = None + _globals['_CREATEMANAGEDFOLDERREQUEST'].fields_by_name['managed_folder']._serialized_options = b'\340A\002' + _globals['_CREATEMANAGEDFOLDERREQUEST'].fields_by_name['managed_folder_id']._loaded_options = None + _globals['_CREATEMANAGEDFOLDERREQUEST'].fields_by_name['managed_folder_id']._serialized_options = b'\340A\002' + _globals['_CREATEMANAGEDFOLDERREQUEST'].fields_by_name['request_id']._loaded_options = None + _globals['_CREATEMANAGEDFOLDERREQUEST'].fields_by_name['request_id']._serialized_options = b'\340A\001\342\214\317\327\010\002\010\001' + _globals['_DELETEMANAGEDFOLDERREQUEST'].fields_by_name['name']._loaded_options = None + _globals['_DELETEMANAGEDFOLDERREQUEST'].fields_by_name['name']._serialized_options = b'\340A\002\372A&\n$storage.googleapis.com/ManagedFolder' + _globals['_DELETEMANAGEDFOLDERREQUEST'].fields_by_name['request_id']._loaded_options = None + _globals['_DELETEMANAGEDFOLDERREQUEST'].fields_by_name['request_id']._serialized_options = b'\340A\001\342\214\317\327\010\002\010\001' + _globals['_LISTMANAGEDFOLDERSREQUEST'].fields_by_name['parent']._loaded_options = None + _globals['_LISTMANAGEDFOLDERSREQUEST'].fields_by_name['parent']._serialized_options = b'\340A\002\372A&\022$storage.googleapis.com/ManagedFolder' + _globals['_LISTMANAGEDFOLDERSREQUEST'].fields_by_name['page_size']._loaded_options = None + _globals['_LISTMANAGEDFOLDERSREQUEST'].fields_by_name['page_size']._serialized_options = b'\340A\001' + _globals['_LISTMANAGEDFOLDERSREQUEST'].fields_by_name['page_token']._loaded_options = None + _globals['_LISTMANAGEDFOLDERSREQUEST'].fields_by_name['page_token']._serialized_options = b'\340A\001' + _globals['_LISTMANAGEDFOLDERSREQUEST'].fields_by_name['prefix']._loaded_options = None + _globals['_LISTMANAGEDFOLDERSREQUEST'].fields_by_name['prefix']._serialized_options = b'\340A\001' + _globals['_LISTMANAGEDFOLDERSREQUEST'].fields_by_name['request_id']._loaded_options = None + _globals['_LISTMANAGEDFOLDERSREQUEST'].fields_by_name['request_id']._serialized_options = b'\340A\001\342\214\317\327\010\002\010\001' + _globals['_ANYWHERECACHE'].fields_by_name['name']._loaded_options = None + _globals['_ANYWHERECACHE'].fields_by_name['name']._serialized_options = b'\340A\005' + _globals['_ANYWHERECACHE'].fields_by_name['zone']._loaded_options = None + _globals['_ANYWHERECACHE'].fields_by_name['zone']._serialized_options = b'\340A\005' + _globals['_ANYWHERECACHE'].fields_by_name['state']._loaded_options = None + _globals['_ANYWHERECACHE'].fields_by_name['state']._serialized_options = b'\340A\003' + _globals['_ANYWHERECACHE'].fields_by_name['create_time']._loaded_options = None + _globals['_ANYWHERECACHE'].fields_by_name['create_time']._serialized_options = b'\340A\003' + _globals['_ANYWHERECACHE'].fields_by_name['update_time']._loaded_options = None + _globals['_ANYWHERECACHE'].fields_by_name['update_time']._serialized_options = b'\340A\003' + _globals['_ANYWHERECACHE'].fields_by_name['pending_update']._loaded_options = None + _globals['_ANYWHERECACHE'].fields_by_name['pending_update']._serialized_options = b'\340A\003' + _globals['_ANYWHERECACHE']._loaded_options = None + _globals['_ANYWHERECACHE']._serialized_options = b'\352A\212\001\n$storage.googleapis.com/AnywhereCache\022Cprojects/{project}/buckets/{bucket}/anywhereCaches/{anywhere_cache}*\016anywhereCaches2\ranywhereCache' + _globals['_CREATEANYWHERECACHEREQUEST'].fields_by_name['parent']._loaded_options = None + _globals['_CREATEANYWHERECACHEREQUEST'].fields_by_name['parent']._serialized_options = b'\340A\002\372A&\022$storage.googleapis.com/AnywhereCache' + _globals['_CREATEANYWHERECACHEREQUEST'].fields_by_name['anywhere_cache']._loaded_options = None + _globals['_CREATEANYWHERECACHEREQUEST'].fields_by_name['anywhere_cache']._serialized_options = b'\340A\002' + _globals['_CREATEANYWHERECACHEREQUEST'].fields_by_name['request_id']._loaded_options = None + _globals['_CREATEANYWHERECACHEREQUEST'].fields_by_name['request_id']._serialized_options = b'\340A\001\342\214\317\327\010\002\010\001' + _globals['_UPDATEANYWHERECACHEREQUEST'].fields_by_name['anywhere_cache']._loaded_options = None + _globals['_UPDATEANYWHERECACHEREQUEST'].fields_by_name['anywhere_cache']._serialized_options = b'\340A\002' + _globals['_UPDATEANYWHERECACHEREQUEST'].fields_by_name['update_mask']._loaded_options = None + _globals['_UPDATEANYWHERECACHEREQUEST'].fields_by_name['update_mask']._serialized_options = b'\340A\002' + _globals['_UPDATEANYWHERECACHEREQUEST'].fields_by_name['request_id']._loaded_options = None + _globals['_UPDATEANYWHERECACHEREQUEST'].fields_by_name['request_id']._serialized_options = b'\340A\001\342\214\317\327\010\002\010\001' + _globals['_DISABLEANYWHERECACHEREQUEST'].fields_by_name['name']._loaded_options = None + _globals['_DISABLEANYWHERECACHEREQUEST'].fields_by_name['name']._serialized_options = b'\340A\002\372A&\n$storage.googleapis.com/AnywhereCache' + _globals['_DISABLEANYWHERECACHEREQUEST'].fields_by_name['request_id']._loaded_options = None + _globals['_DISABLEANYWHERECACHEREQUEST'].fields_by_name['request_id']._serialized_options = b'\340A\001\342\214\317\327\010\002\010\001' + _globals['_PAUSEANYWHERECACHEREQUEST'].fields_by_name['name']._loaded_options = None + _globals['_PAUSEANYWHERECACHEREQUEST'].fields_by_name['name']._serialized_options = b'\340A\002\372A&\n$storage.googleapis.com/AnywhereCache' + _globals['_PAUSEANYWHERECACHEREQUEST'].fields_by_name['request_id']._loaded_options = None + _globals['_PAUSEANYWHERECACHEREQUEST'].fields_by_name['request_id']._serialized_options = b'\340A\001\342\214\317\327\010\002\010\001' + _globals['_RESUMEANYWHERECACHEREQUEST'].fields_by_name['name']._loaded_options = None + _globals['_RESUMEANYWHERECACHEREQUEST'].fields_by_name['name']._serialized_options = b'\340A\002\372A&\n$storage.googleapis.com/AnywhereCache' + _globals['_RESUMEANYWHERECACHEREQUEST'].fields_by_name['request_id']._loaded_options = None + _globals['_RESUMEANYWHERECACHEREQUEST'].fields_by_name['request_id']._serialized_options = b'\340A\001\342\214\317\327\010\002\010\001' + _globals['_GETANYWHERECACHEREQUEST'].fields_by_name['name']._loaded_options = None + _globals['_GETANYWHERECACHEREQUEST'].fields_by_name['name']._serialized_options = b'\340A\002\372A&\n$storage.googleapis.com/AnywhereCache' + _globals['_GETANYWHERECACHEREQUEST'].fields_by_name['request_id']._loaded_options = None + _globals['_GETANYWHERECACHEREQUEST'].fields_by_name['request_id']._serialized_options = b'\340A\001\342\214\317\327\010\002\010\001' + _globals['_LISTANYWHERECACHESREQUEST'].fields_by_name['parent']._loaded_options = None + _globals['_LISTANYWHERECACHESREQUEST'].fields_by_name['parent']._serialized_options = b'\340A\002\372A&\022$storage.googleapis.com/AnywhereCache' + _globals['_LISTANYWHERECACHESREQUEST'].fields_by_name['request_id']._loaded_options = None + _globals['_LISTANYWHERECACHESREQUEST'].fields_by_name['request_id']._serialized_options = b'\340A\001\342\214\317\327\010\002\010\001' + _globals['_INTELLIGENCECONFIG_FILTER_CLOUDSTORAGELOCATIONS'].fields_by_name['locations']._loaded_options = None + _globals['_INTELLIGENCECONFIG_FILTER_CLOUDSTORAGELOCATIONS'].fields_by_name['locations']._serialized_options = b'\340A\001' + _globals['_INTELLIGENCECONFIG_FILTER_CLOUDSTORAGEBUCKETS'].fields_by_name['bucket_id_regexes']._loaded_options = None + _globals['_INTELLIGENCECONFIG_FILTER_CLOUDSTORAGEBUCKETS'].fields_by_name['bucket_id_regexes']._serialized_options = b'\340A\001' + _globals['_INTELLIGENCECONFIG_EFFECTIVEINTELLIGENCECONFIG'].fields_by_name['effective_edition']._loaded_options = None + _globals['_INTELLIGENCECONFIG_EFFECTIVEINTELLIGENCECONFIG'].fields_by_name['effective_edition']._serialized_options = b'\340A\003' + _globals['_INTELLIGENCECONFIG_EFFECTIVEINTELLIGENCECONFIG'].fields_by_name['intelligence_config']._loaded_options = None + _globals['_INTELLIGENCECONFIG_EFFECTIVEINTELLIGENCECONFIG'].fields_by_name['intelligence_config']._serialized_options = b'\340A\003' + _globals['_INTELLIGENCECONFIG_TRIALCONFIG'].fields_by_name['expire_time']._loaded_options = None + _globals['_INTELLIGENCECONFIG_TRIALCONFIG'].fields_by_name['expire_time']._serialized_options = b'\340A\003' + _globals['_INTELLIGENCECONFIG'].fields_by_name['name']._loaded_options = None + _globals['_INTELLIGENCECONFIG'].fields_by_name['name']._serialized_options = b'\340A\010' + _globals['_INTELLIGENCECONFIG'].fields_by_name['edition_config']._loaded_options = None + _globals['_INTELLIGENCECONFIG'].fields_by_name['edition_config']._serialized_options = b'\340A\001' + _globals['_INTELLIGENCECONFIG'].fields_by_name['update_time']._loaded_options = None + _globals['_INTELLIGENCECONFIG'].fields_by_name['update_time']._serialized_options = b'\340A\003' + _globals['_INTELLIGENCECONFIG'].fields_by_name['filter']._loaded_options = None + _globals['_INTELLIGENCECONFIG'].fields_by_name['filter']._serialized_options = b'\340A\001' + _globals['_INTELLIGENCECONFIG'].fields_by_name['effective_intelligence_config']._loaded_options = None + _globals['_INTELLIGENCECONFIG'].fields_by_name['effective_intelligence_config']._serialized_options = b'\340A\003' + _globals['_INTELLIGENCECONFIG']._loaded_options = None + _globals['_INTELLIGENCECONFIG']._serialized_options = b'\352A\207\002\n)storage.googleapis.com/IntelligenceConfig\0228folders/{folder}/locations/{location}/intelligenceConfig\022;organizations/{org}/locations/{location}/intelligenceConfig\022:projects/{project}/locations/{location}/intelligenceConfig*\023intelligenceConfigs2\022intelligenceConfig' + _globals['_UPDATEORGANIZATIONINTELLIGENCECONFIGREQUEST'].fields_by_name['intelligence_config']._loaded_options = None + _globals['_UPDATEORGANIZATIONINTELLIGENCECONFIGREQUEST'].fields_by_name['intelligence_config']._serialized_options = b'\340A\002' + _globals['_UPDATEORGANIZATIONINTELLIGENCECONFIGREQUEST'].fields_by_name['update_mask']._loaded_options = None + _globals['_UPDATEORGANIZATIONINTELLIGENCECONFIGREQUEST'].fields_by_name['update_mask']._serialized_options = b'\340A\002' + _globals['_UPDATEORGANIZATIONINTELLIGENCECONFIGREQUEST'].fields_by_name['request_id']._loaded_options = None + _globals['_UPDATEORGANIZATIONINTELLIGENCECONFIGREQUEST'].fields_by_name['request_id']._serialized_options = b'\340A\001\342\214\317\327\010\002\010\001' + _globals['_UPDATEFOLDERINTELLIGENCECONFIGREQUEST'].fields_by_name['intelligence_config']._loaded_options = None + _globals['_UPDATEFOLDERINTELLIGENCECONFIGREQUEST'].fields_by_name['intelligence_config']._serialized_options = b'\340A\002' + _globals['_UPDATEFOLDERINTELLIGENCECONFIGREQUEST'].fields_by_name['update_mask']._loaded_options = None + _globals['_UPDATEFOLDERINTELLIGENCECONFIGREQUEST'].fields_by_name['update_mask']._serialized_options = b'\340A\002' + _globals['_UPDATEFOLDERINTELLIGENCECONFIGREQUEST'].fields_by_name['request_id']._loaded_options = None + _globals['_UPDATEFOLDERINTELLIGENCECONFIGREQUEST'].fields_by_name['request_id']._serialized_options = b'\340A\001\342\214\317\327\010\002\010\001' + _globals['_UPDATEPROJECTINTELLIGENCECONFIGREQUEST'].fields_by_name['intelligence_config']._loaded_options = None + _globals['_UPDATEPROJECTINTELLIGENCECONFIGREQUEST'].fields_by_name['intelligence_config']._serialized_options = b'\340A\002' + _globals['_UPDATEPROJECTINTELLIGENCECONFIGREQUEST'].fields_by_name['update_mask']._loaded_options = None + _globals['_UPDATEPROJECTINTELLIGENCECONFIGREQUEST'].fields_by_name['update_mask']._serialized_options = b'\340A\002' + _globals['_UPDATEPROJECTINTELLIGENCECONFIGREQUEST'].fields_by_name['request_id']._loaded_options = None + _globals['_UPDATEPROJECTINTELLIGENCECONFIGREQUEST'].fields_by_name['request_id']._serialized_options = b'\340A\001\342\214\317\327\010\002\010\001' + _globals['_GETORGANIZATIONINTELLIGENCECONFIGREQUEST'].fields_by_name['name']._loaded_options = None + _globals['_GETORGANIZATIONINTELLIGENCECONFIGREQUEST'].fields_by_name['name']._serialized_options = b'\340A\002\372A+\n)storage.googleapis.com/IntelligenceConfig' + _globals['_GETFOLDERINTELLIGENCECONFIGREQUEST'].fields_by_name['name']._loaded_options = None + _globals['_GETFOLDERINTELLIGENCECONFIGREQUEST'].fields_by_name['name']._serialized_options = b'\340A\002\372A+\n)storage.googleapis.com/IntelligenceConfig' + _globals['_GETPROJECTINTELLIGENCECONFIGREQUEST'].fields_by_name['name']._loaded_options = None + _globals['_GETPROJECTINTELLIGENCECONFIGREQUEST'].fields_by_name['name']._serialized_options = b'\340A\002\372A+\n)storage.googleapis.com/IntelligenceConfig' + _globals['_STORAGECONTROL']._loaded_options = None + _globals['_STORAGECONTROL']._serialized_options = b'\312A\026storage.googleapis.com\322A\212\002https://www.googleapis.com/auth/cloud-platform,https://www.googleapis.com/auth/cloud-platform.read-only,https://www.googleapis.com/auth/devstorage.full_control,https://www.googleapis.com/auth/devstorage.read_only,https://www.googleapis.com/auth/devstorage.read_write' + _globals['_STORAGECONTROL'].methods_by_name['CreateFolder']._loaded_options = None + _globals['_STORAGECONTROL'].methods_by_name['CreateFolder']._serialized_options = b'\332A\027parent,folder,folder_id\212\323\344\223\002\027\022\025\n\006parent\022\013{bucket=**}' + _globals['_STORAGECONTROL'].methods_by_name['DeleteFolder']._loaded_options = None + _globals['_STORAGECONTROL'].methods_by_name['DeleteFolder']._serialized_options = b'\332A\004name\212\323\344\223\002*\022(\n\004name\022 {bucket=projects/*/buckets/*}/**' + _globals['_STORAGECONTROL'].methods_by_name['GetFolder']._loaded_options = None + _globals['_STORAGECONTROL'].methods_by_name['GetFolder']._serialized_options = b'\332A\004name\212\323\344\223\002*\022(\n\004name\022 {bucket=projects/*/buckets/*}/**' + _globals['_STORAGECONTROL'].methods_by_name['ListFolders']._loaded_options = None + _globals['_STORAGECONTROL'].methods_by_name['ListFolders']._serialized_options = b'\332A\006parent\212\323\344\223\002\027\022\025\n\006parent\022\013{bucket=**}' + _globals['_STORAGECONTROL'].methods_by_name['RenameFolder']._loaded_options = None + _globals['_STORAGECONTROL'].methods_by_name['RenameFolder']._serialized_options = b'\312A\036\n\006Folder\022\024RenameFolderMetadata\332A\032name,destination_folder_id\212\323\344\223\002*\022(\n\004name\022 {bucket=projects/*/buckets/*}/**' + _globals['_STORAGECONTROL'].methods_by_name['DeleteFolderRecursive']._loaded_options = None + _globals['_STORAGECONTROL'].methods_by_name['DeleteFolderRecursive']._serialized_options = b'\312A6\n\025google.protobuf.Empty\022\035DeleteFolderRecursiveMetadata\332A\004name\212\323\344\223\002*\022(\n\004name\022 {bucket=projects/*/buckets/*}/**' + _globals['_STORAGECONTROL'].methods_by_name['GetStorageLayout']._loaded_options = None + _globals['_STORAGECONTROL'].methods_by_name['GetStorageLayout']._serialized_options = b'\332A\004name\212\323\344\223\002*\022(\n\004name\022 {bucket=projects/*/buckets/*}/**' + _globals['_STORAGECONTROL'].methods_by_name['CreateManagedFolder']._loaded_options = None + _globals['_STORAGECONTROL'].methods_by_name['CreateManagedFolder']._serialized_options = b'\332A\'parent,managed_folder,managed_folder_id\212\323\344\223\002\027\022\025\n\006parent\022\013{bucket=**}' + _globals['_STORAGECONTROL'].methods_by_name['DeleteManagedFolder']._loaded_options = None + _globals['_STORAGECONTROL'].methods_by_name['DeleteManagedFolder']._serialized_options = b'\332A\004name\212\323\344\223\002*\022(\n\004name\022 {bucket=projects/*/buckets/*}/**' + _globals['_STORAGECONTROL'].methods_by_name['GetManagedFolder']._loaded_options = None + _globals['_STORAGECONTROL'].methods_by_name['GetManagedFolder']._serialized_options = b'\332A\004name\212\323\344\223\002*\022(\n\004name\022 {bucket=projects/*/buckets/*}/**' + _globals['_STORAGECONTROL'].methods_by_name['ListManagedFolders']._loaded_options = None + _globals['_STORAGECONTROL'].methods_by_name['ListManagedFolders']._serialized_options = b'\332A\006parent\212\323\344\223\002\027\022\025\n\006parent\022\013{bucket=**}' + _globals['_STORAGECONTROL'].methods_by_name['CreateAnywhereCache']._loaded_options = None + _globals['_STORAGECONTROL'].methods_by_name['CreateAnywhereCache']._serialized_options = b'\312A,\n\rAnywhereCache\022\033CreateAnywhereCacheMetadata\332A\025parent,anywhere_cache\212\323\344\223\002\027\022\025\n\006parent\022\013{bucket=**}' + _globals['_STORAGECONTROL'].methods_by_name['UpdateAnywhereCache']._loaded_options = None + _globals['_STORAGECONTROL'].methods_by_name['UpdateAnywhereCache']._serialized_options = b'\312A,\n\rAnywhereCache\022\033UpdateAnywhereCacheMetadata\332A\032anywhere_cache,update_mask\212\323\344\223\0029\0227\n\023anywhere_cache.name\022 {bucket=projects/*/buckets/*}/**' + _globals['_STORAGECONTROL'].methods_by_name['DisableAnywhereCache']._loaded_options = None + _globals['_STORAGECONTROL'].methods_by_name['DisableAnywhereCache']._serialized_options = b'\332A\004name\212\323\344\223\002*\022(\n\004name\022 {bucket=projects/*/buckets/*}/**' + _globals['_STORAGECONTROL'].methods_by_name['PauseAnywhereCache']._loaded_options = None + _globals['_STORAGECONTROL'].methods_by_name['PauseAnywhereCache']._serialized_options = b'\332A\004name\212\323\344\223\002*\022(\n\004name\022 {bucket=projects/*/buckets/*}/**' + _globals['_STORAGECONTROL'].methods_by_name['ResumeAnywhereCache']._loaded_options = None + _globals['_STORAGECONTROL'].methods_by_name['ResumeAnywhereCache']._serialized_options = b'\332A\004name\212\323\344\223\002*\022(\n\004name\022 {bucket=projects/*/buckets/*}/**' + _globals['_STORAGECONTROL'].methods_by_name['GetAnywhereCache']._loaded_options = None + _globals['_STORAGECONTROL'].methods_by_name['GetAnywhereCache']._serialized_options = b'\332A\004name\212\323\344\223\002*\022(\n\004name\022 {bucket=projects/*/buckets/*}/**' + _globals['_STORAGECONTROL'].methods_by_name['ListAnywhereCaches']._loaded_options = None + _globals['_STORAGECONTROL'].methods_by_name['ListAnywhereCaches']._serialized_options = b'\332A\006parent\212\323\344\223\002\027\022\025\n\006parent\022\013{bucket=**}' + _globals['_STORAGECONTROL'].methods_by_name['GetProjectIntelligenceConfig']._loaded_options = None + _globals['_STORAGECONTROL'].methods_by_name['GetProjectIntelligenceConfig']._serialized_options = b'\332A\004name\202\323\344\223\0026\0224/v2/{name=projects/*/locations/*/intelligenceConfig}' + _globals['_STORAGECONTROL'].methods_by_name['UpdateProjectIntelligenceConfig']._loaded_options = None + _globals['_STORAGECONTROL'].methods_by_name['UpdateProjectIntelligenceConfig']._serialized_options = b'\332A\037intelligence_config,update_mask\202\323\344\223\002_2H/v2/{intelligence_config.name=projects/*/locations/*/intelligenceConfig}:\023intelligence_config' + _globals['_STORAGECONTROL'].methods_by_name['GetFolderIntelligenceConfig']._loaded_options = None + _globals['_STORAGECONTROL'].methods_by_name['GetFolderIntelligenceConfig']._serialized_options = b'\332A\004name\202\323\344\223\0025\0223/v2/{name=folders/*/locations/*/intelligenceConfig}' + _globals['_STORAGECONTROL'].methods_by_name['UpdateFolderIntelligenceConfig']._loaded_options = None + _globals['_STORAGECONTROL'].methods_by_name['UpdateFolderIntelligenceConfig']._serialized_options = b'\332A\037intelligence_config,update_mask\202\323\344\223\002^2G/v2/{intelligence_config.name=folders/*/locations/*/intelligenceConfig}:\023intelligence_config' + _globals['_STORAGECONTROL'].methods_by_name['GetOrganizationIntelligenceConfig']._loaded_options = None + _globals['_STORAGECONTROL'].methods_by_name['GetOrganizationIntelligenceConfig']._serialized_options = b'\332A\004name\202\323\344\223\002;\0229/v2/{name=organizations/*/locations/*/intelligenceConfig}' + _globals['_STORAGECONTROL'].methods_by_name['UpdateOrganizationIntelligenceConfig']._loaded_options = None + _globals['_STORAGECONTROL'].methods_by_name['UpdateOrganizationIntelligenceConfig']._serialized_options = b'\332A\037intelligence_config,update_mask\202\323\344\223\002d2M/v2/{intelligence_config.name=organizations/*/locations/*/intelligenceConfig}:\023intelligence_config' + _globals['_STORAGECONTROL'].methods_by_name['GetIamPolicy']._loaded_options = None + _globals['_STORAGECONTROL'].methods_by_name['GetIamPolicy']._serialized_options = b'\332A\010resource\212\323\344\223\002G\022\027\n\010resource\022\013{bucket=**}\022,\n\010resource\022 {bucket=projects/*/buckets/*}/**' + _globals['_STORAGECONTROL'].methods_by_name['SetIamPolicy']._loaded_options = None + _globals['_STORAGECONTROL'].methods_by_name['SetIamPolicy']._serialized_options = b'\332A\017resource,policy\212\323\344\223\002G\022\027\n\010resource\022\013{bucket=**}\022,\n\010resource\022 {bucket=projects/*/buckets/*}/**' + _globals['_STORAGECONTROL'].methods_by_name['TestIamPermissions']._loaded_options = None + _globals['_STORAGECONTROL'].methods_by_name['TestIamPermissions']._serialized_options = b'\332A\024resource,permissions\212\323\344\223\002\214\001\022\027\n\010resource\022\013{bucket=**}\0224\n\010resource\022({bucket=projects/*/buckets/*}/objects/**\022;\n\010resource\022/{bucket=projects/*/buckets/*}/managedFolders/**' + _globals['_PENDINGRENAMEINFO']._serialized_start=473 + _globals['_PENDINGRENAMEINFO']._serialized_end=516 + _globals['_FOLDER']._serialized_start=519 + _globals['_FOLDER']._serialized_end=873 + _globals['_GETFOLDERREQUEST']._serialized_start=876 + _globals['_GETFOLDERREQUEST']._serialized_end=1120 + _globals['_CREATEFOLDERREQUEST']._serialized_start=1123 + _globals['_CREATEFOLDERREQUEST']._serialized_end=1336 + _globals['_DELETEFOLDERREQUEST']._serialized_start=1339 + _globals['_DELETEFOLDERREQUEST']._serialized_end=1586 + _globals['_LISTFOLDERSREQUEST']._serialized_start=1589 + _globals['_LISTFOLDERSREQUEST']._serialized_end=1857 + _globals['_LISTFOLDERSRESPONSE']._serialized_start=1859 + _globals['_LISTFOLDERSRESPONSE']._serialized_end=1957 + _globals['_RENAMEFOLDERREQUEST']._serialized_start=1960 + _globals['_RENAMEFOLDERREQUEST']._serialized_end=2243 + _globals['_DELETEFOLDERRECURSIVEREQUEST']._serialized_start=2246 + _globals['_DELETEFOLDERRECURSIVEREQUEST']._serialized_end=2512 + _globals['_COMMONLONGRUNNINGOPERATIONMETADATA']._serialized_start=2515 + _globals['_COMMONLONGRUNNINGOPERATIONMETADATA']._serialized_end=2797 + _globals['_RENAMEFOLDERMETADATA']._serialized_start=2800 + _globals['_RENAMEFOLDERMETADATA']._serialized_end=2967 + _globals['_DELETEFOLDERRECURSIVEMETADATA']._serialized_start=2970 + _globals['_DELETEFOLDERRECURSIVEMETADATA']._serialized_end=3108 + _globals['_STORAGELAYOUT']._serialized_start=3111 + _globals['_STORAGELAYOUT']._serialized_end=3615 + _globals['_STORAGELAYOUT_CUSTOMPLACEMENTCONFIG']._serialized_start=3401 + _globals['_STORAGELAYOUT_CUSTOMPLACEMENTCONFIG']._serialized_end=3448 + _globals['_STORAGELAYOUT_HIERARCHICALNAMESPACE']._serialized_start=3450 + _globals['_STORAGELAYOUT_HIERARCHICALNAMESPACE']._serialized_end=3490 + _globals['_GETSTORAGELAYOUTREQUEST']._serialized_start=3618 + _globals['_GETSTORAGELAYOUTREQUEST']._serialized_end=3752 + _globals['_MANAGEDFOLDER']._serialized_start=3755 + _globals['_MANAGEDFOLDER']._serialized_end=4074 + _globals['_GETMANAGEDFOLDERREQUEST']._serialized_start=4077 + _globals['_GETMANAGEDFOLDERREQUEST']._serialized_end=4335 + _globals['_CREATEMANAGEDFOLDERREQUEST']._serialized_start=4338 + _globals['_CREATEMANAGEDFOLDERREQUEST']._serialized_end=4564 + _globals['_DELETEMANAGEDFOLDERREQUEST']._serialized_start=4567 + _globals['_DELETEMANAGEDFOLDERREQUEST']._serialized_end=4853 + _globals['_LISTMANAGEDFOLDERSREQUEST']._serialized_start=4856 + _globals['_LISTMANAGEDFOLDERSREQUEST']._serialized_end=5048 + _globals['_LISTMANAGEDFOLDERSRESPONSE']._serialized_start=5050 + _globals['_LISTMANAGEDFOLDERSRESPONSE']._serialized_end=5170 + _globals['_CREATEANYWHERECACHEMETADATA']._serialized_start=5173 + _globals['_CREATEANYWHERECACHEMETADATA']._serialized_end=5477 + _globals['_UPDATEANYWHERECACHEMETADATA']._serialized_start=5480 + _globals['_UPDATEANYWHERECACHEMETADATA']._serialized_end=5784 + _globals['_ANYWHERECACHE']._serialized_start=5787 + _globals['_ANYWHERECACHE']._serialized_end=6208 + _globals['_CREATEANYWHERECACHEREQUEST']._serialized_start=6211 + _globals['_CREATEANYWHERECACHEREQUEST']._serialized_end=6405 + _globals['_UPDATEANYWHERECACHEREQUEST']._serialized_start=6408 + _globals['_UPDATEANYWHERECACHEREQUEST']._serialized_end=6594 + _globals['_DISABLEANYWHERECACHEREQUEST']._serialized_start=6596 + _globals['_DISABLEANYWHERECACHEREQUEST']._serialized_end=6718 + _globals['_PAUSEANYWHERECACHEREQUEST']._serialized_start=6720 + _globals['_PAUSEANYWHERECACHEREQUEST']._serialized_end=6840 + _globals['_RESUMEANYWHERECACHEREQUEST']._serialized_start=6842 + _globals['_RESUMEANYWHERECACHEREQUEST']._serialized_end=6963 + _globals['_GETANYWHERECACHEREQUEST']._serialized_start=6965 + _globals['_GETANYWHERECACHEREQUEST']._serialized_end=7083 + _globals['_LISTANYWHERECACHESREQUEST']._serialized_start=7086 + _globals['_LISTANYWHERECACHESREQUEST']._serialized_end=7247 + _globals['_LISTANYWHERECACHESRESPONSE']._serialized_start=7249 + _globals['_LISTANYWHERECACHESRESPONSE']._serialized_end=7369 + _globals['_INTELLIGENCECONFIG']._serialized_start=7372 + _globals['_INTELLIGENCECONFIG']._serialized_end=9178 + _globals['_INTELLIGENCECONFIG_FILTER']._serialized_start=7833 + _globals['_INTELLIGENCECONFIG_FILTER']._serialized_end=8469 + _globals['_INTELLIGENCECONFIG_FILTER_CLOUDSTORAGELOCATIONS']._serialized_start=8315 + _globals['_INTELLIGENCECONFIG_FILTER_CLOUDSTORAGELOCATIONS']._serialized_end=8362 + _globals['_INTELLIGENCECONFIG_FILTER_CLOUDSTORAGEBUCKETS']._serialized_start=8364 + _globals['_INTELLIGENCECONFIG_FILTER_CLOUDSTORAGEBUCKETS']._serialized_end=8417 + _globals['_INTELLIGENCECONFIG_EFFECTIVEINTELLIGENCECONFIG']._serialized_start=8472 + _globals['_INTELLIGENCECONFIG_EFFECTIVEINTELLIGENCECONFIG']._serialized_end=8738 + _globals['_INTELLIGENCECONFIG_EFFECTIVEINTELLIGENCECONFIG_EFFECTIVEEDITION']._serialized_start=8661 + _globals['_INTELLIGENCECONFIG_EFFECTIVEINTELLIGENCECONFIG_EFFECTIVEEDITION']._serialized_end=8738 + _globals['_INTELLIGENCECONFIG_TRIALCONFIG']._serialized_start=8740 + _globals['_INTELLIGENCECONFIG_TRIALCONFIG']._serialized_end=8807 + _globals['_INTELLIGENCECONFIG_EDITIONCONFIG']._serialized_start=8809 + _globals['_INTELLIGENCECONFIG_EDITIONCONFIG']._serialized_end=8908 + _globals['_UPDATEORGANIZATIONINTELLIGENCECONFIGREQUEST']._serialized_start=9181 + _globals['_UPDATEORGANIZATIONINTELLIGENCECONFIGREQUEST']._serialized_end=9394 + _globals['_UPDATEFOLDERINTELLIGENCECONFIGREQUEST']._serialized_start=9397 + _globals['_UPDATEFOLDERINTELLIGENCECONFIGREQUEST']._serialized_end=9604 + _globals['_UPDATEPROJECTINTELLIGENCECONFIGREQUEST']._serialized_start=9607 + _globals['_UPDATEPROJECTINTELLIGENCECONFIGREQUEST']._serialized_end=9815 + _globals['_GETORGANIZATIONINTELLIGENCECONFIGREQUEST']._serialized_start=9817 + _globals['_GETORGANIZATIONINTELLIGENCECONFIGREQUEST']._serialized_end=9924 + _globals['_GETFOLDERINTELLIGENCECONFIGREQUEST']._serialized_start=9926 + _globals['_GETFOLDERINTELLIGENCECONFIGREQUEST']._serialized_end=10027 + _globals['_GETPROJECTINTELLIGENCECONFIGREQUEST']._serialized_start=10029 + _globals['_GETPROJECTINTELLIGENCECONFIGREQUEST']._serialized_end=10131 + _globals['_STORAGECONTROL']._serialized_start=10134 + _globals['_STORAGECONTROL']._serialized_end=15848 +# @@protoc_insertion_point(module_scope) diff --git a/google/storage/control/v2/storage_control_pb2_grpc.py b/google/storage/control/v2/storage_control_pb2_grpc.py new file mode 100644 index 00000000..cb1b186e --- /dev/null +++ b/google/storage/control/v2/storage_control_pb2_grpc.py @@ -0,0 +1,1275 @@ +# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! +"""Client and server classes corresponding to protobuf-defined services.""" +import grpc +import warnings + +from google.iam.v1 import iam_policy_pb2 as google_dot_iam_dot_v1_dot_iam__policy__pb2 +from google.iam.v1 import policy_pb2 as google_dot_iam_dot_v1_dot_policy__pb2 +from google.longrunning import operations_pb2 as google_dot_longrunning_dot_operations__pb2 +from google.protobuf import empty_pb2 as google_dot_protobuf_dot_empty__pb2 +from google.storage.control.v2 import storage_control_pb2 as google_dot_storage_dot_control_dot_v2_dot_storage__control__pb2 + +GRPC_GENERATED_VERSION = '1.70.0' +GRPC_VERSION = grpc.__version__ +_version_not_supported = False + +try: + from grpc._utilities import first_version_is_lower + _version_not_supported = first_version_is_lower(GRPC_VERSION, GRPC_GENERATED_VERSION) +except ImportError: + _version_not_supported = True + +if _version_not_supported: + raise RuntimeError( + f'The grpc package installed is at version {GRPC_VERSION},' + + f' but the generated code in google/storage/control/v2/storage_control_pb2_grpc.py depends on' + + f' grpcio>={GRPC_GENERATED_VERSION}.' + + f' Please upgrade your grpc module to grpcio>={GRPC_GENERATED_VERSION}' + + f' or downgrade your generated code using grpcio-tools<={GRPC_VERSION}.' + ) + + +class StorageControlStub(object): + """StorageControl service includes selected control plane operations. + """ + + def __init__(self, channel): + """Constructor. + + Args: + channel: A grpc.Channel. + """ + self.CreateFolder = channel.unary_unary( + '/google.storage.control.v2.StorageControl/CreateFolder', + request_serializer=google_dot_storage_dot_control_dot_v2_dot_storage__control__pb2.CreateFolderRequest.SerializeToString, + response_deserializer=google_dot_storage_dot_control_dot_v2_dot_storage__control__pb2.Folder.FromString, + _registered_method=True) + self.DeleteFolder = channel.unary_unary( + '/google.storage.control.v2.StorageControl/DeleteFolder', + request_serializer=google_dot_storage_dot_control_dot_v2_dot_storage__control__pb2.DeleteFolderRequest.SerializeToString, + response_deserializer=google_dot_protobuf_dot_empty__pb2.Empty.FromString, + _registered_method=True) + self.GetFolder = channel.unary_unary( + '/google.storage.control.v2.StorageControl/GetFolder', + request_serializer=google_dot_storage_dot_control_dot_v2_dot_storage__control__pb2.GetFolderRequest.SerializeToString, + response_deserializer=google_dot_storage_dot_control_dot_v2_dot_storage__control__pb2.Folder.FromString, + _registered_method=True) + self.ListFolders = channel.unary_unary( + '/google.storage.control.v2.StorageControl/ListFolders', + request_serializer=google_dot_storage_dot_control_dot_v2_dot_storage__control__pb2.ListFoldersRequest.SerializeToString, + response_deserializer=google_dot_storage_dot_control_dot_v2_dot_storage__control__pb2.ListFoldersResponse.FromString, + _registered_method=True) + self.RenameFolder = channel.unary_unary( + '/google.storage.control.v2.StorageControl/RenameFolder', + request_serializer=google_dot_storage_dot_control_dot_v2_dot_storage__control__pb2.RenameFolderRequest.SerializeToString, + response_deserializer=google_dot_longrunning_dot_operations__pb2.Operation.FromString, + _registered_method=True) + self.DeleteFolderRecursive = channel.unary_unary( + '/google.storage.control.v2.StorageControl/DeleteFolderRecursive', + request_serializer=google_dot_storage_dot_control_dot_v2_dot_storage__control__pb2.DeleteFolderRecursiveRequest.SerializeToString, + response_deserializer=google_dot_longrunning_dot_operations__pb2.Operation.FromString, + _registered_method=True) + self.GetStorageLayout = channel.unary_unary( + '/google.storage.control.v2.StorageControl/GetStorageLayout', + request_serializer=google_dot_storage_dot_control_dot_v2_dot_storage__control__pb2.GetStorageLayoutRequest.SerializeToString, + response_deserializer=google_dot_storage_dot_control_dot_v2_dot_storage__control__pb2.StorageLayout.FromString, + _registered_method=True) + self.CreateManagedFolder = channel.unary_unary( + '/google.storage.control.v2.StorageControl/CreateManagedFolder', + request_serializer=google_dot_storage_dot_control_dot_v2_dot_storage__control__pb2.CreateManagedFolderRequest.SerializeToString, + response_deserializer=google_dot_storage_dot_control_dot_v2_dot_storage__control__pb2.ManagedFolder.FromString, + _registered_method=True) + self.DeleteManagedFolder = channel.unary_unary( + '/google.storage.control.v2.StorageControl/DeleteManagedFolder', + request_serializer=google_dot_storage_dot_control_dot_v2_dot_storage__control__pb2.DeleteManagedFolderRequest.SerializeToString, + response_deserializer=google_dot_protobuf_dot_empty__pb2.Empty.FromString, + _registered_method=True) + self.GetManagedFolder = channel.unary_unary( + '/google.storage.control.v2.StorageControl/GetManagedFolder', + request_serializer=google_dot_storage_dot_control_dot_v2_dot_storage__control__pb2.GetManagedFolderRequest.SerializeToString, + response_deserializer=google_dot_storage_dot_control_dot_v2_dot_storage__control__pb2.ManagedFolder.FromString, + _registered_method=True) + self.ListManagedFolders = channel.unary_unary( + '/google.storage.control.v2.StorageControl/ListManagedFolders', + request_serializer=google_dot_storage_dot_control_dot_v2_dot_storage__control__pb2.ListManagedFoldersRequest.SerializeToString, + response_deserializer=google_dot_storage_dot_control_dot_v2_dot_storage__control__pb2.ListManagedFoldersResponse.FromString, + _registered_method=True) + self.CreateAnywhereCache = channel.unary_unary( + '/google.storage.control.v2.StorageControl/CreateAnywhereCache', + request_serializer=google_dot_storage_dot_control_dot_v2_dot_storage__control__pb2.CreateAnywhereCacheRequest.SerializeToString, + response_deserializer=google_dot_longrunning_dot_operations__pb2.Operation.FromString, + _registered_method=True) + self.UpdateAnywhereCache = channel.unary_unary( + '/google.storage.control.v2.StorageControl/UpdateAnywhereCache', + request_serializer=google_dot_storage_dot_control_dot_v2_dot_storage__control__pb2.UpdateAnywhereCacheRequest.SerializeToString, + response_deserializer=google_dot_longrunning_dot_operations__pb2.Operation.FromString, + _registered_method=True) + self.DisableAnywhereCache = channel.unary_unary( + '/google.storage.control.v2.StorageControl/DisableAnywhereCache', + request_serializer=google_dot_storage_dot_control_dot_v2_dot_storage__control__pb2.DisableAnywhereCacheRequest.SerializeToString, + response_deserializer=google_dot_storage_dot_control_dot_v2_dot_storage__control__pb2.AnywhereCache.FromString, + _registered_method=True) + self.PauseAnywhereCache = channel.unary_unary( + '/google.storage.control.v2.StorageControl/PauseAnywhereCache', + request_serializer=google_dot_storage_dot_control_dot_v2_dot_storage__control__pb2.PauseAnywhereCacheRequest.SerializeToString, + response_deserializer=google_dot_storage_dot_control_dot_v2_dot_storage__control__pb2.AnywhereCache.FromString, + _registered_method=True) + self.ResumeAnywhereCache = channel.unary_unary( + '/google.storage.control.v2.StorageControl/ResumeAnywhereCache', + request_serializer=google_dot_storage_dot_control_dot_v2_dot_storage__control__pb2.ResumeAnywhereCacheRequest.SerializeToString, + response_deserializer=google_dot_storage_dot_control_dot_v2_dot_storage__control__pb2.AnywhereCache.FromString, + _registered_method=True) + self.GetAnywhereCache = channel.unary_unary( + '/google.storage.control.v2.StorageControl/GetAnywhereCache', + request_serializer=google_dot_storage_dot_control_dot_v2_dot_storage__control__pb2.GetAnywhereCacheRequest.SerializeToString, + response_deserializer=google_dot_storage_dot_control_dot_v2_dot_storage__control__pb2.AnywhereCache.FromString, + _registered_method=True) + self.ListAnywhereCaches = channel.unary_unary( + '/google.storage.control.v2.StorageControl/ListAnywhereCaches', + request_serializer=google_dot_storage_dot_control_dot_v2_dot_storage__control__pb2.ListAnywhereCachesRequest.SerializeToString, + response_deserializer=google_dot_storage_dot_control_dot_v2_dot_storage__control__pb2.ListAnywhereCachesResponse.FromString, + _registered_method=True) + self.GetProjectIntelligenceConfig = channel.unary_unary( + '/google.storage.control.v2.StorageControl/GetProjectIntelligenceConfig', + request_serializer=google_dot_storage_dot_control_dot_v2_dot_storage__control__pb2.GetProjectIntelligenceConfigRequest.SerializeToString, + response_deserializer=google_dot_storage_dot_control_dot_v2_dot_storage__control__pb2.IntelligenceConfig.FromString, + _registered_method=True) + self.UpdateProjectIntelligenceConfig = channel.unary_unary( + '/google.storage.control.v2.StorageControl/UpdateProjectIntelligenceConfig', + request_serializer=google_dot_storage_dot_control_dot_v2_dot_storage__control__pb2.UpdateProjectIntelligenceConfigRequest.SerializeToString, + response_deserializer=google_dot_storage_dot_control_dot_v2_dot_storage__control__pb2.IntelligenceConfig.FromString, + _registered_method=True) + self.GetFolderIntelligenceConfig = channel.unary_unary( + '/google.storage.control.v2.StorageControl/GetFolderIntelligenceConfig', + request_serializer=google_dot_storage_dot_control_dot_v2_dot_storage__control__pb2.GetFolderIntelligenceConfigRequest.SerializeToString, + response_deserializer=google_dot_storage_dot_control_dot_v2_dot_storage__control__pb2.IntelligenceConfig.FromString, + _registered_method=True) + self.UpdateFolderIntelligenceConfig = channel.unary_unary( + '/google.storage.control.v2.StorageControl/UpdateFolderIntelligenceConfig', + request_serializer=google_dot_storage_dot_control_dot_v2_dot_storage__control__pb2.UpdateFolderIntelligenceConfigRequest.SerializeToString, + response_deserializer=google_dot_storage_dot_control_dot_v2_dot_storage__control__pb2.IntelligenceConfig.FromString, + _registered_method=True) + self.GetOrganizationIntelligenceConfig = channel.unary_unary( + '/google.storage.control.v2.StorageControl/GetOrganizationIntelligenceConfig', + request_serializer=google_dot_storage_dot_control_dot_v2_dot_storage__control__pb2.GetOrganizationIntelligenceConfigRequest.SerializeToString, + response_deserializer=google_dot_storage_dot_control_dot_v2_dot_storage__control__pb2.IntelligenceConfig.FromString, + _registered_method=True) + self.UpdateOrganizationIntelligenceConfig = channel.unary_unary( + '/google.storage.control.v2.StorageControl/UpdateOrganizationIntelligenceConfig', + request_serializer=google_dot_storage_dot_control_dot_v2_dot_storage__control__pb2.UpdateOrganizationIntelligenceConfigRequest.SerializeToString, + response_deserializer=google_dot_storage_dot_control_dot_v2_dot_storage__control__pb2.IntelligenceConfig.FromString, + _registered_method=True) + self.GetIamPolicy = channel.unary_unary( + '/google.storage.control.v2.StorageControl/GetIamPolicy', + request_serializer=google_dot_iam_dot_v1_dot_iam__policy__pb2.GetIamPolicyRequest.SerializeToString, + response_deserializer=google_dot_iam_dot_v1_dot_policy__pb2.Policy.FromString, + _registered_method=True) + self.SetIamPolicy = channel.unary_unary( + '/google.storage.control.v2.StorageControl/SetIamPolicy', + request_serializer=google_dot_iam_dot_v1_dot_iam__policy__pb2.SetIamPolicyRequest.SerializeToString, + response_deserializer=google_dot_iam_dot_v1_dot_policy__pb2.Policy.FromString, + _registered_method=True) + self.TestIamPermissions = channel.unary_unary( + '/google.storage.control.v2.StorageControl/TestIamPermissions', + request_serializer=google_dot_iam_dot_v1_dot_iam__policy__pb2.TestIamPermissionsRequest.SerializeToString, + response_deserializer=google_dot_iam_dot_v1_dot_iam__policy__pb2.TestIamPermissionsResponse.FromString, + _registered_method=True) + + +class StorageControlServicer(object): + """StorageControl service includes selected control plane operations. + """ + + def CreateFolder(self, request, context): + """Creates a new folder. This operation is only applicable to a hierarchical + namespace enabled bucket. + """ + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def DeleteFolder(self, request, context): + """Permanently deletes an empty folder. This operation is only applicable to a + hierarchical namespace enabled bucket. + """ + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def GetFolder(self, request, context): + """Returns metadata for the specified folder. This operation is only + applicable to a hierarchical namespace enabled bucket. + """ + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def ListFolders(self, request, context): + """Retrieves a list of folders. This operation is only applicable to a + hierarchical namespace enabled bucket. + """ + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def RenameFolder(self, request, context): + """Renames a source folder to a destination folder. This operation is only + applicable to a hierarchical namespace enabled bucket. During a rename, the + source and destination folders are locked until the long running operation + completes. + """ + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def DeleteFolderRecursive(self, request, context): + """Deletes a folder recursively. This operation is only applicable to a + hierarchical namespace enabled bucket. + """ + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def GetStorageLayout(self, request, context): + """Returns the storage layout configuration for a given bucket. + """ + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def CreateManagedFolder(self, request, context): + """Creates a new managed folder. + """ + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def DeleteManagedFolder(self, request, context): + """Permanently deletes an empty managed folder. + """ + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def GetManagedFolder(self, request, context): + """Returns metadata for the specified managed folder. + """ + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def ListManagedFolders(self, request, context): + """Retrieves a list of managed folders for a given bucket. + """ + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def CreateAnywhereCache(self, request, context): + """Creates an Anywhere Cache instance. + """ + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def UpdateAnywhereCache(self, request, context): + """Updates an Anywhere Cache instance. Mutable fields include `ttl` and + `admission_policy`. + """ + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def DisableAnywhereCache(self, request, context): + """Disables an Anywhere Cache instance. A disabled instance is read-only. The + disablement could be revoked by calling ResumeAnywhereCache. The cache + instance will be deleted automatically if it remains in the disabled state + for at least one hour. + """ + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def PauseAnywhereCache(self, request, context): + """Pauses an Anywhere Cache instance. + """ + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def ResumeAnywhereCache(self, request, context): + """Resumes a disabled or paused Anywhere Cache instance. + """ + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def GetAnywhereCache(self, request, context): + """Gets an Anywhere Cache instance. + """ + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def ListAnywhereCaches(self, request, context): + """Lists Anywhere Cache instances for a given bucket. + """ + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def GetProjectIntelligenceConfig(self, request, context): + """Returns the Project scoped singleton IntelligenceConfig resource. + """ + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def UpdateProjectIntelligenceConfig(self, request, context): + """Updates the Project scoped singleton IntelligenceConfig resource. + """ + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def GetFolderIntelligenceConfig(self, request, context): + """Returns the Folder scoped singleton IntelligenceConfig resource. + """ + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def UpdateFolderIntelligenceConfig(self, request, context): + """Updates the Folder scoped singleton IntelligenceConfig resource. + """ + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def GetOrganizationIntelligenceConfig(self, request, context): + """Returns the Organization scoped singleton IntelligenceConfig resource. + """ + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def UpdateOrganizationIntelligenceConfig(self, request, context): + """Updates the Organization scoped singleton IntelligenceConfig resource. + """ + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def GetIamPolicy(self, request, context): + """Gets the IAM policy for a specified bucket. + The `resource` field in the request should be + `projects/_/buckets/{bucket}` for a bucket, or + `projects/_/buckets/{bucket}/managedFolders/{managedFolder}` + for a managed folder. + """ + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def SetIamPolicy(self, request, context): + """Updates an IAM policy for the specified bucket. + The `resource` field in the request should be + `projects/_/buckets/{bucket}` for a bucket, or + `projects/_/buckets/{bucket}/managedFolders/{managedFolder}` + for a managed folder. + """ + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def TestIamPermissions(self, request, context): + """Tests a set of permissions on the given bucket, object, or managed folder + to see which, if any, are held by the caller. + The `resource` field in the request should be + `projects/_/buckets/{bucket}` for a bucket, + `projects/_/buckets/{bucket}/objects/{object}` for an object, or + `projects/_/buckets/{bucket}/managedFolders/{managedFolder}` + for a managed folder. + """ + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + +def add_StorageControlServicer_to_server(servicer, server): + rpc_method_handlers = { + 'CreateFolder': grpc.unary_unary_rpc_method_handler( + servicer.CreateFolder, + request_deserializer=google_dot_storage_dot_control_dot_v2_dot_storage__control__pb2.CreateFolderRequest.FromString, + response_serializer=google_dot_storage_dot_control_dot_v2_dot_storage__control__pb2.Folder.SerializeToString, + ), + 'DeleteFolder': grpc.unary_unary_rpc_method_handler( + servicer.DeleteFolder, + request_deserializer=google_dot_storage_dot_control_dot_v2_dot_storage__control__pb2.DeleteFolderRequest.FromString, + response_serializer=google_dot_protobuf_dot_empty__pb2.Empty.SerializeToString, + ), + 'GetFolder': grpc.unary_unary_rpc_method_handler( + servicer.GetFolder, + request_deserializer=google_dot_storage_dot_control_dot_v2_dot_storage__control__pb2.GetFolderRequest.FromString, + response_serializer=google_dot_storage_dot_control_dot_v2_dot_storage__control__pb2.Folder.SerializeToString, + ), + 'ListFolders': grpc.unary_unary_rpc_method_handler( + servicer.ListFolders, + request_deserializer=google_dot_storage_dot_control_dot_v2_dot_storage__control__pb2.ListFoldersRequest.FromString, + response_serializer=google_dot_storage_dot_control_dot_v2_dot_storage__control__pb2.ListFoldersResponse.SerializeToString, + ), + 'RenameFolder': grpc.unary_unary_rpc_method_handler( + servicer.RenameFolder, + request_deserializer=google_dot_storage_dot_control_dot_v2_dot_storage__control__pb2.RenameFolderRequest.FromString, + response_serializer=google_dot_longrunning_dot_operations__pb2.Operation.SerializeToString, + ), + 'DeleteFolderRecursive': grpc.unary_unary_rpc_method_handler( + servicer.DeleteFolderRecursive, + request_deserializer=google_dot_storage_dot_control_dot_v2_dot_storage__control__pb2.DeleteFolderRecursiveRequest.FromString, + response_serializer=google_dot_longrunning_dot_operations__pb2.Operation.SerializeToString, + ), + 'GetStorageLayout': grpc.unary_unary_rpc_method_handler( + servicer.GetStorageLayout, + request_deserializer=google_dot_storage_dot_control_dot_v2_dot_storage__control__pb2.GetStorageLayoutRequest.FromString, + response_serializer=google_dot_storage_dot_control_dot_v2_dot_storage__control__pb2.StorageLayout.SerializeToString, + ), + 'CreateManagedFolder': grpc.unary_unary_rpc_method_handler( + servicer.CreateManagedFolder, + request_deserializer=google_dot_storage_dot_control_dot_v2_dot_storage__control__pb2.CreateManagedFolderRequest.FromString, + response_serializer=google_dot_storage_dot_control_dot_v2_dot_storage__control__pb2.ManagedFolder.SerializeToString, + ), + 'DeleteManagedFolder': grpc.unary_unary_rpc_method_handler( + servicer.DeleteManagedFolder, + request_deserializer=google_dot_storage_dot_control_dot_v2_dot_storage__control__pb2.DeleteManagedFolderRequest.FromString, + response_serializer=google_dot_protobuf_dot_empty__pb2.Empty.SerializeToString, + ), + 'GetManagedFolder': grpc.unary_unary_rpc_method_handler( + servicer.GetManagedFolder, + request_deserializer=google_dot_storage_dot_control_dot_v2_dot_storage__control__pb2.GetManagedFolderRequest.FromString, + response_serializer=google_dot_storage_dot_control_dot_v2_dot_storage__control__pb2.ManagedFolder.SerializeToString, + ), + 'ListManagedFolders': grpc.unary_unary_rpc_method_handler( + servicer.ListManagedFolders, + request_deserializer=google_dot_storage_dot_control_dot_v2_dot_storage__control__pb2.ListManagedFoldersRequest.FromString, + response_serializer=google_dot_storage_dot_control_dot_v2_dot_storage__control__pb2.ListManagedFoldersResponse.SerializeToString, + ), + 'CreateAnywhereCache': grpc.unary_unary_rpc_method_handler( + servicer.CreateAnywhereCache, + request_deserializer=google_dot_storage_dot_control_dot_v2_dot_storage__control__pb2.CreateAnywhereCacheRequest.FromString, + response_serializer=google_dot_longrunning_dot_operations__pb2.Operation.SerializeToString, + ), + 'UpdateAnywhereCache': grpc.unary_unary_rpc_method_handler( + servicer.UpdateAnywhereCache, + request_deserializer=google_dot_storage_dot_control_dot_v2_dot_storage__control__pb2.UpdateAnywhereCacheRequest.FromString, + response_serializer=google_dot_longrunning_dot_operations__pb2.Operation.SerializeToString, + ), + 'DisableAnywhereCache': grpc.unary_unary_rpc_method_handler( + servicer.DisableAnywhereCache, + request_deserializer=google_dot_storage_dot_control_dot_v2_dot_storage__control__pb2.DisableAnywhereCacheRequest.FromString, + response_serializer=google_dot_storage_dot_control_dot_v2_dot_storage__control__pb2.AnywhereCache.SerializeToString, + ), + 'PauseAnywhereCache': grpc.unary_unary_rpc_method_handler( + servicer.PauseAnywhereCache, + request_deserializer=google_dot_storage_dot_control_dot_v2_dot_storage__control__pb2.PauseAnywhereCacheRequest.FromString, + response_serializer=google_dot_storage_dot_control_dot_v2_dot_storage__control__pb2.AnywhereCache.SerializeToString, + ), + 'ResumeAnywhereCache': grpc.unary_unary_rpc_method_handler( + servicer.ResumeAnywhereCache, + request_deserializer=google_dot_storage_dot_control_dot_v2_dot_storage__control__pb2.ResumeAnywhereCacheRequest.FromString, + response_serializer=google_dot_storage_dot_control_dot_v2_dot_storage__control__pb2.AnywhereCache.SerializeToString, + ), + 'GetAnywhereCache': grpc.unary_unary_rpc_method_handler( + servicer.GetAnywhereCache, + request_deserializer=google_dot_storage_dot_control_dot_v2_dot_storage__control__pb2.GetAnywhereCacheRequest.FromString, + response_serializer=google_dot_storage_dot_control_dot_v2_dot_storage__control__pb2.AnywhereCache.SerializeToString, + ), + 'ListAnywhereCaches': grpc.unary_unary_rpc_method_handler( + servicer.ListAnywhereCaches, + request_deserializer=google_dot_storage_dot_control_dot_v2_dot_storage__control__pb2.ListAnywhereCachesRequest.FromString, + response_serializer=google_dot_storage_dot_control_dot_v2_dot_storage__control__pb2.ListAnywhereCachesResponse.SerializeToString, + ), + 'GetProjectIntelligenceConfig': grpc.unary_unary_rpc_method_handler( + servicer.GetProjectIntelligenceConfig, + request_deserializer=google_dot_storage_dot_control_dot_v2_dot_storage__control__pb2.GetProjectIntelligenceConfigRequest.FromString, + response_serializer=google_dot_storage_dot_control_dot_v2_dot_storage__control__pb2.IntelligenceConfig.SerializeToString, + ), + 'UpdateProjectIntelligenceConfig': grpc.unary_unary_rpc_method_handler( + servicer.UpdateProjectIntelligenceConfig, + request_deserializer=google_dot_storage_dot_control_dot_v2_dot_storage__control__pb2.UpdateProjectIntelligenceConfigRequest.FromString, + response_serializer=google_dot_storage_dot_control_dot_v2_dot_storage__control__pb2.IntelligenceConfig.SerializeToString, + ), + 'GetFolderIntelligenceConfig': grpc.unary_unary_rpc_method_handler( + servicer.GetFolderIntelligenceConfig, + request_deserializer=google_dot_storage_dot_control_dot_v2_dot_storage__control__pb2.GetFolderIntelligenceConfigRequest.FromString, + response_serializer=google_dot_storage_dot_control_dot_v2_dot_storage__control__pb2.IntelligenceConfig.SerializeToString, + ), + 'UpdateFolderIntelligenceConfig': grpc.unary_unary_rpc_method_handler( + servicer.UpdateFolderIntelligenceConfig, + request_deserializer=google_dot_storage_dot_control_dot_v2_dot_storage__control__pb2.UpdateFolderIntelligenceConfigRequest.FromString, + response_serializer=google_dot_storage_dot_control_dot_v2_dot_storage__control__pb2.IntelligenceConfig.SerializeToString, + ), + 'GetOrganizationIntelligenceConfig': grpc.unary_unary_rpc_method_handler( + servicer.GetOrganizationIntelligenceConfig, + request_deserializer=google_dot_storage_dot_control_dot_v2_dot_storage__control__pb2.GetOrganizationIntelligenceConfigRequest.FromString, + response_serializer=google_dot_storage_dot_control_dot_v2_dot_storage__control__pb2.IntelligenceConfig.SerializeToString, + ), + 'UpdateOrganizationIntelligenceConfig': grpc.unary_unary_rpc_method_handler( + servicer.UpdateOrganizationIntelligenceConfig, + request_deserializer=google_dot_storage_dot_control_dot_v2_dot_storage__control__pb2.UpdateOrganizationIntelligenceConfigRequest.FromString, + response_serializer=google_dot_storage_dot_control_dot_v2_dot_storage__control__pb2.IntelligenceConfig.SerializeToString, + ), + 'GetIamPolicy': grpc.unary_unary_rpc_method_handler( + servicer.GetIamPolicy, + request_deserializer=google_dot_iam_dot_v1_dot_iam__policy__pb2.GetIamPolicyRequest.FromString, + response_serializer=google_dot_iam_dot_v1_dot_policy__pb2.Policy.SerializeToString, + ), + 'SetIamPolicy': grpc.unary_unary_rpc_method_handler( + servicer.SetIamPolicy, + request_deserializer=google_dot_iam_dot_v1_dot_iam__policy__pb2.SetIamPolicyRequest.FromString, + response_serializer=google_dot_iam_dot_v1_dot_policy__pb2.Policy.SerializeToString, + ), + 'TestIamPermissions': grpc.unary_unary_rpc_method_handler( + servicer.TestIamPermissions, + request_deserializer=google_dot_iam_dot_v1_dot_iam__policy__pb2.TestIamPermissionsRequest.FromString, + response_serializer=google_dot_iam_dot_v1_dot_iam__policy__pb2.TestIamPermissionsResponse.SerializeToString, + ), + } + generic_handler = grpc.method_handlers_generic_handler( + 'google.storage.control.v2.StorageControl', rpc_method_handlers) + server.add_generic_rpc_handlers((generic_handler,)) + server.add_registered_method_handlers('google.storage.control.v2.StorageControl', rpc_method_handlers) + + + # This class is part of an EXPERIMENTAL API. +class StorageControl(object): + """StorageControl service includes selected control plane operations. + """ + + @staticmethod + def CreateFolder(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/google.storage.control.v2.StorageControl/CreateFolder', + google_dot_storage_dot_control_dot_v2_dot_storage__control__pb2.CreateFolderRequest.SerializeToString, + google_dot_storage_dot_control_dot_v2_dot_storage__control__pb2.Folder.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) + + @staticmethod + def DeleteFolder(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/google.storage.control.v2.StorageControl/DeleteFolder', + google_dot_storage_dot_control_dot_v2_dot_storage__control__pb2.DeleteFolderRequest.SerializeToString, + google_dot_protobuf_dot_empty__pb2.Empty.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) + + @staticmethod + def GetFolder(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/google.storage.control.v2.StorageControl/GetFolder', + google_dot_storage_dot_control_dot_v2_dot_storage__control__pb2.GetFolderRequest.SerializeToString, + google_dot_storage_dot_control_dot_v2_dot_storage__control__pb2.Folder.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) + + @staticmethod + def ListFolders(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/google.storage.control.v2.StorageControl/ListFolders', + google_dot_storage_dot_control_dot_v2_dot_storage__control__pb2.ListFoldersRequest.SerializeToString, + google_dot_storage_dot_control_dot_v2_dot_storage__control__pb2.ListFoldersResponse.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) + + @staticmethod + def RenameFolder(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/google.storage.control.v2.StorageControl/RenameFolder', + google_dot_storage_dot_control_dot_v2_dot_storage__control__pb2.RenameFolderRequest.SerializeToString, + google_dot_longrunning_dot_operations__pb2.Operation.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) + + @staticmethod + def DeleteFolderRecursive(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/google.storage.control.v2.StorageControl/DeleteFolderRecursive', + google_dot_storage_dot_control_dot_v2_dot_storage__control__pb2.DeleteFolderRecursiveRequest.SerializeToString, + google_dot_longrunning_dot_operations__pb2.Operation.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) + + @staticmethod + def GetStorageLayout(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/google.storage.control.v2.StorageControl/GetStorageLayout', + google_dot_storage_dot_control_dot_v2_dot_storage__control__pb2.GetStorageLayoutRequest.SerializeToString, + google_dot_storage_dot_control_dot_v2_dot_storage__control__pb2.StorageLayout.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) + + @staticmethod + def CreateManagedFolder(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/google.storage.control.v2.StorageControl/CreateManagedFolder', + google_dot_storage_dot_control_dot_v2_dot_storage__control__pb2.CreateManagedFolderRequest.SerializeToString, + google_dot_storage_dot_control_dot_v2_dot_storage__control__pb2.ManagedFolder.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) + + @staticmethod + def DeleteManagedFolder(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/google.storage.control.v2.StorageControl/DeleteManagedFolder', + google_dot_storage_dot_control_dot_v2_dot_storage__control__pb2.DeleteManagedFolderRequest.SerializeToString, + google_dot_protobuf_dot_empty__pb2.Empty.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) + + @staticmethod + def GetManagedFolder(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/google.storage.control.v2.StorageControl/GetManagedFolder', + google_dot_storage_dot_control_dot_v2_dot_storage__control__pb2.GetManagedFolderRequest.SerializeToString, + google_dot_storage_dot_control_dot_v2_dot_storage__control__pb2.ManagedFolder.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) + + @staticmethod + def ListManagedFolders(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/google.storage.control.v2.StorageControl/ListManagedFolders', + google_dot_storage_dot_control_dot_v2_dot_storage__control__pb2.ListManagedFoldersRequest.SerializeToString, + google_dot_storage_dot_control_dot_v2_dot_storage__control__pb2.ListManagedFoldersResponse.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) + + @staticmethod + def CreateAnywhereCache(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/google.storage.control.v2.StorageControl/CreateAnywhereCache', + google_dot_storage_dot_control_dot_v2_dot_storage__control__pb2.CreateAnywhereCacheRequest.SerializeToString, + google_dot_longrunning_dot_operations__pb2.Operation.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) + + @staticmethod + def UpdateAnywhereCache(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/google.storage.control.v2.StorageControl/UpdateAnywhereCache', + google_dot_storage_dot_control_dot_v2_dot_storage__control__pb2.UpdateAnywhereCacheRequest.SerializeToString, + google_dot_longrunning_dot_operations__pb2.Operation.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) + + @staticmethod + def DisableAnywhereCache(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/google.storage.control.v2.StorageControl/DisableAnywhereCache', + google_dot_storage_dot_control_dot_v2_dot_storage__control__pb2.DisableAnywhereCacheRequest.SerializeToString, + google_dot_storage_dot_control_dot_v2_dot_storage__control__pb2.AnywhereCache.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) + + @staticmethod + def PauseAnywhereCache(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/google.storage.control.v2.StorageControl/PauseAnywhereCache', + google_dot_storage_dot_control_dot_v2_dot_storage__control__pb2.PauseAnywhereCacheRequest.SerializeToString, + google_dot_storage_dot_control_dot_v2_dot_storage__control__pb2.AnywhereCache.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) + + @staticmethod + def ResumeAnywhereCache(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/google.storage.control.v2.StorageControl/ResumeAnywhereCache', + google_dot_storage_dot_control_dot_v2_dot_storage__control__pb2.ResumeAnywhereCacheRequest.SerializeToString, + google_dot_storage_dot_control_dot_v2_dot_storage__control__pb2.AnywhereCache.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) + + @staticmethod + def GetAnywhereCache(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/google.storage.control.v2.StorageControl/GetAnywhereCache', + google_dot_storage_dot_control_dot_v2_dot_storage__control__pb2.GetAnywhereCacheRequest.SerializeToString, + google_dot_storage_dot_control_dot_v2_dot_storage__control__pb2.AnywhereCache.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) + + @staticmethod + def ListAnywhereCaches(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/google.storage.control.v2.StorageControl/ListAnywhereCaches', + google_dot_storage_dot_control_dot_v2_dot_storage__control__pb2.ListAnywhereCachesRequest.SerializeToString, + google_dot_storage_dot_control_dot_v2_dot_storage__control__pb2.ListAnywhereCachesResponse.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) + + @staticmethod + def GetProjectIntelligenceConfig(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/google.storage.control.v2.StorageControl/GetProjectIntelligenceConfig', + google_dot_storage_dot_control_dot_v2_dot_storage__control__pb2.GetProjectIntelligenceConfigRequest.SerializeToString, + google_dot_storage_dot_control_dot_v2_dot_storage__control__pb2.IntelligenceConfig.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) + + @staticmethod + def UpdateProjectIntelligenceConfig(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/google.storage.control.v2.StorageControl/UpdateProjectIntelligenceConfig', + google_dot_storage_dot_control_dot_v2_dot_storage__control__pb2.UpdateProjectIntelligenceConfigRequest.SerializeToString, + google_dot_storage_dot_control_dot_v2_dot_storage__control__pb2.IntelligenceConfig.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) + + @staticmethod + def GetFolderIntelligenceConfig(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/google.storage.control.v2.StorageControl/GetFolderIntelligenceConfig', + google_dot_storage_dot_control_dot_v2_dot_storage__control__pb2.GetFolderIntelligenceConfigRequest.SerializeToString, + google_dot_storage_dot_control_dot_v2_dot_storage__control__pb2.IntelligenceConfig.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) + + @staticmethod + def UpdateFolderIntelligenceConfig(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/google.storage.control.v2.StorageControl/UpdateFolderIntelligenceConfig', + google_dot_storage_dot_control_dot_v2_dot_storage__control__pb2.UpdateFolderIntelligenceConfigRequest.SerializeToString, + google_dot_storage_dot_control_dot_v2_dot_storage__control__pb2.IntelligenceConfig.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) + + @staticmethod + def GetOrganizationIntelligenceConfig(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/google.storage.control.v2.StorageControl/GetOrganizationIntelligenceConfig', + google_dot_storage_dot_control_dot_v2_dot_storage__control__pb2.GetOrganizationIntelligenceConfigRequest.SerializeToString, + google_dot_storage_dot_control_dot_v2_dot_storage__control__pb2.IntelligenceConfig.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) + + @staticmethod + def UpdateOrganizationIntelligenceConfig(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/google.storage.control.v2.StorageControl/UpdateOrganizationIntelligenceConfig', + google_dot_storage_dot_control_dot_v2_dot_storage__control__pb2.UpdateOrganizationIntelligenceConfigRequest.SerializeToString, + google_dot_storage_dot_control_dot_v2_dot_storage__control__pb2.IntelligenceConfig.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) + + @staticmethod + def GetIamPolicy(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/google.storage.control.v2.StorageControl/GetIamPolicy', + google_dot_iam_dot_v1_dot_iam__policy__pb2.GetIamPolicyRequest.SerializeToString, + google_dot_iam_dot_v1_dot_policy__pb2.Policy.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) + + @staticmethod + def SetIamPolicy(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/google.storage.control.v2.StorageControl/SetIamPolicy', + google_dot_iam_dot_v1_dot_iam__policy__pb2.SetIamPolicyRequest.SerializeToString, + google_dot_iam_dot_v1_dot_policy__pb2.Policy.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) + + @staticmethod + def TestIamPermissions(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/google.storage.control.v2.StorageControl/TestIamPermissions', + google_dot_iam_dot_v1_dot_iam__policy__pb2.TestIamPermissionsRequest.SerializeToString, + google_dot_iam_dot_v1_dot_iam__policy__pb2.TestIamPermissionsResponse.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) diff --git a/google/storage/v2/storage_pb2.py b/google/storage/v2/storage_pb2.py index f26e3b24..392dd83a 100644 --- a/google/storage/v2/storage_pb2.py +++ b/google/storage/v2/storage_pb2.py @@ -1,18 +1,4 @@ # -*- coding: utf-8 -*- -# Copyright 2024 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. - # Generated by the protocol buffer compiler. DO NOT EDIT! # NO CHECKED-IN PROTOBUF GENCODE # source: google/storage/v2/storage.proto diff --git a/google/storage/v2/storage_pb2_grpc.py b/google/storage/v2/storage_pb2_grpc.py index 1ed487bf..fa6feef8 100644 --- a/google/storage/v2/storage_pb2_grpc.py +++ b/google/storage/v2/storage_pb2_grpc.py @@ -1,18 +1,4 @@ # Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! -# Copyright 2024 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. - """Client and server classes corresponding to protobuf-defined services.""" import grpc import warnings diff --git a/testbench/database.py b/testbench/database.py index 675166f7..bd640c1e 100644 --- a/testbench/database.py +++ b/testbench/database.py @@ -39,6 +39,7 @@ def __init__( retry_tests, supported_methods, soft_deleted_objects, + folders=None, ): self._resources_lock = threading.RLock() self._buckets = buckets @@ -59,9 +60,12 @@ def __init__( self._projects_lock = threading.RLock() self._projects = {} + self._folders_lock = threading.RLock() + self._folders = folders if folders is not None else {} + @classmethod def init(cls): - return cls({}, {}, {}, {}, {}, {}, [], {}) + return cls({}, {}, {}, {}, {}, {}, [], {}, {}) def clear(self): """Clear all data except for the supported method list.""" @@ -76,6 +80,8 @@ def clear(self): self._rewrites = {} with self._retry_tests_lock: self._retry_tests = {} + with self._folders_lock: + self._folders = {} # The list of supported methods for `retry_test` is defined via flask # decorators, it should remain unchanged after the test or application # is initialized. Arguably this means it should be in a global variable. @@ -790,3 +796,58 @@ def delete_retry_test(self, retry_test_id): with self._retry_tests_lock: self.get_retry_test(retry_test_id) del self._retry_tests[retry_test_id] + + # === FOLDER OPERATIONS === # + + def insert_folder(self, folder_name, folder, context): + """Insert a folder into the database.""" + with self._folders_lock: + if folder_name in self._folders: + testbench.error.already_exists( + "Folder %s already exists" % folder_name, context + ) + self._folders[folder_name] = folder + return folder + + def get_folder(self, folder_name, context): + """Get a folder from the database.""" + with self._folders_lock: + folder = self._folders.get(folder_name) + if folder is None: + testbench.error.notfound("Folder %s" % folder_name, context) + return folder + + def delete_folder(self, folder_name, context): + """Delete a folder from the database.""" + with self._folders_lock: + if folder_name not in self._folders: + testbench.error.notfound("Folder %s" % folder_name, context) + del self._folders[folder_name] + + def list_folders(self, bucket_name, prefix, context): + """List folders in a bucket with optional prefix filter.""" + with self._folders_lock: + folders = [] + for folder_name, folder in self._folders.items(): + # Filter by bucket + if not folder_name.startswith(bucket_name): + continue + # Filter by prefix if provided + if prefix and not folder_name.startswith(f"{bucket_name}/{prefix}"): + continue + folders.append(folder) + return folders + + def rename_folder(self, src_folder_name, dst_folder_name, context): + """Rename a folder.""" + with self._folders_lock: + if src_folder_name not in self._folders: + testbench.error.notfound("Source folder %s" % src_folder_name, context) + if dst_folder_name in self._folders: + testbench.error.already_exists( + "Destination folder %s already exists" % dst_folder_name, context + ) + folder = self._folders[src_folder_name] + del self._folders[src_folder_name] + self._folders[dst_folder_name] = folder + return folder diff --git a/testbench/grpc_server.py b/testbench/grpc_server.py index 66782273..c59bfc10 100644 --- a/testbench/grpc_server.py +++ b/testbench/grpc_server.py @@ -40,6 +40,7 @@ import testbench from google.iam.v1 import iam_policy_pb2 from google.storage.v2 import storage_pb2, storage_pb2_grpc +from google.storage.control.v2 import storage_control_pb2, storage_control_pb2_grpc _GRPC_SERVER_THREAD_COUNT = 2 @@ -1163,6 +1164,81 @@ def QueryWriteStatus(self, request, context): return storage_pb2.QueryWriteStatusResponse(persisted_size=len(upload.media)) +# === STORAGE CONTROL SERVICER === # + + +@decorate_all_rpc_methods +class StorageControlServicer(storage_control_pb2_grpc.StorageControlServicer): + """Implements the google.storage.control.v2.StorageControl gRPC service.""" + + def __init__(self, db, echo_metadata=False): + self.db = db + self.db.insert_test_bucket() + self.echo_metadata = echo_metadata + + def _apply_stall(self, context): + """Check for stall instructions and apply delay if needed.""" + import time + + instruction = testbench.common.extract_instruction(None, context) + if instruction and "stall" in instruction: + # Parse stall instruction (e.g., "stall-always", "stall-for-10s") + if instruction.startswith("stall-always"): + time.sleep(10) + elif instruction.startswith("stall-for-"): + # Parse "stall-for-10s" format + match = re.match(r'stall-for-(\d+)s', instruction) + if match: + time.sleep(int(match.group(1))) + + @retry_test(method="storage.folders.create") + def CreateFolder(self, request, context): + self._apply_stall(context) + # Create a simple folder metadata + folder = storage_control_pb2.Folder() + # The name should include the full path + folder.name = f"{request.parent}/folders/{request.folder_id}" + folder.metageneration = 1 + folder.create_time.FromDatetime(datetime.datetime.now(datetime.timezone.utc)) + folder.update_time.CopyFrom(folder.create_time) + + # Store in database using full name as key + self.db.insert_folder(folder.name, folder, context) + return folder + + @retry_test(method="storage.folders.delete") + def DeleteFolder(self, request, context): + self._apply_stall(context) + folder_key = request.name + self.db.delete_folder(folder_key, context) + return empty_pb2.Empty() + + @retry_test(method="storage.folders.get") + def GetFolder(self, request, context): + self._apply_stall(context) + folder_key = request.name + return self.db.get_folder(folder_key, context) + + @retry_test(method="storage.folders.list") + def ListFolders(self, request, context): + self._apply_stall(context) + # Extract bucket from parent (format: "projects/_/buckets/{bucket}") + bucket_name = request.parent + prefix = request.prefix if hasattr(request, 'prefix') else "" + + folders = self.db.list_folders(bucket_name, prefix, context) + return storage_control_pb2.ListFoldersResponse(folders=folders) + + @retry_test(method="storage.folders.rename") + def RenameFolder(self, request, context): + self._apply_stall(context) + src_folder = request.name + dst_folder = request.destination_folder_id + + folder = self.db.rename_folder(src_folder, dst_folder, context) + return folder + + def run(port, database, echo_metadata=False): server = grpc.server( futures.ThreadPoolExecutor(max_workers=_GRPC_SERVER_THREAD_COUNT) @@ -1170,6 +1246,9 @@ def run(port, database, echo_metadata=False): storage_pb2_grpc.add_StorageServicer_to_server( StorageServicer(database, echo_metadata), server ) + storage_control_pb2_grpc.add_StorageControlServicer_to_server( + StorageControlServicer(database, echo_metadata), server + ) port = server.add_insecure_port("0.0.0.0:%d" % port) server.start() return port, server diff --git a/tests/test_storage_control_stall.py b/tests/test_storage_control_stall.py new file mode 100644 index 00000000..7b9994a0 --- /dev/null +++ b/tests/test_storage_control_stall.py @@ -0,0 +1,195 @@ +#!/usr/bin/env python3 +# Copyright 2020 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. + +"""Test stall functionality for Storage Control API.""" + +import time +import unittest +import unittest.mock + +import grpc + +import testbench +from google.protobuf import empty_pb2 +from google.storage.control.v2 import storage_control_pb2 + + +class TestStorageControlStall(unittest.TestCase): + """Test cases for Storage Control API stall functionality.""" + + def mock_context(self, metadata=None): + """Create a mock context with optional metadata.""" + context = unittest.mock.Mock() + if metadata is None: + metadata = [] + context.invocation_metadata = unittest.mock.Mock(return_value=metadata) + return context + + def setUp(self): + self.db = testbench.database.Database.init() + self.servicer = testbench.grpc_server.StorageControlServicer( + self.db, echo_metadata=False + ) + + def test_create_folder_no_stall(self): + """Test folder creation without stall instruction.""" + request = storage_control_pb2.CreateFolderRequest() + request.parent = "projects/_/buckets/test-bucket" + request.folder_id = "test-folder" + + context = self.mock_context() + + start_time = time.time() + folder = self.servicer.CreateFolder(request, context) + elapsed = time.time() - start_time + + self.assertIsNotNone(folder) + self.assertEqual(folder.name, "projects/_/buckets/test-bucket/folders/test-folder") + self.assertLess(elapsed, 1.0, "Should complete quickly without stall") + + def test_create_folder_stall_always(self): + """Test folder creation with stall-always instruction.""" + request = storage_control_pb2.CreateFolderRequest() + request.parent = "projects/_/buckets/test-bucket" + request.folder_id = "test-folder-stall" + + metadata = [("x-goog-emulator-instructions", "stall-always")] + context = self.mock_context(metadata) + + start_time = time.time() + folder = self.servicer.CreateFolder(request, context) + elapsed = time.time() - start_time + + self.assertIsNotNone(folder) + self.assertEqual(folder.name, "projects/_/buckets/test-bucket/folders/test-folder-stall") + self.assertGreaterEqual(elapsed, 10.0, "Should stall for at least 10 seconds") + + def test_create_folder_stall_custom_duration(self): + """Test folder creation with custom stall duration.""" + request = storage_control_pb2.CreateFolderRequest() + request.parent = "projects/_/buckets/test-bucket" + request.folder_id = "test-folder-custom-stall" + + metadata = [("x-goog-emulator-instructions", "stall-for-3s")] + context = self.mock_context(metadata) + + start_time = time.time() + folder = self.servicer.CreateFolder(request, context) + elapsed = time.time() - start_time + + self.assertIsNotNone(folder) + self.assertEqual(folder.name, "projects/_/buckets/test-bucket/folders/test-folder-custom-stall") + self.assertGreaterEqual(elapsed, 3.0, "Should stall for at least 3 seconds") + self.assertLess(elapsed, 5.0, "Should not stall longer than 5 seconds") + + def test_delete_folder_stall(self): + """Test folder deletion with stall instruction.""" + # First create a folder + create_request = storage_control_pb2.CreateFolderRequest() + create_request.parent = "projects/_/buckets/test-bucket" + create_request.folder_id = "test-folder-delete" + context = self.mock_context() + self.servicer.CreateFolder(create_request, context) + + # Now delete it with stall + delete_request = storage_control_pb2.DeleteFolderRequest() + delete_request.name = "projects/_/buckets/test-bucket/test-folder-delete" + + metadata = [("x-goog-emulator-instructions", "stall-for-2s")] + context_stall = self.mock_context(metadata) + + start_time = time.time() + result = self.servicer.DeleteFolder(delete_request, context_stall) + elapsed = time.time() - start_time + + self.assertIsInstance(result, empty_pb2.Empty) + self.assertGreaterEqual(elapsed, 2.0, "Should stall for at least 2 seconds") + + def test_get_folder_stall(self): + """Test get folder with stall instruction.""" + # First create a folder + create_request = storage_control_pb2.CreateFolderRequest() + create_request.parent = "projects/_/buckets/test-bucket" + create_request.folder_id = "test-folder-get" + context = self.mock_context() + created_folder = self.servicer.CreateFolder(create_request, context) + + # Now get it with stall + get_request = storage_control_pb2.GetFolderRequest() + get_request.name = "projects/_/buckets/test-bucket/folders/test-folder-get" + + metadata = [("x-goog-emulator-instructions", "stall-for-5s")] + context_stall = self.mock_context(metadata) + + start_time = time.time() + folder = self.servicer.GetFolder(get_request, context_stall) + elapsed = time.time() - start_time + + self.assertIsNotNone(folder) + self.assertEqual(folder.name, "projects/_/buckets/test-bucket/folders/test-folder-get") + self.assertGreaterEqual(elapsed, 5.0, "Should stall for at least 5 seconds") + + def test_list_folders_stall(self): + """Test list folders with stall instruction.""" + # Create some folders + for i in range(3): + create_request = storage_control_pb2.CreateFolderRequest() + create_request.parent = "projects/_/buckets/test-bucket" + create_request.folder_id = f"test-folder-list-{i}" + context = self.mock_context() + self.servicer.CreateFolder(create_request, context) + + # List with stall + list_request = storage_control_pb2.ListFoldersRequest() + list_request.parent = "projects/_/buckets/test-bucket" + + metadata = [("x-goog-emulator-instructions", "stall-for-4s")] + context_stall = self.mock_context(metadata) + + start_time = time.time() + response = self.servicer.ListFolders(list_request, context_stall) + elapsed = time.time() - start_time + + self.assertIsNotNone(response) + self.assertGreaterEqual(len(response.folders), 3) + self.assertGreaterEqual(elapsed, 4.0, "Should stall for at least 4 seconds") + + def test_rename_folder_stall(self): + """Test rename folder with stall instruction.""" + # Create a folder + create_request = storage_control_pb2.CreateFolderRequest() + create_request.parent = "projects/_/buckets/test-bucket" + create_request.folder_id = "test-folder-rename-src" + context = self.mock_context() + self.servicer.CreateFolder(create_request, context) + + # Rename with stall + rename_request = storage_control_pb2.RenameFolderRequest() + rename_request.name = "projects/_/buckets/test-bucket/test-folder-rename-src" + rename_request.destination_folder_id = "projects/_/buckets/test-bucket/test-folder-rename-dst" + + metadata = [("x-goog-emulator-instructions", "stall-for-6s")] + context_stall = self.mock_context(metadata) + + start_time = time.time() + folder = self.servicer.RenameFolder(rename_request, context_stall) + elapsed = time.time() - start_time + + self.assertIsNotNone(folder) + self.assertGreaterEqual(elapsed, 6.0, "Should stall for at least 6 seconds") + + +if __name__ == "__main__": + unittest.main() diff --git a/update-protos.sh b/update-protos.sh index 4e09fcf2..073767e5 100755 --- a/update-protos.sh +++ b/update-protos.sh @@ -31,6 +31,7 @@ readonly INPUTS=( google/iam/v1/options.proto google/iam/v1/policy.proto google/storage/v2/storage.proto + google/storage/control/v2/storage_control.proto ) readonly INPUTS From e2409e434f02c9dcb3557c8bc375b5eb96853b83 Mon Sep 17 00:00:00 2001 From: raj-prince Date: Tue, 17 Mar 2026 06:49:41 +0000 Subject: [PATCH 02/13] adding more test and removing redundant content --- README.md | 31 ++++- STALL_VALIDATION.md | 156 -------------------------- google/storage/control/__init__.py | 2 +- google/storage/control/v2/__init__.py | 13 --- testbench/grpc_server.py | 8 +- tests/test_storage_control_stall.py | 81 ++++++++++--- 6 files changed, 96 insertions(+), 195 deletions(-) delete mode 100644 STALL_VALIDATION.md delete mode 100644 google/storage/control/v2/__init__.py diff --git a/README.md b/README.md index 70042961..108f8cd7 100644 --- a/README.md +++ b/README.md @@ -15,8 +15,7 @@ is expected to be used by Storage library maintainers. - [When to use this testbench](#when-to-use-this-testbench) - [How to use this testbench](#how-to-use-this-testbench) - [Initial set up](#initial-set-up) - - [Run the testbench](#run-the-testbench) - - [Start the gRPC server](#start-the-gRPC-server) + - [Start the gRPC server](#start-the-grpc-server) - [Check that the testbench is running](#check-that-the-testbench-is-running) - [Updating Proto Files](#updating-proto-files) - [Force Failures](#force-failures) @@ -35,6 +34,7 @@ is expected to be used by Storage library maintainers. - [Delete a Retry Test resource](#delete-a-retry-test-resource) - [Causing a failure using x-retry-test-id header](#causing-a-failure-using-x-retry-test-id-header) - [Forced Failures Supported](#forced-failures-supported) + - [Storage Control API Stall Support](#storage-control-api-stall-support) - [Developing for the testbench](#developing-for-the-testbench) - [Writing and running tests](#writing-and-running-tests) - [Releasing the testbench](#releasing-the-testbench) @@ -277,6 +277,33 @@ curl -H "x-retry-test-id: 1d05c20627844214a9ff7cbcf696317d" "http://localhost:91 | redirect-send-token-T | [HTTP] Unsupported [GRPC] Testbench will fail the RPC with `ABORTED` and include appropriate redirection error details. | redirect-send-handle-and-token-T | [HTTP] Unsupported [GRPC] Testbench will fail the RPC with `ABORTED` and include appropriate redirection error details. +## Storage Control API Stall Support + +The testbench supports stall functionality for the Storage Control API (gRPC only) to test client retry behavior. All folder operations (`CreateFolder`, `DeleteFolder`, `GetFolder`, `ListFolders`, `RenameFolder`) can be delayed using the `x-goog-emulator-instructions` metadata header. + +> **Note:** The Storage Control API uses the **same gRPC server** as the Storage API. Both services are available on the same port (e.g., port 8888 if started with `curl "http://localhost:9000/start_grpc?port=8888"`). + +**Supported stall instruction:** +- `stall-for-Ns`: Stalls for N seconds (e.g., `stall-for-3s` stalls for 3 seconds) + +**Example usage in Python:** +```python +import grpc +from google.storage.control.v2 import storage_control_pb2, storage_control_pb2_grpc + +# Connect to the same gRPC server port (8888) started earlier +channel = grpc.insecure_channel('localhost:8888') +stub = storage_control_pb2_grpc.StorageControlStub(channel) + +# Create folder with 2-second stall +metadata = [('x-goog-emulator-instructions', 'stall-for-2s')] +request = storage_control_pb2.CreateFolderRequest( + parent="projects/_/buckets/test-bucket", + folder_id="test-folder" +) +response = stub.CreateFolder(request, metadata=metadata) +``` + ## Developing for the testbench ### Writing and running tests diff --git a/STALL_VALIDATION.md b/STALL_VALIDATION.md deleted file mode 100644 index c2727af6..00000000 --- a/STALL_VALIDATION.md +++ /dev/null @@ -1,156 +0,0 @@ -# Storage Control API Stall Implementation - Validation Guide - -## Overview - -This document describes how to validate the stall functionality for the Storage Control API that was implemented in this branch. - -## What Was Implemented - -1. **Protobuf Generation**: Added `google/storage/control/v2/storage_control.proto` to the proto generation pipeline -2. **Database Layer**: Added folder storage **capabilities** to `testbench/database.py` -3. **gRPC Service**: Implemented `StorageControlServicer` in `testbench/grpc_server.py` with stall support -4. **Stall Functionality**: Added `_apply_stall()` method that intercepts folder API calls and applies delays based on `x-goog-emulator-instructions` metadata - -## Stall Instructions Supported - -The implementation supports the following stall instructions via gRPC metadata header `x-goog-emulator-instructions`: - -- `stall-always`: Stalls for 10 seconds at the beginning of the request -- `stall-for-Ns`: Stalls for N seconds (e.g., `stall-for-3s` stalls for 3 seconds) - -## Supported Operations - -All Storage Control API folder operations support stall: -- `CreateFolder` -- `DeleteFolder` -- `GetFolder` -- `ListFolders` -- `RenameFolder` - -## Running the Tests - -### Prerequisites - -1. Ensure you're in a virtual environment: -```bash -source ../venv-storage-testbench/bin/activate -``` - -### Quick Validation (Non-Stall Test) - -Run this test to verify basic functionality (~1 second): -```bash -python -m unittest tests.test_storage_control_stall.TestStorageControlStall.test_create_folder_no_stall -v -``` - -### Stall Functionality Tests - -Run these tests to verify stall functionality (each takes several seconds due to intentional delays): - -**Custom duration stall (3 seconds):** -```bash -python -m unittest tests.test_storage_control_stall.TestStorageControlStall.test_create_folder_stall_custom_duration -v -``` - -**Delete with stall (2 seconds):** -```bash -python -m unittest tests.test_storage_control_stall.TestStorageControlStall.test_delete_folder_stall -v -``` - -**Get with stall (5 seconds):** -```bash -python -m unittest tests.test_storage_control_stall.TestStorageControlStall.test_get_folder_stall -v -``` - -**List with stall (4 seconds):** -```bash -python -m unittest tests.test_storage_control_stall.TestStorageControlStall.test_list_folders_stall -v -``` - -**Rename with stall (6 seconds):** -```bash -python -m unittest tests.test_storage_control_stall.TestStorageControlStall.test_rename_folder_stall -v -``` - -### Full Test Suite - -Run all tests at once (takes ~30+ seconds): -```bash -python -m unittest tests.test_storage_control_stall -v -``` - -**Note:** The `test_create_folder_stall_always` test takes 10+ seconds as it uses the default stall duration. - -## Manual Testing with gRPC Client - -You can also test using a Python gRPC client: - -```python -import grpc -from google.storage.control.v2 import storage_control_pb2, storage_control_pb2_grpc -import time - -# Connect to testbench -channel = grpc.insecure_channel('localhost:9099') # Use actual gRPC port -stub = storage_control_pb2_grpc.StorageControlStub(channel) - -# Test with stall-for-5s instruction -metadata = [('x-goog-emulator-instructions', 'stall-for-5s')] -request = storage_control_pb2.CreateFolderRequest() -request.parent = "projects/_/buckets/test-bucket" -request.folder_id = "my-test-folder" - -start = time.time() -folder = stub.CreateFolder(request, metadata=metadata) -elapsed = time.time() - start - -print(f"Request took {elapsed:.2f} seconds") -print(f"Created folder: {folder.name}") -``` - -## Expected Test Results - -✅ **Success Criteria:** -- Non-stall tests complete in < 1 second -- `stall-always` tests take >= 10 seconds -- `stall-for-Ns` tests take >= N seconds (within 1-2 seconds tolerance) -- Folder operations (create, delete, get, list, rename) all work correctly -- Stall applies before the operation executes - -## Integration with Existing Tests - -The implementation doesn't affect existing Storage API tests. You can verify this by running: - -```bash -python -m unittest discover -s tests/ -p "test_grpc_server.py" -v -``` - -This should pass without any issues. - -## Files Modified - -- `update-protos.sh`: Added storage_control.proto to the generation list -- `testbench/database.py`: Added folder storage methods -- `testbench/grpc_server.py`: Added StorageControlServicer with stall support -- `tests/test_storage_control_stall.py`: Comprehensive test suite for stall functionality -- `google/storage/control/v2/`: Generated protobuf files (storage_control_pb2.py, storage_control_pb2_grpc.py) - -## Troubleshooting - -**Issue: ImportError for storage_control_pb2** -- Solution: Regenerate protobuf files: `source ../venv-storage-testbench/bin/activate && bash update-protos.sh` - -**Issue: Tests timing out** -- Solution: Increase timeout values in test runner, stall tests are intentionally slow - -**Issue: "Protocol message Folder has no field 'bucket'"** -- Solution: This was fixed - Folder only has fields: name, metageneration, create_time, update_time, pending_rename_info - -## Architecture Notes - -The stall implementation uses `time.sleep()` which blocks the current thread. This is appropriate for: -- Testing timeout handling -- Simulating slow backends -- Reproducing transient network conditions - -The implementation extracts stall instructions from gRPC metadata via `testbench.common.extract_instruction()`, which checks for the `x-goog-emulator-instructions` header in the invocation metadata. diff --git a/google/storage/control/__init__.py b/google/storage/control/__init__.py index c6334245..58d482ea 100644 --- a/google/storage/control/__init__.py +++ b/google/storage/control/__init__.py @@ -1,4 +1,4 @@ -# Copyright 2020 Google LLC +# 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. diff --git a/google/storage/control/v2/__init__.py b/google/storage/control/v2/__init__.py deleted file mode 100644 index c6334245..00000000 --- a/google/storage/control/v2/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright 2020 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. diff --git a/testbench/grpc_server.py b/testbench/grpc_server.py index c59bfc10..9853e065 100644 --- a/testbench/grpc_server.py +++ b/testbench/grpc_server.py @@ -1182,11 +1182,9 @@ def _apply_stall(self, context): instruction = testbench.common.extract_instruction(None, context) if instruction and "stall" in instruction: - # Parse stall instruction (e.g., "stall-always", "stall-for-10s") - if instruction.startswith("stall-always"): - time.sleep(10) - elif instruction.startswith("stall-for-"): - # Parse "stall-for-10s" format + # Parse stall instruction (e.g., "stall-for-1s") + if instruction.startswith("stall-for-"): + # Parse "stall-for-1s" format match = re.match(r'stall-for-(\d+)s', instruction) if match: time.sleep(int(match.group(1))) diff --git a/tests/test_storage_control_stall.py b/tests/test_storage_control_stall.py index 7b9994a0..54c3eb29 100644 --- a/tests/test_storage_control_stall.py +++ b/tests/test_storage_control_stall.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright 2020 Google LLC +# 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. @@ -59,13 +59,13 @@ def test_create_folder_no_stall(self): self.assertEqual(folder.name, "projects/_/buckets/test-bucket/folders/test-folder") self.assertLess(elapsed, 1.0, "Should complete quickly without stall") - def test_create_folder_stall_always(self): - """Test folder creation with stall-always instruction.""" + def test_create_folder_stall_1s(self): + """Test folder creation with 1s stall instruction.""" request = storage_control_pb2.CreateFolderRequest() request.parent = "projects/_/buckets/test-bucket" request.folder_id = "test-folder-stall" - metadata = [("x-goog-emulator-instructions", "stall-always")] + metadata = [("x-goog-emulator-instructions", "stall-for-1s")] context = self.mock_context(metadata) start_time = time.time() @@ -74,7 +74,7 @@ def test_create_folder_stall_always(self): self.assertIsNotNone(folder) self.assertEqual(folder.name, "projects/_/buckets/test-bucket/folders/test-folder-stall") - self.assertGreaterEqual(elapsed, 10.0, "Should stall for at least 10 seconds") + self.assertGreaterEqual(elapsed, 1.0, "Should stall for at least 1 second") def test_create_folder_stall_custom_duration(self): """Test folder creation with custom stall duration.""" @@ -82,7 +82,7 @@ def test_create_folder_stall_custom_duration(self): request.parent = "projects/_/buckets/test-bucket" request.folder_id = "test-folder-custom-stall" - metadata = [("x-goog-emulator-instructions", "stall-for-3s")] + metadata = [("x-goog-emulator-instructions", "stall-for-2s")] context = self.mock_context(metadata) start_time = time.time() @@ -91,8 +91,8 @@ def test_create_folder_stall_custom_duration(self): self.assertIsNotNone(folder) self.assertEqual(folder.name, "projects/_/buckets/test-bucket/folders/test-folder-custom-stall") - self.assertGreaterEqual(elapsed, 3.0, "Should stall for at least 3 seconds") - self.assertLess(elapsed, 5.0, "Should not stall longer than 5 seconds") + self.assertGreaterEqual(elapsed, 2.0, "Should stall for at least 2 seconds") + self.assertLess(elapsed, 3.0, "Should not stall longer than 3 seconds") def test_delete_folder_stall(self): """Test folder deletion with stall instruction.""" @@ -105,9 +105,9 @@ def test_delete_folder_stall(self): # Now delete it with stall delete_request = storage_control_pb2.DeleteFolderRequest() - delete_request.name = "projects/_/buckets/test-bucket/test-folder-delete" + delete_request.name = "projects/_/buckets/test-bucket/folders/test-folder-delete" - metadata = [("x-goog-emulator-instructions", "stall-for-2s")] + metadata = [("x-goog-emulator-instructions", "stall-for-1s")] context_stall = self.mock_context(metadata) start_time = time.time() @@ -115,7 +115,7 @@ def test_delete_folder_stall(self): elapsed = time.time() - start_time self.assertIsInstance(result, empty_pb2.Empty) - self.assertGreaterEqual(elapsed, 2.0, "Should stall for at least 2 seconds") + self.assertGreaterEqual(elapsed, 1.0, "Should stall for at least 1 second") def test_get_folder_stall(self): """Test get folder with stall instruction.""" @@ -130,7 +130,7 @@ def test_get_folder_stall(self): get_request = storage_control_pb2.GetFolderRequest() get_request.name = "projects/_/buckets/test-bucket/folders/test-folder-get" - metadata = [("x-goog-emulator-instructions", "stall-for-5s")] + metadata = [("x-goog-emulator-instructions", "stall-for-1s")] context_stall = self.mock_context(metadata) start_time = time.time() @@ -139,7 +139,7 @@ def test_get_folder_stall(self): self.assertIsNotNone(folder) self.assertEqual(folder.name, "projects/_/buckets/test-bucket/folders/test-folder-get") - self.assertGreaterEqual(elapsed, 5.0, "Should stall for at least 5 seconds") + self.assertGreaterEqual(elapsed, 1.0, "Should stall for at least 1 second") def test_list_folders_stall(self): """Test list folders with stall instruction.""" @@ -155,7 +155,7 @@ def test_list_folders_stall(self): list_request = storage_control_pb2.ListFoldersRequest() list_request.parent = "projects/_/buckets/test-bucket" - metadata = [("x-goog-emulator-instructions", "stall-for-4s")] + metadata = [("x-goog-emulator-instructions", "stall-for-1s")] context_stall = self.mock_context(metadata) start_time = time.time() @@ -164,7 +164,7 @@ def test_list_folders_stall(self): self.assertIsNotNone(response) self.assertGreaterEqual(len(response.folders), 3) - self.assertGreaterEqual(elapsed, 4.0, "Should stall for at least 4 seconds") + self.assertGreaterEqual(elapsed, 1.0, "Should stall for at least 1 second") def test_rename_folder_stall(self): """Test rename folder with stall instruction.""" @@ -177,10 +177,10 @@ def test_rename_folder_stall(self): # Rename with stall rename_request = storage_control_pb2.RenameFolderRequest() - rename_request.name = "projects/_/buckets/test-bucket/test-folder-rename-src" + rename_request.name = "projects/_/buckets/test-bucket/folders/test-folder-rename-src" rename_request.destination_folder_id = "projects/_/buckets/test-bucket/test-folder-rename-dst" - metadata = [("x-goog-emulator-instructions", "stall-for-6s")] + metadata = [("x-goog-emulator-instructions", "stall-for-1s")] context_stall = self.mock_context(metadata) start_time = time.time() @@ -188,7 +188,52 @@ def test_rename_folder_stall(self): elapsed = time.time() - start_time self.assertIsNotNone(folder) - self.assertGreaterEqual(elapsed, 6.0, "Should stall for at least 6 seconds") + self.assertGreaterEqual(elapsed, 1.0, "Should stall for at least 1 second") + + def test_multiple_stalls_and_no_stall(self): + """Test that stall happens twice with metadata and not without.""" + # First call with stall metadata + request1 = storage_control_pb2.CreateFolderRequest() + request1.parent = "projects/_/buckets/test-bucket" + request1.folder_id = "test-folder-multi-1" + + metadata = [("x-goog-emulator-instructions", "stall-for-1s")] + context_stall = self.mock_context(metadata) + + start_time = time.time() + folder1 = self.servicer.CreateFolder(request1, context_stall) + elapsed1 = time.time() - start_time + + self.assertIsNotNone(folder1) + self.assertGreaterEqual(elapsed1, 1.0, "First call should stall for at least 1 second") + + # Second call with stall metadata + request2 = storage_control_pb2.CreateFolderRequest() + request2.parent = "projects/_/buckets/test-bucket" + request2.folder_id = "test-folder-multi-2" + + context_stall2 = self.mock_context(metadata) + + start_time = time.time() + folder2 = self.servicer.CreateFolder(request2, context_stall2) + elapsed2 = time.time() - start_time + + self.assertIsNotNone(folder2) + self.assertGreaterEqual(elapsed2, 1.0, "Second call should stall for at least 1 second") + + # Third call without stall metadata + request3 = storage_control_pb2.CreateFolderRequest() + request3.parent = "projects/_/buckets/test-bucket" + request3.folder_id = "test-folder-multi-3" + + context_no_stall = self.mock_context() + + start_time = time.time() + folder3 = self.servicer.CreateFolder(request3, context_no_stall) + elapsed3 = time.time() - start_time + + self.assertIsNotNone(folder3) + self.assertLess(elapsed3, 1.0, "Third call should complete quickly without stall") if __name__ == "__main__": From 61fb8f3117cb137ab121701745cc066eeecc858d Mon Sep 17 00:00:00 2001 From: raj-prince Date: Tue, 17 Mar 2026 07:01:39 +0000 Subject: [PATCH 03/13] adding GetStorageLayout API support --- README.md | 6 ++++- testbench/grpc_server.py | 13 +++++++++++ tests/test_storage_control_stall.py | 34 +++++++++++++++++++++++++++++ 3 files changed, 52 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 108f8cd7..7b77857f 100644 --- a/README.md +++ b/README.md @@ -279,7 +279,11 @@ curl -H "x-retry-test-id: 1d05c20627844214a9ff7cbcf696317d" "http://localhost:91 ## Storage Control API Stall Support -The testbench supports stall functionality for the Storage Control API (gRPC only) to test client retry behavior. All folder operations (`CreateFolder`, `DeleteFolder`, `GetFolder`, `ListFolders`, `RenameFolder`) can be delayed using the `x-goog-emulator-instructions` metadata header. +The testbench supports stall functionality for the Storage Control API (gRPC only) to test client retry behavior. All folder operations and storage layout operations can be delayed using the `x-goog-emulator-instructions` metadata header. + +**Supported operations:** +- **Folder operations:** `CreateFolder`, `DeleteFolder`, `GetFolder`, `ListFolders`, `RenameFolder` +- **Storage layout operations:** `GetStorageLayout` > **Note:** The Storage Control API uses the **same gRPC server** as the Storage API. Both services are available on the same port (e.g., port 8888 if started with `curl "http://localhost:9000/start_grpc?port=8888"`). diff --git a/testbench/grpc_server.py b/testbench/grpc_server.py index 9853e065..0a76f862 100644 --- a/testbench/grpc_server.py +++ b/testbench/grpc_server.py @@ -1236,6 +1236,19 @@ def RenameFolder(self, request, context): folder = self.db.rename_folder(src_folder, dst_folder, context) return folder + @retry_test(method="storage.storageLayout.get") + def GetStorageLayout(self, request, context): + self._apply_stall(context) + # Create a simple storage layout response + layout = storage_control_pb2.StorageLayout() + layout.name = request.name + # Set default location and location_type + layout.location = "US" + layout.location_type = "multi-region" + # Optionally set hierarchical namespace enabled flag + layout.hierarchical_namespace.enabled = False + return layout + def run(port, database, echo_metadata=False): server = grpc.server( diff --git a/tests/test_storage_control_stall.py b/tests/test_storage_control_stall.py index 54c3eb29..1fad3472 100644 --- a/tests/test_storage_control_stall.py +++ b/tests/test_storage_control_stall.py @@ -235,6 +235,40 @@ def test_multiple_stalls_and_no_stall(self): self.assertIsNotNone(folder3) self.assertLess(elapsed3, 1.0, "Third call should complete quickly without stall") + def test_get_storage_layout_no_stall(self): + """Test get storage layout without stall instruction.""" + request = storage_control_pb2.GetStorageLayoutRequest() + request.name = "projects/_/buckets/test-bucket/storageLayout" + + context = self.mock_context() + + start_time = time.time() + layout = self.servicer.GetStorageLayout(request, context) + elapsed = time.time() - start_time + + self.assertIsNotNone(layout) + self.assertEqual(layout.name, "projects/_/buckets/test-bucket/storageLayout") + self.assertLess(elapsed, 1.0, "Should complete quickly without stall") + + def test_get_storage_layout_stall(self): + """Test get storage layout with stall instruction.""" + request = storage_control_pb2.GetStorageLayoutRequest() + request.name = "projects/_/buckets/test-bucket/storageLayout" + + metadata = [("x-goog-emulator-instructions", "stall-for-1s")] + context_stall = self.mock_context(metadata) + + start_time = time.time() + layout = self.servicer.GetStorageLayout(request, context_stall) + elapsed = time.time() - start_time + + self.assertIsNotNone(layout) + self.assertEqual(layout.name, "projects/_/buckets/test-bucket/storageLayout") + self.assertEqual(layout.location, "US") + self.assertEqual(layout.location_type, "multi-region") + self.assertFalse(layout.hierarchical_namespace.enabled) + self.assertGreaterEqual(elapsed, 1.0, "Should stall for at least 1 second") + if __name__ == "__main__": unittest.main() From e64634c282399c3f21207de7080940e6727a0a00 Mon Sep 17 00:00:00 2001 From: raj-prince Date: Tue, 17 Mar 2026 07:04:54 +0000 Subject: [PATCH 04/13] fixing lint issue --- testbench/grpc_server.py | 17 ++-- tests/test_storage_control_stall.py | 125 ++++++++++++++++------------ 2 files changed, 83 insertions(+), 59 deletions(-) diff --git a/testbench/grpc_server.py b/testbench/grpc_server.py index 0a76f862..78c293e2 100644 --- a/testbench/grpc_server.py +++ b/testbench/grpc_server.py @@ -541,7 +541,10 @@ def precondition(_, live_version, ctx): bucket = self.db.get_bucket(request.destination.bucket, context).metadata metadata = storage_pb2.Object() metadata.MergeFrom(request.destination) - (blob, _,) = gcs.object.Object.init( + ( + blob, + _, + ) = gcs.object.Object.init( request, metadata, composed_media, bucket, True, context ) self.db.insert_object( @@ -1179,13 +1182,13 @@ def __init__(self, db, echo_metadata=False): def _apply_stall(self, context): """Check for stall instructions and apply delay if needed.""" import time - + instruction = testbench.common.extract_instruction(None, context) if instruction and "stall" in instruction: # Parse stall instruction (e.g., "stall-for-1s") if instruction.startswith("stall-for-"): # Parse "stall-for-1s" format - match = re.match(r'stall-for-(\d+)s', instruction) + match = re.match(r"stall-for-(\d+)s", instruction) if match: time.sleep(int(match.group(1))) @@ -1199,7 +1202,7 @@ def CreateFolder(self, request, context): folder.metageneration = 1 folder.create_time.FromDatetime(datetime.datetime.now(datetime.timezone.utc)) folder.update_time.CopyFrom(folder.create_time) - + # Store in database using full name as key self.db.insert_folder(folder.name, folder, context) return folder @@ -1222,8 +1225,8 @@ def ListFolders(self, request, context): self._apply_stall(context) # Extract bucket from parent (format: "projects/_/buckets/{bucket}") bucket_name = request.parent - prefix = request.prefix if hasattr(request, 'prefix') else "" - + prefix = request.prefix if hasattr(request, "prefix") else "" + folders = self.db.list_folders(bucket_name, prefix, context) return storage_control_pb2.ListFoldersResponse(folders=folders) @@ -1232,7 +1235,7 @@ def RenameFolder(self, request, context): self._apply_stall(context) src_folder = request.name dst_folder = request.destination_folder_id - + folder = self.db.rename_folder(src_folder, dst_folder, context) return folder diff --git a/tests/test_storage_control_stall.py b/tests/test_storage_control_stall.py index 1fad3472..507cd358 100644 --- a/tests/test_storage_control_stall.py +++ b/tests/test_storage_control_stall.py @@ -48,15 +48,17 @@ def test_create_folder_no_stall(self): request = storage_control_pb2.CreateFolderRequest() request.parent = "projects/_/buckets/test-bucket" request.folder_id = "test-folder" - + context = self.mock_context() - + start_time = time.time() folder = self.servicer.CreateFolder(request, context) elapsed = time.time() - start_time - + self.assertIsNotNone(folder) - self.assertEqual(folder.name, "projects/_/buckets/test-bucket/folders/test-folder") + self.assertEqual( + folder.name, "projects/_/buckets/test-bucket/folders/test-folder" + ) self.assertLess(elapsed, 1.0, "Should complete quickly without stall") def test_create_folder_stall_1s(self): @@ -64,16 +66,18 @@ def test_create_folder_stall_1s(self): request = storage_control_pb2.CreateFolderRequest() request.parent = "projects/_/buckets/test-bucket" request.folder_id = "test-folder-stall" - + metadata = [("x-goog-emulator-instructions", "stall-for-1s")] context = self.mock_context(metadata) - + start_time = time.time() folder = self.servicer.CreateFolder(request, context) elapsed = time.time() - start_time - + self.assertIsNotNone(folder) - self.assertEqual(folder.name, "projects/_/buckets/test-bucket/folders/test-folder-stall") + self.assertEqual( + folder.name, "projects/_/buckets/test-bucket/folders/test-folder-stall" + ) self.assertGreaterEqual(elapsed, 1.0, "Should stall for at least 1 second") def test_create_folder_stall_custom_duration(self): @@ -81,16 +85,19 @@ def test_create_folder_stall_custom_duration(self): request = storage_control_pb2.CreateFolderRequest() request.parent = "projects/_/buckets/test-bucket" request.folder_id = "test-folder-custom-stall" - + metadata = [("x-goog-emulator-instructions", "stall-for-2s")] context = self.mock_context(metadata) - + start_time = time.time() folder = self.servicer.CreateFolder(request, context) elapsed = time.time() - start_time - + self.assertIsNotNone(folder) - self.assertEqual(folder.name, "projects/_/buckets/test-bucket/folders/test-folder-custom-stall") + self.assertEqual( + folder.name, + "projects/_/buckets/test-bucket/folders/test-folder-custom-stall", + ) self.assertGreaterEqual(elapsed, 2.0, "Should stall for at least 2 seconds") self.assertLess(elapsed, 3.0, "Should not stall longer than 3 seconds") @@ -102,18 +109,20 @@ def test_delete_folder_stall(self): create_request.folder_id = "test-folder-delete" context = self.mock_context() self.servicer.CreateFolder(create_request, context) - + # Now delete it with stall delete_request = storage_control_pb2.DeleteFolderRequest() - delete_request.name = "projects/_/buckets/test-bucket/folders/test-folder-delete" - + delete_request.name = ( + "projects/_/buckets/test-bucket/folders/test-folder-delete" + ) + metadata = [("x-goog-emulator-instructions", "stall-for-1s")] context_stall = self.mock_context(metadata) - + start_time = time.time() result = self.servicer.DeleteFolder(delete_request, context_stall) elapsed = time.time() - start_time - + self.assertIsInstance(result, empty_pb2.Empty) self.assertGreaterEqual(elapsed, 1.0, "Should stall for at least 1 second") @@ -125,20 +134,22 @@ def test_get_folder_stall(self): create_request.folder_id = "test-folder-get" context = self.mock_context() created_folder = self.servicer.CreateFolder(create_request, context) - + # Now get it with stall get_request = storage_control_pb2.GetFolderRequest() get_request.name = "projects/_/buckets/test-bucket/folders/test-folder-get" - + metadata = [("x-goog-emulator-instructions", "stall-for-1s")] context_stall = self.mock_context(metadata) - + start_time = time.time() folder = self.servicer.GetFolder(get_request, context_stall) elapsed = time.time() - start_time - + self.assertIsNotNone(folder) - self.assertEqual(folder.name, "projects/_/buckets/test-bucket/folders/test-folder-get") + self.assertEqual( + folder.name, "projects/_/buckets/test-bucket/folders/test-folder-get" + ) self.assertGreaterEqual(elapsed, 1.0, "Should stall for at least 1 second") def test_list_folders_stall(self): @@ -150,18 +161,18 @@ def test_list_folders_stall(self): create_request.folder_id = f"test-folder-list-{i}" context = self.mock_context() self.servicer.CreateFolder(create_request, context) - + # List with stall list_request = storage_control_pb2.ListFoldersRequest() list_request.parent = "projects/_/buckets/test-bucket" - + metadata = [("x-goog-emulator-instructions", "stall-for-1s")] context_stall = self.mock_context(metadata) - + start_time = time.time() response = self.servicer.ListFolders(list_request, context_stall) elapsed = time.time() - start_time - + self.assertIsNotNone(response) self.assertGreaterEqual(len(response.folders), 3) self.assertGreaterEqual(elapsed, 1.0, "Should stall for at least 1 second") @@ -174,19 +185,23 @@ def test_rename_folder_stall(self): create_request.folder_id = "test-folder-rename-src" context = self.mock_context() self.servicer.CreateFolder(create_request, context) - + # Rename with stall rename_request = storage_control_pb2.RenameFolderRequest() - rename_request.name = "projects/_/buckets/test-bucket/folders/test-folder-rename-src" - rename_request.destination_folder_id = "projects/_/buckets/test-bucket/test-folder-rename-dst" - + rename_request.name = ( + "projects/_/buckets/test-bucket/folders/test-folder-rename-src" + ) + rename_request.destination_folder_id = ( + "projects/_/buckets/test-bucket/test-folder-rename-dst" + ) + metadata = [("x-goog-emulator-instructions", "stall-for-1s")] context_stall = self.mock_context(metadata) - + start_time = time.time() folder = self.servicer.RenameFolder(rename_request, context_stall) elapsed = time.time() - start_time - + self.assertIsNotNone(folder) self.assertGreaterEqual(elapsed, 1.0, "Should stall for at least 1 second") @@ -196,56 +211,62 @@ def test_multiple_stalls_and_no_stall(self): request1 = storage_control_pb2.CreateFolderRequest() request1.parent = "projects/_/buckets/test-bucket" request1.folder_id = "test-folder-multi-1" - + metadata = [("x-goog-emulator-instructions", "stall-for-1s")] context_stall = self.mock_context(metadata) - + start_time = time.time() folder1 = self.servicer.CreateFolder(request1, context_stall) elapsed1 = time.time() - start_time - + self.assertIsNotNone(folder1) - self.assertGreaterEqual(elapsed1, 1.0, "First call should stall for at least 1 second") - + self.assertGreaterEqual( + elapsed1, 1.0, "First call should stall for at least 1 second" + ) + # Second call with stall metadata request2 = storage_control_pb2.CreateFolderRequest() request2.parent = "projects/_/buckets/test-bucket" request2.folder_id = "test-folder-multi-2" - + context_stall2 = self.mock_context(metadata) - + start_time = time.time() folder2 = self.servicer.CreateFolder(request2, context_stall2) elapsed2 = time.time() - start_time - + self.assertIsNotNone(folder2) - self.assertGreaterEqual(elapsed2, 1.0, "Second call should stall for at least 1 second") - + self.assertGreaterEqual( + elapsed2, 1.0, "Second call should stall for at least 1 second" + ) + # Third call without stall metadata request3 = storage_control_pb2.CreateFolderRequest() request3.parent = "projects/_/buckets/test-bucket" request3.folder_id = "test-folder-multi-3" - + context_no_stall = self.mock_context() - + start_time = time.time() folder3 = self.servicer.CreateFolder(request3, context_no_stall) elapsed3 = time.time() - start_time - + self.assertIsNotNone(folder3) - self.assertLess(elapsed3, 1.0, "Third call should complete quickly without stall") + self.assertLess( + elapsed3, 1.0, "Third call should complete quickly without stall" + ) def test_get_storage_layout_no_stall(self): """Test get storage layout without stall instruction.""" request = storage_control_pb2.GetStorageLayoutRequest() request.name = "projects/_/buckets/test-bucket/storageLayout" - + context = self.mock_context() - + start_time = time.time() layout = self.servicer.GetStorageLayout(request, context) elapsed = time.time() - start_time - + self.assertIsNotNone(layout) self.assertEqual(layout.name, "projects/_/buckets/test-bucket/storageLayout") self.assertLess(elapsed, 1.0, "Should complete quickly without stall") @@ -254,14 +275,14 @@ def test_get_storage_layout_stall(self): """Test get storage layout with stall instruction.""" request = storage_control_pb2.GetStorageLayoutRequest() request.name = "projects/_/buckets/test-bucket/storageLayout" - + metadata = [("x-goog-emulator-instructions", "stall-for-1s")] context_stall = self.mock_context(metadata) - + start_time = time.time() layout = self.servicer.GetStorageLayout(request, context_stall) elapsed = time.time() - start_time - + self.assertIsNotNone(layout) self.assertEqual(layout.name, "projects/_/buckets/test-bucket/storageLayout") self.assertEqual(layout.location, "US") From 6f5a9d42f24c644fdc95aab8217ab80af1e60f84 Mon Sep 17 00:00:00 2001 From: Nitin Garg Date: Thu, 26 Mar 2026 06:48:00 +0000 Subject: [PATCH 05/13] fix in setup.py --- setup.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/setup.py b/setup.py index 979e13e4..3b642d6b 100644 --- a/setup.py +++ b/setup.py @@ -35,6 +35,10 @@ "Operating System :: OS Independent", ], packages=[ + "google", + "google/storage", + "google/storage/control", + "google/storage/control/v2", "google/storage/v2", "google/iam/v1", "testbench", From ce08859b4016f3c9895d57984bc584fb60ac7bfe Mon Sep 17 00:00:00 2001 From: Nitin Garg Date: Thu, 26 Mar 2026 07:17:13 +0000 Subject: [PATCH 06/13] fix code formatting --- gcs/project.py | 6 +++--- testbench/grpc_server.py | 7 ++----- testbench/rest_server.py | 4 ++-- tests/test_storage_control_stall.py | 2 +- 4 files changed, 8 insertions(+), 11 deletions(-) diff --git a/gcs/project.py b/gcs/project.py index e8bd9ed2..e15af26f 100644 --- a/gcs/project.py +++ b/gcs/project.py @@ -167,7 +167,7 @@ def service_account(self, service_account_email): def delete_hmac_key(self, access_id, context=None): """Remove a key from the project.""" - (service_account, key_id) = access_id.split(":", 2) + service_account, key_id = access_id.split(":", 2) sa = self.service_accounts.get(service_account) if sa is None: return testbench.error.notfound( @@ -177,7 +177,7 @@ def delete_hmac_key(self, access_id, context=None): def get_hmac_key(self, access_id, context=None): """Get an existing key in the project.""" - (service_account, key_id) = access_id.split(":", 2) + service_account, key_id = access_id.split(":", 2) sa = self.service_accounts.get(service_account) if sa is None: return testbench.error.notfound( @@ -187,7 +187,7 @@ def get_hmac_key(self, access_id, context=None): def update_hmac_key(self, access_id, payload, context=None): """Update an existing key in the project.""" - (service_account, key_id) = access_id.split(":", 2) + service_account, key_id = access_id.split(":", 2) sa = self.service_accounts.get(service_account, None) if sa is None: return testbench.error.notfound( diff --git a/testbench/grpc_server.py b/testbench/grpc_server.py index 78c293e2..16cbbdc0 100644 --- a/testbench/grpc_server.py +++ b/testbench/grpc_server.py @@ -39,8 +39,8 @@ import gcs import testbench from google.iam.v1 import iam_policy_pb2 -from google.storage.v2 import storage_pb2, storage_pb2_grpc from google.storage.control.v2 import storage_control_pb2, storage_control_pb2_grpc +from google.storage.v2 import storage_pb2, storage_pb2_grpc _GRPC_SERVER_THREAD_COUNT = 2 @@ -541,10 +541,7 @@ def precondition(_, live_version, ctx): bucket = self.db.get_bucket(request.destination.bucket, context).metadata metadata = storage_pb2.Object() metadata.MergeFrom(request.destination) - ( - blob, - _, - ) = gcs.object.Object.init( + (blob, _,) = gcs.object.Object.init( request, metadata, composed_media, bucket, True, context ) self.db.insert_object( diff --git a/testbench/rest_server.py b/testbench/rest_server.py index ca6c123d..b08515fe 100644 --- a/testbench/rest_server.py +++ b/testbench/rest_server.py @@ -1247,10 +1247,10 @@ def delete_resumable_upload(bucket_name): # === SERVER === # # Define the WSGI application to handle HMAC key and service account requests -(PROJECTS_HANDLER_PATH, projects_app) = projects_rest_server.get_projects_app(db) +PROJECTS_HANDLER_PATH, projects_app = projects_rest_server.get_projects_app(db) # Define the WSGI application to handle IAM requests -(IAM_HANDLER_PATH, iam_app) = iam_rest_server.get_iam_app() +IAM_HANDLER_PATH, iam_app = iam_rest_server.get_iam_app() server = flask.Flask(__name__) server.debug = False diff --git a/tests/test_storage_control_stall.py b/tests/test_storage_control_stall.py index 507cd358..25f2eee2 100644 --- a/tests/test_storage_control_stall.py +++ b/tests/test_storage_control_stall.py @@ -20,9 +20,9 @@ import unittest.mock import grpc +from google.protobuf import empty_pb2 import testbench -from google.protobuf import empty_pb2 from google.storage.control.v2 import storage_control_pb2 From 69876ea96ff14d213d56866b61dd52a3020b6f0e Mon Sep 17 00:00:00 2001 From: Nitin Garg Date: Wed, 1 Apr 2026 05:37:53 +0000 Subject: [PATCH 07/13] add Prince review comment --- gcs/project.py | 6 +++--- tests/test_storage_control_stall.py | 21 --------------------- 2 files changed, 3 insertions(+), 24 deletions(-) diff --git a/gcs/project.py b/gcs/project.py index e15af26f..e8bd9ed2 100644 --- a/gcs/project.py +++ b/gcs/project.py @@ -167,7 +167,7 @@ def service_account(self, service_account_email): def delete_hmac_key(self, access_id, context=None): """Remove a key from the project.""" - service_account, key_id = access_id.split(":", 2) + (service_account, key_id) = access_id.split(":", 2) sa = self.service_accounts.get(service_account) if sa is None: return testbench.error.notfound( @@ -177,7 +177,7 @@ def delete_hmac_key(self, access_id, context=None): def get_hmac_key(self, access_id, context=None): """Get an existing key in the project.""" - service_account, key_id = access_id.split(":", 2) + (service_account, key_id) = access_id.split(":", 2) sa = self.service_accounts.get(service_account) if sa is None: return testbench.error.notfound( @@ -187,7 +187,7 @@ def get_hmac_key(self, access_id, context=None): def update_hmac_key(self, access_id, payload, context=None): """Update an existing key in the project.""" - service_account, key_id = access_id.split(":", 2) + (service_account, key_id) = access_id.split(":", 2) sa = self.service_accounts.get(service_account, None) if sa is None: return testbench.error.notfound( diff --git a/tests/test_storage_control_stall.py b/tests/test_storage_control_stall.py index 25f2eee2..f5dd4e9c 100644 --- a/tests/test_storage_control_stall.py +++ b/tests/test_storage_control_stall.py @@ -80,27 +80,6 @@ def test_create_folder_stall_1s(self): ) self.assertGreaterEqual(elapsed, 1.0, "Should stall for at least 1 second") - def test_create_folder_stall_custom_duration(self): - """Test folder creation with custom stall duration.""" - request = storage_control_pb2.CreateFolderRequest() - request.parent = "projects/_/buckets/test-bucket" - request.folder_id = "test-folder-custom-stall" - - metadata = [("x-goog-emulator-instructions", "stall-for-2s")] - context = self.mock_context(metadata) - - start_time = time.time() - folder = self.servicer.CreateFolder(request, context) - elapsed = time.time() - start_time - - self.assertIsNotNone(folder) - self.assertEqual( - folder.name, - "projects/_/buckets/test-bucket/folders/test-folder-custom-stall", - ) - self.assertGreaterEqual(elapsed, 2.0, "Should stall for at least 2 seconds") - self.assertLess(elapsed, 3.0, "Should not stall longer than 3 seconds") - def test_delete_folder_stall(self): """Test folder deletion with stall instruction.""" # First create a folder From abcb078c4e12ae7aeb433cbf025fd27681c3f124 Mon Sep 17 00:00:00 2001 From: Nitin Garg Date: Wed, 1 Apr 2026 06:37:07 +0000 Subject: [PATCH 08/13] Changed control-client unit tests to table-based as per review comment given by Prince. Also added a new unit test to test a bit more complex scenario of first stalling 2 times, and then trying out without stall to see that it works. --- tests/test_storage_control_stall.py | 456 +++++++++++++++------------- 1 file changed, 247 insertions(+), 209 deletions(-) diff --git a/tests/test_storage_control_stall.py b/tests/test_storage_control_stall.py index f5dd4e9c..a5977238 100644 --- a/tests/test_storage_control_stall.py +++ b/tests/test_storage_control_stall.py @@ -15,9 +15,11 @@ """Test stall functionality for Storage Control API.""" +import dataclasses import time import unittest import unittest.mock +from typing import Any, Callable, Optional import grpc from google.protobuf import empty_pb2 @@ -26,6 +28,18 @@ from google.storage.control.v2 import storage_control_pb2 +@dataclasses.dataclass +class StallTestCase: + name: str + method_name: str + request: Any + instructions: Optional[str] + min_duration: Optional[float] + max_duration: Optional[float] + setup_func: Optional[Callable[["TestStorageControlStall"], None]] = None + verify_func: Optional[Callable[["TestStorageControlStall", Any], None]] = None + + class TestStorageControlStall(unittest.TestCase): """Test cases for Storage Control API stall functionality.""" @@ -43,231 +57,255 @@ def setUp(self): self.db, echo_metadata=False ) - def test_create_folder_no_stall(self): - """Test folder creation without stall instruction.""" - request = storage_control_pb2.CreateFolderRequest() - request.parent = "projects/_/buckets/test-bucket" - request.folder_id = "test-folder" - - context = self.mock_context() - - start_time = time.time() - folder = self.servicer.CreateFolder(request, context) - elapsed = time.time() - start_time - - self.assertIsNotNone(folder) - self.assertEqual( - folder.name, "projects/_/buckets/test-bucket/folders/test-folder" - ) - self.assertLess(elapsed, 1.0, "Should complete quickly without stall") - - def test_create_folder_stall_1s(self): - """Test folder creation with 1s stall instruction.""" - request = storage_control_pb2.CreateFolderRequest() - request.parent = "projects/_/buckets/test-bucket" - request.folder_id = "test-folder-stall" - - metadata = [("x-goog-emulator-instructions", "stall-for-1s")] - context = self.mock_context(metadata) - - start_time = time.time() - folder = self.servicer.CreateFolder(request, context) - elapsed = time.time() - start_time - - self.assertIsNotNone(folder) - self.assertEqual( - folder.name, "projects/_/buckets/test-bucket/folders/test-folder-stall" - ) - self.assertGreaterEqual(elapsed, 1.0, "Should stall for at least 1 second") - - def test_delete_folder_stall(self): - """Test folder deletion with stall instruction.""" - # First create a folder - create_request = storage_control_pb2.CreateFolderRequest() - create_request.parent = "projects/_/buckets/test-bucket" - create_request.folder_id = "test-folder-delete" - context = self.mock_context() - self.servicer.CreateFolder(create_request, context) - - # Now delete it with stall - delete_request = storage_control_pb2.DeleteFolderRequest() - delete_request.name = ( - "projects/_/buckets/test-bucket/folders/test-folder-delete" - ) - - metadata = [("x-goog-emulator-instructions", "stall-for-1s")] - context_stall = self.mock_context(metadata) - - start_time = time.time() - result = self.servicer.DeleteFolder(delete_request, context_stall) - elapsed = time.time() - start_time - - self.assertIsInstance(result, empty_pb2.Empty) - self.assertGreaterEqual(elapsed, 1.0, "Should stall for at least 1 second") - - def test_get_folder_stall(self): - """Test get folder with stall instruction.""" - # First create a folder - create_request = storage_control_pb2.CreateFolderRequest() - create_request.parent = "projects/_/buckets/test-bucket" - create_request.folder_id = "test-folder-get" - context = self.mock_context() - created_folder = self.servicer.CreateFolder(create_request, context) - - # Now get it with stall - get_request = storage_control_pb2.GetFolderRequest() - get_request.name = "projects/_/buckets/test-bucket/folders/test-folder-get" - - metadata = [("x-goog-emulator-instructions", "stall-for-1s")] - context_stall = self.mock_context(metadata) - - start_time = time.time() - folder = self.servicer.GetFolder(get_request, context_stall) - elapsed = time.time() - start_time - - self.assertIsNotNone(folder) - self.assertEqual( - folder.name, "projects/_/buckets/test-bucket/folders/test-folder-get" - ) - self.assertGreaterEqual(elapsed, 1.0, "Should stall for at least 1 second") - - def test_list_folders_stall(self): - """Test list folders with stall instruction.""" - # Create some folders - for i in range(3): - create_request = storage_control_pb2.CreateFolderRequest() - create_request.parent = "projects/_/buckets/test-bucket" - create_request.folder_id = f"test-folder-list-{i}" - context = self.mock_context() - self.servicer.CreateFolder(create_request, context) - - # List with stall - list_request = storage_control_pb2.ListFoldersRequest() - list_request.parent = "projects/_/buckets/test-bucket" - - metadata = [("x-goog-emulator-instructions", "stall-for-1s")] - context_stall = self.mock_context(metadata) - - start_time = time.time() - response = self.servicer.ListFolders(list_request, context_stall) - elapsed = time.time() - start_time - - self.assertIsNotNone(response) - self.assertGreaterEqual(len(response.folders), 3) - self.assertGreaterEqual(elapsed, 1.0, "Should stall for at least 1 second") - - def test_rename_folder_stall(self): - """Test rename folder with stall instruction.""" - # Create a folder - create_request = storage_control_pb2.CreateFolderRequest() - create_request.parent = "projects/_/buckets/test-bucket" - create_request.folder_id = "test-folder-rename-src" - context = self.mock_context() - self.servicer.CreateFolder(create_request, context) - - # Rename with stall - rename_request = storage_control_pb2.RenameFolderRequest() - rename_request.name = ( - "projects/_/buckets/test-bucket/folders/test-folder-rename-src" + def _create_folder(self, folder_id): + request = storage_control_pb2.CreateFolderRequest( + parent="projects/_/buckets/test-bucket", folder_id=folder_id ) - rename_request.destination_folder_id = ( - "projects/_/buckets/test-bucket/test-folder-rename-dst" - ) - - metadata = [("x-goog-emulator-instructions", "stall-for-1s")] - context_stall = self.mock_context(metadata) - - start_time = time.time() - folder = self.servicer.RenameFolder(rename_request, context_stall) - elapsed = time.time() - start_time - - self.assertIsNotNone(folder) - self.assertGreaterEqual(elapsed, 1.0, "Should stall for at least 1 second") + self.servicer.CreateFolder(request, self.mock_context()) + + def test_stall_behaviors(self): + """Table-driven test for various stall behaviors across API methods.""" + test_cases = [ + # CreateFolder + StallTestCase( + name="create_folder_no_stall", + method_name="CreateFolder", + request=storage_control_pb2.CreateFolderRequest( + parent="projects/_/buckets/test-bucket", folder_id="create-no-stall" + ), + instructions=None, + min_duration=None, + max_duration=0.5, + verify_func=lambda self, res: self.assertEqual( + res.name, "projects/_/buckets/test-bucket/folders/create-no-stall" + ), + ), + StallTestCase( + name="create_folder_stall_1s", + method_name="CreateFolder", + request=storage_control_pb2.CreateFolderRequest( + parent="projects/_/buckets/test-bucket", folder_id="create-stall-1s" + ), + instructions="stall-for-1s", + min_duration=1.0, + max_duration=None, + verify_func=lambda self, res: self.assertEqual( + res.name, "projects/_/buckets/test-bucket/folders/create-stall-1s" + ), + ), + # DeleteFolder + StallTestCase( + name="delete_folder_no_stall", + method_name="DeleteFolder", + setup_func=lambda self: self._create_folder("delete-no-stall"), + request=storage_control_pb2.DeleteFolderRequest( + name="projects/_/buckets/test-bucket/folders/delete-no-stall" + ), + instructions=None, + min_duration=None, + max_duration=0.5, + verify_func=lambda self, res: self.assertIsInstance( + res, empty_pb2.Empty + ), + ), + StallTestCase( + name="delete_folder_stall_1s", + method_name="DeleteFolder", + setup_func=lambda self: self._create_folder("delete-stall-1s"), + request=storage_control_pb2.DeleteFolderRequest( + name="projects/_/buckets/test-bucket/folders/delete-stall-1s" + ), + instructions="stall-for-1s", + min_duration=1.0, + max_duration=None, + verify_func=lambda self, res: self.assertIsInstance( + res, empty_pb2.Empty + ), + ), + # GetFolder + StallTestCase( + name="get_folder_no_stall", + method_name="GetFolder", + setup_func=lambda self: self._create_folder("get-no-stall"), + request=storage_control_pb2.GetFolderRequest( + name="projects/_/buckets/test-bucket/folders/get-no-stall" + ), + instructions=None, + min_duration=None, + max_duration=0.5, + verify_func=lambda self, res: self.assertEqual( + res.name, "projects/_/buckets/test-bucket/folders/get-no-stall" + ), + ), + StallTestCase( + name="get_folder_stall_1s", + method_name="GetFolder", + setup_func=lambda self: self._create_folder("get-stall-1s"), + request=storage_control_pb2.GetFolderRequest( + name="projects/_/buckets/test-bucket/folders/get-stall-1s" + ), + instructions="stall-for-1s", + min_duration=1.0, + max_duration=None, + verify_func=lambda self, res: self.assertEqual( + res.name, "projects/_/buckets/test-bucket/folders/get-stall-1s" + ), + ), + # ListFolders + StallTestCase( + name="list_folders_no_stall", + method_name="ListFolders", + setup_func=lambda self: self._create_folder("list-no-stall"), + request=storage_control_pb2.ListFoldersRequest( + parent="projects/_/buckets/test-bucket" + ), + instructions=None, + min_duration=None, + max_duration=0.5, + verify_func=lambda self, res: self.assertGreaterEqual( + len(res.folders), 1 + ), + ), + StallTestCase( + name="list_folders_stall_1s", + method_name="ListFolders", + setup_func=lambda self: self._create_folder("list-stall-1s"), + request=storage_control_pb2.ListFoldersRequest( + parent="projects/_/buckets/test-bucket" + ), + instructions="stall-for-1s", + min_duration=1.0, + max_duration=None, + verify_func=lambda self, res: self.assertGreaterEqual( + len(res.folders), 1 + ), + ), + # RenameFolder + StallTestCase( + name="rename_folder_no_stall", + method_name="RenameFolder", + setup_func=lambda self: self._create_folder("rename-src-no-stall"), + request=storage_control_pb2.RenameFolderRequest( + name="projects/_/buckets/test-bucket/folders/rename-src-no-stall", + destination_folder_id="projects/_/buckets/test-bucket/folders/rename-dst-no-stall", + ), + instructions=None, + min_duration=None, + max_duration=0.5, + verify_func=lambda self, res: self.assertEqual( + res.name, + "projects/_/buckets/test-bucket/folders/rename-src-no-stall", + ), + ), + StallTestCase( + name="rename_folder_stall_1s", + method_name="RenameFolder", + setup_func=lambda self: self._create_folder("rename-src-stall-1s"), + request=storage_control_pb2.RenameFolderRequest( + name="projects/_/buckets/test-bucket/folders/rename-src-stall-1s", + destination_folder_id="projects/_/buckets/test-bucket/folders/rename-dst-stall-1s", + ), + instructions="stall-for-1s", + min_duration=1.0, + max_duration=None, + verify_func=lambda self, res: self.assertEqual( + res.name, + "projects/_/buckets/test-bucket/folders/rename-src-stall-1s", + ), + ), + # GetStorageLayout + StallTestCase( + name="get_storage_layout_no_stall", + method_name="GetStorageLayout", + request=storage_control_pb2.GetStorageLayoutRequest( + name="projects/_/buckets/test-bucket/storageLayout" + ), + instructions=None, + min_duration=None, + max_duration=0.5, + verify_func=lambda self, res: self.assertEqual( + res.name, "projects/_/buckets/test-bucket/storageLayout" + ), + ), + StallTestCase( + name="get_storage_layout_stall_1s", + method_name="GetStorageLayout", + request=storage_control_pb2.GetStorageLayoutRequest( + name="projects/_/buckets/test-bucket/storageLayout" + ), + instructions="stall-for-1s", + min_duration=1.0, + max_duration=None, + verify_func=lambda self, res: self.assertEqual(res.location, "US"), + ), + ] + + for tc in test_cases: + with self.subTest(name=tc.name): + if tc.setup_func: + tc.setup_func(self) + + metadata = ( + [("x-goog-emulator-instructions", tc.instructions)] + if tc.instructions + else [] + ) + context = self.mock_context(metadata) + + # Dynamically get the method from the servicer + method = getattr(self.servicer, tc.method_name) + + start_time = time.time() + result = method(tc.request, context) + elapsed = time.time() - start_time + + self.assertIsNotNone(result) + if tc.min_duration is not None: + self.assertGreaterEqual( + elapsed, + tc.min_duration, + f"Should stall for at least {tc.min_duration}s", + ) + if tc.max_duration is not None: + self.assertLess( + elapsed, + tc.max_duration, + f"Should not stall longer than {tc.max_duration}s", + ) + + if tc.verify_func: + tc.verify_func(self, result) def test_multiple_stalls_and_no_stall(self): """Test that stall happens twice with metadata and not without.""" - # First call with stall metadata - request1 = storage_control_pb2.CreateFolderRequest() - request1.parent = "projects/_/buckets/test-bucket" - request1.folder_id = "test-folder-multi-1" - metadata = [("x-goog-emulator-instructions", "stall-for-1s")] - context_stall = self.mock_context(metadata) + # First call with stall metadata + req1 = storage_control_pb2.CreateFolderRequest( + parent="projects/_/buckets/test-bucket", folder_id="test-folder-multi-1" + ) start_time = time.time() - folder1 = self.servicer.CreateFolder(request1, context_stall) + self.servicer.CreateFolder(req1, self.mock_context(metadata)) elapsed1 = time.time() - start_time - - self.assertIsNotNone(folder1) - self.assertGreaterEqual( - elapsed1, 1.0, "First call should stall for at least 1 second" - ) + self.assertGreaterEqual(elapsed1, 1.0) # Second call with stall metadata - request2 = storage_control_pb2.CreateFolderRequest() - request2.parent = "projects/_/buckets/test-bucket" - request2.folder_id = "test-folder-multi-2" - - context_stall2 = self.mock_context(metadata) - + req2 = storage_control_pb2.CreateFolderRequest( + parent="projects/_/buckets/test-bucket", folder_id="test-folder-multi-2" + ) start_time = time.time() - folder2 = self.servicer.CreateFolder(request2, context_stall2) + self.servicer.CreateFolder(req2, self.mock_context(metadata)) elapsed2 = time.time() - start_time - - self.assertIsNotNone(folder2) - self.assertGreaterEqual( - elapsed2, 1.0, "Second call should stall for at least 1 second" - ) + self.assertGreaterEqual(elapsed2, 1.0) # Third call without stall metadata - request3 = storage_control_pb2.CreateFolderRequest() - request3.parent = "projects/_/buckets/test-bucket" - request3.folder_id = "test-folder-multi-3" - - context_no_stall = self.mock_context() - - start_time = time.time() - folder3 = self.servicer.CreateFolder(request3, context_no_stall) - elapsed3 = time.time() - start_time - - self.assertIsNotNone(folder3) - self.assertLess( - elapsed3, 1.0, "Third call should complete quickly without stall" + req3 = storage_control_pb2.CreateFolderRequest( + parent="projects/_/buckets/test-bucket", folder_id="test-folder-multi-3" ) - - def test_get_storage_layout_no_stall(self): - """Test get storage layout without stall instruction.""" - request = storage_control_pb2.GetStorageLayoutRequest() - request.name = "projects/_/buckets/test-bucket/storageLayout" - - context = self.mock_context() - start_time = time.time() - layout = self.servicer.GetStorageLayout(request, context) - elapsed = time.time() - start_time - - self.assertIsNotNone(layout) - self.assertEqual(layout.name, "projects/_/buckets/test-bucket/storageLayout") - self.assertLess(elapsed, 1.0, "Should complete quickly without stall") - - def test_get_storage_layout_stall(self): - """Test get storage layout with stall instruction.""" - request = storage_control_pb2.GetStorageLayoutRequest() - request.name = "projects/_/buckets/test-bucket/storageLayout" - - metadata = [("x-goog-emulator-instructions", "stall-for-1s")] - context_stall = self.mock_context(metadata) - - start_time = time.time() - layout = self.servicer.GetStorageLayout(request, context_stall) - elapsed = time.time() - start_time - - self.assertIsNotNone(layout) - self.assertEqual(layout.name, "projects/_/buckets/test-bucket/storageLayout") - self.assertEqual(layout.location, "US") - self.assertEqual(layout.location_type, "multi-region") - self.assertFalse(layout.hierarchical_namespace.enabled) - self.assertGreaterEqual(elapsed, 1.0, "Should stall for at least 1 second") + self.servicer.CreateFolder(req3, self.mock_context()) + elapsed3 = time.time() - start_time + self.assertLess(elapsed3, 1.0) if __name__ == "__main__": From 48d6635a4f1035b4a0255655d46393c8309cb5f1 Mon Sep 17 00:00:00 2001 From: Nitin Garg Date: Wed, 1 Apr 2026 09:27:43 +0000 Subject: [PATCH 09/13] split stall and no-stall table-driven test cases --- tests/test_storage_control_stall.py | 215 ++++++++++++---------------- 1 file changed, 90 insertions(+), 125 deletions(-) diff --git a/tests/test_storage_control_stall.py b/tests/test_storage_control_stall.py index a5977238..097e92bc 100644 --- a/tests/test_storage_control_stall.py +++ b/tests/test_storage_control_stall.py @@ -29,13 +29,10 @@ @dataclasses.dataclass -class StallTestCase: +class ApiTestCase: name: str method_name: str request: Any - instructions: Optional[str] - min_duration: Optional[float] - max_duration: Optional[float] setup_func: Optional[Callable[["TestStorageControlStall"], None]] = None verify_func: Optional[Callable[["TestStorageControlStall", Any], None]] = None @@ -63,125 +60,53 @@ def _create_folder(self, folder_id): ) self.servicer.CreateFolder(request, self.mock_context()) - def test_stall_behaviors(self): - """Table-driven test for various stall behaviors across API methods.""" + def test_no_stall_behaviors(self): + """Table-driven test for no-stall behaviors across API methods.""" test_cases = [ - # CreateFolder - StallTestCase( + ApiTestCase( name="create_folder_no_stall", method_name="CreateFolder", request=storage_control_pb2.CreateFolderRequest( parent="projects/_/buckets/test-bucket", folder_id="create-no-stall" ), - instructions=None, - min_duration=None, - max_duration=0.5, verify_func=lambda self, res: self.assertEqual( res.name, "projects/_/buckets/test-bucket/folders/create-no-stall" ), ), - StallTestCase( - name="create_folder_stall_1s", - method_name="CreateFolder", - request=storage_control_pb2.CreateFolderRequest( - parent="projects/_/buckets/test-bucket", folder_id="create-stall-1s" - ), - instructions="stall-for-1s", - min_duration=1.0, - max_duration=None, - verify_func=lambda self, res: self.assertEqual( - res.name, "projects/_/buckets/test-bucket/folders/create-stall-1s" - ), - ), - # DeleteFolder - StallTestCase( + ApiTestCase( name="delete_folder_no_stall", method_name="DeleteFolder", setup_func=lambda self: self._create_folder("delete-no-stall"), request=storage_control_pb2.DeleteFolderRequest( name="projects/_/buckets/test-bucket/folders/delete-no-stall" ), - instructions=None, - min_duration=None, - max_duration=0.5, verify_func=lambda self, res: self.assertIsInstance( res, empty_pb2.Empty ), ), - StallTestCase( - name="delete_folder_stall_1s", - method_name="DeleteFolder", - setup_func=lambda self: self._create_folder("delete-stall-1s"), - request=storage_control_pb2.DeleteFolderRequest( - name="projects/_/buckets/test-bucket/folders/delete-stall-1s" - ), - instructions="stall-for-1s", - min_duration=1.0, - max_duration=None, - verify_func=lambda self, res: self.assertIsInstance( - res, empty_pb2.Empty - ), - ), - # GetFolder - StallTestCase( + ApiTestCase( name="get_folder_no_stall", method_name="GetFolder", setup_func=lambda self: self._create_folder("get-no-stall"), request=storage_control_pb2.GetFolderRequest( name="projects/_/buckets/test-bucket/folders/get-no-stall" ), - instructions=None, - min_duration=None, - max_duration=0.5, verify_func=lambda self, res: self.assertEqual( res.name, "projects/_/buckets/test-bucket/folders/get-no-stall" ), ), - StallTestCase( - name="get_folder_stall_1s", - method_name="GetFolder", - setup_func=lambda self: self._create_folder("get-stall-1s"), - request=storage_control_pb2.GetFolderRequest( - name="projects/_/buckets/test-bucket/folders/get-stall-1s" - ), - instructions="stall-for-1s", - min_duration=1.0, - max_duration=None, - verify_func=lambda self, res: self.assertEqual( - res.name, "projects/_/buckets/test-bucket/folders/get-stall-1s" - ), - ), - # ListFolders - StallTestCase( + ApiTestCase( name="list_folders_no_stall", method_name="ListFolders", setup_func=lambda self: self._create_folder("list-no-stall"), request=storage_control_pb2.ListFoldersRequest( parent="projects/_/buckets/test-bucket" ), - instructions=None, - min_duration=None, - max_duration=0.5, verify_func=lambda self, res: self.assertGreaterEqual( len(res.folders), 1 ), ), - StallTestCase( - name="list_folders_stall_1s", - method_name="ListFolders", - setup_func=lambda self: self._create_folder("list-stall-1s"), - request=storage_control_pb2.ListFoldersRequest( - parent="projects/_/buckets/test-bucket" - ), - instructions="stall-for-1s", - min_duration=1.0, - max_duration=None, - verify_func=lambda self, res: self.assertGreaterEqual( - len(res.folders), 1 - ), - ), - # RenameFolder - StallTestCase( + ApiTestCase( name="rename_folder_no_stall", method_name="RenameFolder", setup_func=lambda self: self._create_folder("rename-src-no-stall"), @@ -189,15 +114,90 @@ def test_stall_behaviors(self): name="projects/_/buckets/test-bucket/folders/rename-src-no-stall", destination_folder_id="projects/_/buckets/test-bucket/folders/rename-dst-no-stall", ), - instructions=None, - min_duration=None, - max_duration=0.5, verify_func=lambda self, res: self.assertEqual( res.name, "projects/_/buckets/test-bucket/folders/rename-src-no-stall", ), ), - StallTestCase( + ApiTestCase( + name="get_storage_layout_no_stall", + method_name="GetStorageLayout", + request=storage_control_pb2.GetStorageLayoutRequest( + name="projects/_/buckets/test-bucket/storageLayout" + ), + verify_func=lambda self, res: self.assertEqual( + res.name, "projects/_/buckets/test-bucket/storageLayout" + ), + ), + ] + + for tc in test_cases: + with self.subTest(name=tc.name): + if tc.setup_func: + tc.setup_func(self) + + context = self.mock_context() + + # Dynamically get the method from the servicer + method = getattr(self.servicer, tc.method_name) + + start_time = time.time() + result = method(tc.request, context) + elapsed = time.time() - start_time + + self.assertIsNotNone(result) + self.assertLess(elapsed, 0.5, "Should take less than 0.5s") + + if tc.verify_func: + tc.verify_func(self, result) + + def test_stall_behaviors(self): + """Table-driven test for various stall behaviors across API methods.""" + test_cases = [ + ApiTestCase( + name="create_folder_stall_1s", + method_name="CreateFolder", + request=storage_control_pb2.CreateFolderRequest( + parent="projects/_/buckets/test-bucket", folder_id="create-stall-1s" + ), + verify_func=lambda self, res: self.assertEqual( + res.name, "projects/_/buckets/test-bucket/folders/create-stall-1s" + ), + ), + ApiTestCase( + name="delete_folder_stall_1s", + method_name="DeleteFolder", + setup_func=lambda self: self._create_folder("delete-stall-1s"), + request=storage_control_pb2.DeleteFolderRequest( + name="projects/_/buckets/test-bucket/folders/delete-stall-1s" + ), + verify_func=lambda self, res: self.assertIsInstance( + res, empty_pb2.Empty + ), + ), + ApiTestCase( + name="get_folder_stall_1s", + method_name="GetFolder", + setup_func=lambda self: self._create_folder("get-stall-1s"), + request=storage_control_pb2.GetFolderRequest( + name="projects/_/buckets/test-bucket/folders/get-stall-1s" + ), + verify_func=lambda self, res: self.assertEqual( + res.name, "projects/_/buckets/test-bucket/folders/get-stall-1s" + ), + ), + ApiTestCase( + name="list_folders_stall_1s", + method_name="ListFolders", + setup_func=lambda self: self._create_folder("list-stall-1s"), + request=storage_control_pb2.ListFoldersRequest( + parent="projects/_/buckets/test-bucket" + ), + verify_func=lambda self, res: self.assertGreaterEqual( + len(res.folders), 1 + ), + ), + ApiTestCase( name="rename_folder_stall_1s", method_name="RenameFolder", setup_func=lambda self: self._create_folder("rename-src-stall-1s"), @@ -205,37 +205,17 @@ def test_stall_behaviors(self): name="projects/_/buckets/test-bucket/folders/rename-src-stall-1s", destination_folder_id="projects/_/buckets/test-bucket/folders/rename-dst-stall-1s", ), - instructions="stall-for-1s", - min_duration=1.0, - max_duration=None, verify_func=lambda self, res: self.assertEqual( res.name, "projects/_/buckets/test-bucket/folders/rename-src-stall-1s", ), ), - # GetStorageLayout - StallTestCase( - name="get_storage_layout_no_stall", - method_name="GetStorageLayout", - request=storage_control_pb2.GetStorageLayoutRequest( - name="projects/_/buckets/test-bucket/storageLayout" - ), - instructions=None, - min_duration=None, - max_duration=0.5, - verify_func=lambda self, res: self.assertEqual( - res.name, "projects/_/buckets/test-bucket/storageLayout" - ), - ), - StallTestCase( + ApiTestCase( name="get_storage_layout_stall_1s", method_name="GetStorageLayout", request=storage_control_pb2.GetStorageLayoutRequest( name="projects/_/buckets/test-bucket/storageLayout" ), - instructions="stall-for-1s", - min_duration=1.0, - max_duration=None, verify_func=lambda self, res: self.assertEqual(res.location, "US"), ), ] @@ -245,11 +225,7 @@ def test_stall_behaviors(self): if tc.setup_func: tc.setup_func(self) - metadata = ( - [("x-goog-emulator-instructions", tc.instructions)] - if tc.instructions - else [] - ) + metadata = [("x-goog-emulator-instructions", "stall-for-1s")] context = self.mock_context(metadata) # Dynamically get the method from the servicer @@ -260,18 +236,7 @@ def test_stall_behaviors(self): elapsed = time.time() - start_time self.assertIsNotNone(result) - if tc.min_duration is not None: - self.assertGreaterEqual( - elapsed, - tc.min_duration, - f"Should stall for at least {tc.min_duration}s", - ) - if tc.max_duration is not None: - self.assertLess( - elapsed, - tc.max_duration, - f"Should not stall longer than {tc.max_duration}s", - ) + self.assertGreaterEqual(elapsed, 1.0, "Should stall for at least 1.0s") if tc.verify_func: tc.verify_func(self, result) From ddf99dfab83106eb952589e6200860dcdcea8548 Mon Sep 17 00:00:00 2001 From: Nitin Garg Date: Fri, 3 Apr 2026 06:00:06 +0000 Subject: [PATCH 10/13] removing unrelated changes for cleanup --- README.md | 7 ++++--- google/iam/v1/iam_policy_pb2.py | 14 ++++++++++++++ google/iam/v1/iam_policy_pb2_grpc.py | 14 ++++++++++++++ google/iam/v1/options_pb2.py | 14 ++++++++++++++ google/iam/v1/options_pb2_grpc.py | 14 ++++++++++++++ google/iam/v1/policy_pb2.py | 14 ++++++++++++++ google/iam/v1/policy_pb2_grpc.py | 14 ++++++++++++++ google/storage/control/__init__.py | 13 ------------- google/storage/v2/storage_pb2.py | 14 ++++++++++++++ google/storage/v2/storage_pb2_grpc.py | 14 ++++++++++++++ testbench/rest_server.py | 4 ++-- 11 files changed, 118 insertions(+), 18 deletions(-) delete mode 100644 google/storage/control/__init__.py diff --git a/README.md b/README.md index 7b77857f..1e48e36e 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,8 @@ is expected to be used by Storage library maintainers. - [When to use this testbench](#when-to-use-this-testbench) - [How to use this testbench](#how-to-use-this-testbench) - [Initial set up](#initial-set-up) - - [Start the gRPC server](#start-the-grpc-server) + - [Run the testbench](#run-the-testbench) + - [Start the gRPC server](#start-the-gRPC-server) - [Check that the testbench is running](#check-that-the-testbench-is-running) - [Updating Proto Files](#updating-proto-files) - [Force Failures](#force-failures) @@ -312,7 +313,7 @@ response = stub.CreateFolder(request, metadata=metadata) ### Writing and running tests -Tests are located in the `tests/` directory. To run the tests locally, use +Tests are located in the `tests/` directory. To run the tests locally, use ```bash python -m unittest [test_module.py] # runs all the tests in test_module.py @@ -330,4 +331,4 @@ Steps: 1. Title "v0.x.x" 1. Click Generate release notes 1. Make sure "Set as the latest release" is checked -1. Click "Publish Release" to release \ No newline at end of file +1. Click "Publish Release" to release diff --git a/google/iam/v1/iam_policy_pb2.py b/google/iam/v1/iam_policy_pb2.py index a294d174..03eda47c 100644 --- a/google/iam/v1/iam_policy_pb2.py +++ b/google/iam/v1/iam_policy_pb2.py @@ -1,4 +1,18 @@ # -*- coding: utf-8 -*- +# Copyright 2024 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. + # Generated by the protocol buffer compiler. DO NOT EDIT! # NO CHECKED-IN PROTOBUF GENCODE # source: google/iam/v1/iam_policy.proto diff --git a/google/iam/v1/iam_policy_pb2_grpc.py b/google/iam/v1/iam_policy_pb2_grpc.py index 0d2513db..28ed5951 100644 --- a/google/iam/v1/iam_policy_pb2_grpc.py +++ b/google/iam/v1/iam_policy_pb2_grpc.py @@ -1,4 +1,18 @@ # Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! +# Copyright 2024 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. + """Client and server classes corresponding to protobuf-defined services.""" import grpc import warnings diff --git a/google/iam/v1/options_pb2.py b/google/iam/v1/options_pb2.py index 65739b0d..75e2a2b3 100644 --- a/google/iam/v1/options_pb2.py +++ b/google/iam/v1/options_pb2.py @@ -1,4 +1,18 @@ # -*- coding: utf-8 -*- +# Copyright 2024 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. + # Generated by the protocol buffer compiler. DO NOT EDIT! # NO CHECKED-IN PROTOBUF GENCODE # source: google/iam/v1/options.proto diff --git a/google/iam/v1/options_pb2_grpc.py b/google/iam/v1/options_pb2_grpc.py index 8c96ff94..baae5aa9 100644 --- a/google/iam/v1/options_pb2_grpc.py +++ b/google/iam/v1/options_pb2_grpc.py @@ -1,4 +1,18 @@ # Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! +# Copyright 2024 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. + """Client and server classes corresponding to protobuf-defined services.""" import grpc import warnings diff --git a/google/iam/v1/policy_pb2.py b/google/iam/v1/policy_pb2.py index 83cee4c1..2c976271 100644 --- a/google/iam/v1/policy_pb2.py +++ b/google/iam/v1/policy_pb2.py @@ -1,4 +1,18 @@ # -*- coding: utf-8 -*- +# Copyright 2024 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. + # Generated by the protocol buffer compiler. DO NOT EDIT! # NO CHECKED-IN PROTOBUF GENCODE # source: google/iam/v1/policy.proto diff --git a/google/iam/v1/policy_pb2_grpc.py b/google/iam/v1/policy_pb2_grpc.py index b9faf955..af0dbb7f 100644 --- a/google/iam/v1/policy_pb2_grpc.py +++ b/google/iam/v1/policy_pb2_grpc.py @@ -1,4 +1,18 @@ # Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! +# Copyright 2024 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. + """Client and server classes corresponding to protobuf-defined services.""" import grpc import warnings diff --git a/google/storage/control/__init__.py b/google/storage/control/__init__.py deleted file mode 100644 index 58d482ea..00000000 --- a/google/storage/control/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# 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. diff --git a/google/storage/v2/storage_pb2.py b/google/storage/v2/storage_pb2.py index 392dd83a..f26e3b24 100644 --- a/google/storage/v2/storage_pb2.py +++ b/google/storage/v2/storage_pb2.py @@ -1,4 +1,18 @@ # -*- coding: utf-8 -*- +# Copyright 2024 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. + # Generated by the protocol buffer compiler. DO NOT EDIT! # NO CHECKED-IN PROTOBUF GENCODE # source: google/storage/v2/storage.proto diff --git a/google/storage/v2/storage_pb2_grpc.py b/google/storage/v2/storage_pb2_grpc.py index fa6feef8..1ed487bf 100644 --- a/google/storage/v2/storage_pb2_grpc.py +++ b/google/storage/v2/storage_pb2_grpc.py @@ -1,4 +1,18 @@ # Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! +# Copyright 2024 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. + """Client and server classes corresponding to protobuf-defined services.""" import grpc import warnings diff --git a/testbench/rest_server.py b/testbench/rest_server.py index b08515fe..ca6c123d 100644 --- a/testbench/rest_server.py +++ b/testbench/rest_server.py @@ -1247,10 +1247,10 @@ def delete_resumable_upload(bucket_name): # === SERVER === # # Define the WSGI application to handle HMAC key and service account requests -PROJECTS_HANDLER_PATH, projects_app = projects_rest_server.get_projects_app(db) +(PROJECTS_HANDLER_PATH, projects_app) = projects_rest_server.get_projects_app(db) # Define the WSGI application to handle IAM requests -IAM_HANDLER_PATH, iam_app = iam_rest_server.get_iam_app() +(IAM_HANDLER_PATH, iam_app) = iam_rest_server.get_iam_app() server = flask.Flask(__name__) server.debug = False From ef09199a4755020f084f53e07abbe710e794767d Mon Sep 17 00:00:00 2001 From: Nitin Garg Date: Mon, 6 Apr 2026 06:51:30 +0000 Subject: [PATCH 11/13] fix validations and handling of hns buckets in grpc server --- testbench/grpc_server.py | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/testbench/grpc_server.py b/testbench/grpc_server.py index 16cbbdc0..9d39519e 100644 --- a/testbench/grpc_server.py +++ b/testbench/grpc_server.py @@ -1239,14 +1239,32 @@ def RenameFolder(self, request, context): @retry_test(method="storage.storageLayout.get") def GetStorageLayout(self, request, context): self._apply_stall(context) + # Extract bucket path from request.name which is "projects/_/buckets/bucket_name/storageLayout" + bucket_path = request.name.replace("/storageLayout", "") + bucket = self.db.get_bucket(bucket_path, context) + # Create a simple storage layout response layout = storage_control_pb2.StorageLayout() layout.name = request.name + + if bucket is None: + # If the bucket isn't found (e.g. abort is mocked), use defaults + layout.location = "US" + layout.location_type = "multi-region" + layout.hierarchical_namespace.enabled = False + return layout + # Set default location and location_type - layout.location = "US" + layout.location = bucket.metadata.location if bucket.metadata.location else "US" layout.location_type = "multi-region" - # Optionally set hierarchical namespace enabled flag - layout.hierarchical_namespace.enabled = False + # Set hierarchical namespace enabled flag based on bucket metadata + if ( + bucket.metadata.hierarchical_namespace + and bucket.metadata.hierarchical_namespace.enabled + ): + layout.hierarchical_namespace.enabled = True + else: + layout.hierarchical_namespace.enabled = False return layout From 04d16bcc9415eb55d92eb8f828726b98472772d5 Mon Sep 17 00:00:00 2001 From: Nitin Garg Date: Mon, 6 Apr 2026 07:12:09 +0000 Subject: [PATCH 12/13] add support for control-client stall duration in milliseconds --- testbench/grpc_server.py | 29 +++++++++++++++++++++-------- tests/test_storage_control_stall.py | 27 +++++++++++++++++++++++++-- 2 files changed, 46 insertions(+), 10 deletions(-) diff --git a/testbench/grpc_server.py b/testbench/grpc_server.py index 9d39519e..6124fcb8 100644 --- a/testbench/grpc_server.py +++ b/testbench/grpc_server.py @@ -1182,12 +1182,17 @@ def _apply_stall(self, context): instruction = testbench.common.extract_instruction(None, context) if instruction and "stall" in instruction: - # Parse stall instruction (e.g., "stall-for-1s") + # Parse stall instruction (e.g., "stall-for-1s" or "stall-for-500ms") if instruction.startswith("stall-for-"): - # Parse "stall-for-1s" format - match = re.match(r"stall-for-(\d+)s", instruction) - if match: - time.sleep(int(match.group(1))) + # Check for milliseconds. + match_ms = re.match(r"stall-for-(\d+)ms", instruction) + if match_ms: + time.sleep(int(match_ms.group(1)) / 1000.0) + return + # Check for seconds. + match_s = re.match(r"stall-for-(\d+)s", instruction) + if match_s: + time.sleep(int(match_s.group(1))) @retry_test(method="storage.folders.create") def CreateFolder(self, request, context): @@ -1239,16 +1244,24 @@ def RenameFolder(self, request, context): @retry_test(method="storage.storageLayout.get") def GetStorageLayout(self, request, context): self._apply_stall(context) + # Extract bucket path from request.name which is "projects/_/buckets/bucket_name/storageLayout" bucket_path = request.name.replace("/storageLayout", "") - bucket = self.db.get_bucket(bucket_path, context) # Create a simple storage layout response layout = storage_control_pb2.StorageLayout() layout.name = request.name - if bucket is None: - # If the bucket isn't found (e.g. abort is mocked), use defaults + try: + bucket = self.db.get_bucket(bucket_path, context) + if bucket is None: + # If the bucket isn't found (e.g. abort is mocked), use defaults + layout.location = "US" + layout.location_type = "multi-region" + layout.hierarchical_namespace.enabled = False + return layout + except Exception: + # handle testbench errors gracefully (i.e abort contexts raising an error) layout.location = "US" layout.location_type = "multi-region" layout.hierarchical_namespace.enabled = False diff --git a/tests/test_storage_control_stall.py b/tests/test_storage_control_stall.py index 097e92bc..228fd4cd 100644 --- a/tests/test_storage_control_stall.py +++ b/tests/test_storage_control_stall.py @@ -154,6 +154,18 @@ def test_no_stall_behaviors(self): def test_stall_behaviors(self): """Table-driven test for various stall behaviors across API methods.""" test_cases = [ + ApiTestCase( + name="create_folder_stall_500ms", + method_name="CreateFolder", + request=storage_control_pb2.CreateFolderRequest( + parent="projects/_/buckets/test-bucket", + folder_id="create-stall-500ms", + ), + verify_func=lambda self, res: self.assertEqual( + res.name, + "projects/_/buckets/test-bucket/folders/create-stall-500ms", + ), + ), ApiTestCase( name="create_folder_stall_1s", method_name="CreateFolder", @@ -225,7 +237,14 @@ def test_stall_behaviors(self): if tc.setup_func: tc.setup_func(self) - metadata = [("x-goog-emulator-instructions", "stall-for-1s")] + # Determine instruction based on test name + instruction = "stall-for-1s" + expected_stall = 1.0 + if "500ms" in tc.name: + instruction = "stall-for-500ms" + expected_stall = 0.5 + + metadata = [("x-goog-emulator-instructions", instruction)] context = self.mock_context(metadata) # Dynamically get the method from the servicer @@ -236,7 +255,11 @@ def test_stall_behaviors(self): elapsed = time.time() - start_time self.assertIsNotNone(result) - self.assertGreaterEqual(elapsed, 1.0, "Should stall for at least 1.0s") + self.assertGreaterEqual( + elapsed, + expected_stall, + f"Should stall for at least {expected_stall}s", + ) if tc.verify_func: tc.verify_func(self, result) From 156411991d865d24ebe166325acd0d6b2f6511a3 Mon Sep 17 00:00:00 2001 From: Nitin Garg Date: Mon, 6 Apr 2026 09:05:12 +0000 Subject: [PATCH 13/13] address review comment --- testbench/grpc_server.py | 19 ++++--------------- tests/test_storage_control_stall.py | 13 +++++++++++++ 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/testbench/grpc_server.py b/testbench/grpc_server.py index 6124fcb8..7a408469 100644 --- a/testbench/grpc_server.py +++ b/testbench/grpc_server.py @@ -1247,26 +1247,15 @@ def GetStorageLayout(self, request, context): # Extract bucket path from request.name which is "projects/_/buckets/bucket_name/storageLayout" bucket_path = request.name.replace("/storageLayout", "") + bucket = self.db.get_bucket(bucket_path, context) + + if bucket is None: + return None # Create a simple storage layout response layout = storage_control_pb2.StorageLayout() layout.name = request.name - try: - bucket = self.db.get_bucket(bucket_path, context) - if bucket is None: - # If the bucket isn't found (e.g. abort is mocked), use defaults - layout.location = "US" - layout.location_type = "multi-region" - layout.hierarchical_namespace.enabled = False - return layout - except Exception: - # handle testbench errors gracefully (i.e abort contexts raising an error) - layout.location = "US" - layout.location_type = "multi-region" - layout.hierarchical_namespace.enabled = False - return layout - # Set default location and location_type layout.location = bucket.metadata.location if bucket.metadata.location else "US" layout.location_type = "multi-region" diff --git a/tests/test_storage_control_stall.py b/tests/test_storage_control_stall.py index 228fd4cd..9a5d5210 100644 --- a/tests/test_storage_control_stall.py +++ b/tests/test_storage_control_stall.py @@ -16,6 +16,7 @@ """Test stall functionality for Storage Control API.""" import dataclasses +import os import time import unittest import unittest.mock @@ -49,11 +50,23 @@ def mock_context(self, metadata=None): return context def setUp(self): + self.original_env_bucket = os.environ.get( + "GOOGLE_CLOUD_CPP_STORAGE_TEST_BUCKET_NAME" + ) + os.environ["GOOGLE_CLOUD_CPP_STORAGE_TEST_BUCKET_NAME"] = "test-bucket" self.db = testbench.database.Database.init() self.servicer = testbench.grpc_server.StorageControlServicer( self.db, echo_metadata=False ) + def tearDown(self): + if self.original_env_bucket is None: + os.environ.pop("GOOGLE_CLOUD_CPP_STORAGE_TEST_BUCKET_NAME", None) + else: + os.environ[ + "GOOGLE_CLOUD_CPP_STORAGE_TEST_BUCKET_NAME" + ] = self.original_env_bucket + def _create_folder(self, folder_id): request = storage_control_pb2.CreateFolderRequest( parent="projects/_/buckets/test-bucket", folder_id=folder_id