From 9f1ed5c166a346f972ae1843e4c8ea86178a0308 Mon Sep 17 00:00:00 2001 From: Cathy Ouyang Date: Mon, 20 May 2024 14:55:57 -0700 Subject: [PATCH 1/2] feat: support HNS enablement in bucket metadata --- google/cloud/storage/bucket.py | 25 +++++++++++++++++++++++++ tests/system/test_bucket.py | 29 +++++++++++++++++++++++++++++ tests/unit/test_bucket.py | 13 +++++++++++++ 3 files changed, 67 insertions(+) diff --git a/google/cloud/storage/bucket.py b/google/cloud/storage/bucket.py index 15fb408e8..ab1a8c9ec 100644 --- a/google/cloud/storage/bucket.py +++ b/google/cloud/storage/bucket.py @@ -2957,6 +2957,31 @@ def object_retention_mode(self): if object_retention is not None: return object_retention.get("mode") + @property + def hierarchical_namespace_enabled(self): + """Whether hierarchical namespace is enabled for this bucket. + + :setter: Update whether hierarchical namespace is enabled for this bucket. + :getter: Query whether hierarchical namespace is enabled for this bucket. + + :rtype: bool + :returns: True if enabled, else False. + """ + hns = self._properties.get("hierarchicalNamespace", {}) + return hns.get("enabled") + + @hierarchical_namespace_enabled.setter + def hierarchical_namespace_enabled(self, value): + """Enable or disable hierarchical namespace at the bucket-level. + + :type value: convertible to boolean + :param value: If true, enable hierarchical namespace for this bucket. + If false, disable hierarchical namespace for this bucket. + """ + hns = self._properties.get("hierarchicalNamespace", {}) + hns["enabled"] = bool(value) + self._patch_property("hierarchicalNamespace", hns) + def configure_website(self, main_page_suffix=None, not_found_page=None): """Configure website-related properties. diff --git a/tests/system/test_bucket.py b/tests/system/test_bucket.py index 735570f6c..270a77ad1 100644 --- a/tests/system/test_bucket.py +++ b/tests/system/test_bucket.py @@ -1236,3 +1236,32 @@ def test_soft_delete_policy( bucket.soft_delete_policy.retention_duration_seconds = new_duration_secs bucket.patch() assert bucket.soft_delete_policy.retention_duration_seconds == new_duration_secs + + +def test_new_bucket_with_hierarchical_namespace( + storage_client, + buckets_to_delete, +): + # Test new bucket without specifying hierarchical namespace + bucket_name = _helpers.unique_name("new-wo-hns") + bucket_obj = storage_client.bucket(bucket_name) + bucket = storage_client.create_bucket(bucket_obj) + buckets_to_delete.append(bucket) + assert bucket.hierarchical_namespace_enabled is None + + # Test new bucket with hierarchical namespace disabled + bucket_name = _helpers.unique_name("new-hns-disabled") + bucket_obj = storage_client.bucket(bucket_name) + bucket_obj.hierarchical_namespace_enabled = False + bucket = storage_client.create_bucket(bucket_obj) + buckets_to_delete.append(bucket) + assert bucket.hierarchical_namespace_enabled is False + + # Test new bucket with hierarchical namespace enabled + bucket_name = _helpers.unique_name("new-hns-enabled") + bucket_obj = storage_client.bucket(bucket_name) + bucket_obj.hierarchical_namespace_enabled = True + bucket_obj.iam_configuration.uniform_bucket_level_access_enabled = True + bucket = storage_client.create_bucket(bucket_obj) + buckets_to_delete.append(bucket) + assert bucket.hierarchical_namespace_enabled is True diff --git a/tests/unit/test_bucket.py b/tests/unit/test_bucket.py index 2df41c3ff..030fba72b 100644 --- a/tests/unit/test_bucket.py +++ b/tests/unit/test_bucket.py @@ -3163,6 +3163,19 @@ def test_soft_delete_policy_setter(self): self.assertTrue("softDeletePolicy" in bucket._changes) self.assertEqual(bucket.soft_delete_policy.retention_duration_seconds, seconds) + def test_hierarchical_namespace_enabled_getter_and_setter(self): + # Test hierarchical_namespace configuration unset + bucket = self._make_one() + self.assertIsNone(bucket.hierarchical_namespace_enabled) + + # Test hierarchical_namespace configuration explicitly set + properties = {"hierarchicalNamespace": {"enabled": True}} + bucket = self._make_one(properties=properties) + self.assertTrue(bucket.hierarchical_namespace_enabled) + bucket.hierarchical_namespace_enabled = False + self.assertIn("hierarchicalNamespace", bucket._changes) + self.assertFalse(bucket.hierarchical_namespace_enabled) + def test_configure_website_defaults(self): NAME = "name" UNSET = {"website": {"mainPageSuffix": None, "notFoundPage": None}} From 049439fcc7d1fef03ff4ed4d373d759813384699 Mon Sep 17 00:00:00 2001 From: Cathy Ouyang Date: Tue, 21 May 2024 16:34:21 -0700 Subject: [PATCH 2/2] update docstrings --- google/cloud/storage/bucket.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/google/cloud/storage/bucket.py b/google/cloud/storage/bucket.py index ab1a8c9ec..7b6421d29 100644 --- a/google/cloud/storage/bucket.py +++ b/google/cloud/storage/bucket.py @@ -2977,6 +2977,10 @@ def hierarchical_namespace_enabled(self, value): :type value: convertible to boolean :param value: If true, enable hierarchical namespace for this bucket. If false, disable hierarchical namespace for this bucket. + + .. note:: + To enable hierarchical namespace, you must set it at bucket creation time. + Currently, hierarchical namespace configuration cannot be changed after bucket creation. """ hns = self._properties.get("hierarchicalNamespace", {}) hns["enabled"] = bool(value)