diff --git a/storagecontrol/conftest.py b/storagecontrol/conftest.py index 8b0951fe38a..6e5f924d8f2 100644 --- a/storagecontrol/conftest.py +++ b/storagecontrol/conftest.py @@ -25,6 +25,11 @@ def project_id() -> str: yield os.environ.get("BUILD_SPECIFIC_GCLOUD_PROJECT") +@pytest.fixture(scope="function") +def uuid_name(prefix: str = "storagecontrol") -> str: + yield f"{prefix}-{uuid.uuid4().hex[:5]}" + + @pytest.fixture(scope="function") def bucket_name() -> str: yield f"storagecontrol-samples-{uuid.uuid4()}" @@ -42,3 +47,20 @@ def gcs_bucket(project_id: str, bucket_name: str) -> storage.Bucket: yield bucket bucket.delete(force=True) + + +@pytest.fixture(scope="function") +def hns_enabled_bucket(project_id: str, bucket_name: str) -> storage.Bucket: + """ + Yields and auto-cleans up an HNS enabled bucket. + """ + + storage_client = storage.Client(project=project_id) + bucket = storage_client.bucket(bucket_name) + bucket.iam_configuration.uniform_bucket_level_access_enabled = True + bucket.hierarchical_namespace_enabled = True + bucket = storage_client.create_bucket(bucket) + + yield bucket + + bucket.delete(force=True) diff --git a/storagecontrol/create_folder.py b/storagecontrol/create_folder.py new file mode 100644 index 00000000000..84c7963b87b --- /dev/null +++ b/storagecontrol/create_folder.py @@ -0,0 +1,47 @@ +# 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 +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import sys + +# [START storage_control_create_folder] +from google.cloud import storage_control_v2 + + +def create_folder(bucket_name: str, folder_name: str) -> None: + # The ID of your GCS bucket + # bucket_name = "your-unique-bucket-name" + + # The name of the folder to be created + # folder_name = "folder-name" + + storage_control_client = storage_control_v2.StorageControlClient() + # The storage bucket path uses the global access pattern, in which the "_" + # denotes this bucket exists in the global namespace. + project_path = storage_control_client.common_project_path("_") + bucket_path = f"{project_path}/buckets/{bucket_name}" + + request = storage_control_v2.CreateFolderRequest( + parent=bucket_path, + folder_id=folder_name, + ) + response = storage_control_client.create_folder(request=request) + + print(f"Created folder: {response.name}") + + +# [END storage_control_create_folder] + + +if __name__ == "__main__": + create_folder(bucket_name=sys.argv[1], folder_name=sys.argv[2]) diff --git a/storagecontrol/delete_folder.py b/storagecontrol/delete_folder.py new file mode 100644 index 00000000000..58876c59017 --- /dev/null +++ b/storagecontrol/delete_folder.py @@ -0,0 +1,47 @@ +# 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 +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import sys + +# [START storage_control_delete_folder] +from google.cloud import storage_control_v2 + + +def delete_folder(bucket_name: str, folder_name: str) -> None: + # The ID of your GCS bucket + # bucket_name = "your-unique-bucket-name" + + # The name of the folder to be deleted + # folder_name = "folder-name" + + storage_control_client = storage_control_v2.StorageControlClient() + # The storage bucket path uses the global access pattern, in which the "_" + # denotes this bucket exists in the global namespace. + folder_path = storage_control_client.folder_path( + project="_", bucket=bucket_name, folder=folder_name + ) + + request = storage_control_v2.DeleteFolderRequest( + name=folder_path, + ) + storage_control_client.delete_folder(request=request) + + print(f"Deleted folder {folder_name}") + + +# [END storage_control_delete_folder] + + +if __name__ == "__main__": + delete_folder(bucket_name=sys.argv[1], folder_name=sys.argv[2]) diff --git a/storagecontrol/get_folder.py b/storagecontrol/get_folder.py new file mode 100644 index 00000000000..e61eca6c86d --- /dev/null +++ b/storagecontrol/get_folder.py @@ -0,0 +1,47 @@ +# 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 +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import sys + +# [START storage_control_get_folder] +from google.cloud import storage_control_v2 + + +def get_folder(bucket_name: str, folder_name: str) -> None: + # The ID of your GCS bucket + # bucket_name = "your-unique-bucket-name" + + # The name of the folder + # folder_name = "folder-name" + + storage_control_client = storage_control_v2.StorageControlClient() + # The storage bucket path uses the global access pattern, in which the "_" + # denotes this bucket exists in the global namespace. + folder_path = storage_control_client.folder_path( + project="_", bucket=bucket_name, folder=folder_name + ) + + request = storage_control_v2.GetFolderRequest( + name=folder_path, + ) + response = storage_control_client.get_folder(request=request) + + print(f"Got folder: {response.name}") + + +# [END storage_control_get_folder] + + +if __name__ == "__main__": + get_folder(bucket_name=sys.argv[1], folder_name=sys.argv[2]) diff --git a/storagecontrol/list_folders.py b/storagecontrol/list_folders.py new file mode 100644 index 00000000000..2685c873f69 --- /dev/null +++ b/storagecontrol/list_folders.py @@ -0,0 +1,46 @@ +# 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 +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import sys + +# [START storage_control_list_folders] +from google.cloud import storage_control_v2 + + +def list_folders(bucket_name: str) -> None: + # The ID of your GCS bucket + # bucket_name = "your-unique-bucket-name" + + storage_control_client = storage_control_v2.StorageControlClient() + # The storage bucket path uses the global access pattern, in which the "_" + # denotes this bucket exists in the global namespace. + project_path = storage_control_client.common_project_path("_") + bucket_path = f"{project_path}/buckets/{bucket_name}" + + request = storage_control_v2.ListFoldersRequest( + parent=bucket_path, + ) + + page_result = storage_control_client.list_folders(request=request) + for folder in page_result: + print(folder) + + print(f"Listed folders in bucket {bucket_name}") + + +# [END storage_control_list_folders] + + +if __name__ == "__main__": + list_folders(bucket_name=sys.argv[1]) diff --git a/storagecontrol/noxfile_config.py b/storagecontrol/noxfile_config.py index 290c1f35f78..8568231c306 100644 --- a/storagecontrol/noxfile_config.py +++ b/storagecontrol/noxfile_config.py @@ -22,7 +22,7 @@ TEST_CONFIG_OVERRIDE = { # You can opt out from the test for specific Python versions. - "ignored_versions": ["2.7", "3.7", "3.9", "3.10", "3.11"], + "ignored_versions": ["2.7", "3.7", "3.9", "3.11", "3.12"], # Old samples are opted out of enforcing Python type hints # All new samples should feature them "enforce_type_hints": True, diff --git a/storagecontrol/rename_folder.py b/storagecontrol/rename_folder.py new file mode 100644 index 00000000000..a182af509ce --- /dev/null +++ b/storagecontrol/rename_folder.py @@ -0,0 +1,59 @@ +# 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 +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import sys + +# [START storage_control_rename_folder] +from google.cloud import storage_control_v2 + + +def rename_folder( + bucket_name: str, source_folder_name: str, destination_folder_name: str +) -> None: + # The ID of your GCS bucket + # bucket_name = "your-unique-bucket-name" + # + # The source folder ID + # source_folder_name = "current-folder-name" + # + # The destination folder ID + # destination_folder_name = "new-folder-name" + + storage_control_client = storage_control_v2.StorageControlClient() + # The storage bucket path uses the global access pattern, in which the "_" + # denotes this bucket exists in the global namespace. + source_folder_path = storage_control_client.folder_path( + project="_", bucket=bucket_name, folder=source_folder_name + ) + + request = storage_control_v2.RenameFolderRequest( + name=source_folder_path, + destination_folder_id=destination_folder_name, + ) + + operation = storage_control_client.rename_folder(request=request) + operation.result(60) + + print(f"Renamed folder {source_folder_name} to {destination_folder_name}") + + +# [END storage_control_rename_folder] + + +if __name__ == "__main__": + rename_folder( + bucket_name=sys.argv[1], + source_folder_name=sys.argv[2], + destination_folder_name=sys.argv[3], + ) diff --git a/storagecontrol/requirements-test.txt b/storagecontrol/requirements-test.txt index 54384f168eb..f2e02a98e66 100644 --- a/storagecontrol/requirements-test.txt +++ b/storagecontrol/requirements-test.txt @@ -1,2 +1,2 @@ pytest==8.2.0 -google-cloud-storage==2.16.0 \ No newline at end of file +google-cloud-storage==2.17.0 \ No newline at end of file diff --git a/storagecontrol/snippets_test.py b/storagecontrol/snippets_test.py new file mode 100644 index 00000000000..cadd1340feb --- /dev/null +++ b/storagecontrol/snippets_test.py @@ -0,0 +1,63 @@ +# 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 +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from google.cloud import storage + +import pytest + +import create_folder +import delete_folder +import get_folder +import list_folders +import rename_folder + + +# === Folders === # + + +def test_folder_create_get_list_rename_delete( + capsys: pytest.LogCaptureFixture, hns_enabled_bucket: storage.Bucket, uuid_name: str +) -> None: + bucket_name = hns_enabled_bucket.name + folder_name = uuid_name + + # Test create folder + create_folder.create_folder(bucket_name=bucket_name, folder_name=folder_name) + out, _ = capsys.readouterr() + assert folder_name in out + + # Test get folder + get_folder.get_folder(bucket_name=bucket_name, folder_name=folder_name) + out, _ = capsys.readouterr() + assert folder_name in out + + # Test list folders + list_folders.list_folders(bucket_name=bucket_name) + out, _ = capsys.readouterr() + assert folder_name in out + + # Test rename folder + new_name = f"new-name-{uuid_name}" + rename_folder.rename_folder( + bucket_name=bucket_name, + source_folder_name=folder_name, + destination_folder_name=new_name, + ) + out, _ = capsys.readouterr() + assert folder_name in out + + # Test delete folder + delete_folder.delete_folder(bucket_name=bucket_name, folder_name=new_name) + out, _ = capsys.readouterr() + assert new_name in out