Skip to content

Commit 4dafc81

Browse files
author
Aaron Gabriel Neyer
authored
feat: add turbo replication support and samples (#622)
* feat: add turbo replication support * lintfix * nother-lint-fix * hmm weird * i need to learn how to use lint better... * . * . * how about now? * take rpo out of constructor * add unit tests * add link to docs * ensure inclusion of "rpo" in bucket._changes * add rpo samples * lint it * address cathys nits * fix a little test thing * start to fix weirdness, creating issue to address more fully * change it back
1 parent 8aa4130 commit 4dafc81

File tree

9 files changed

+317
-0
lines changed

9 files changed

+317
-0
lines changed

google/cloud/storage/bucket.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -631,6 +631,29 @@ def _set_properties(self, value):
631631
self._label_removals.clear()
632632
return super(Bucket, self)._set_properties(value)
633633

634+
@property
635+
def rpo(self):
636+
"""Get the RPO (Recovery Point Objective) of this bucket
637+
638+
See: https://cloud.google.com/storage/docs/managing-turbo-replication
639+
640+
"ASYNC_TURBO" or "DEFAULT"
641+
:rtype: str
642+
"""
643+
return self._properties.get("rpo")
644+
645+
@rpo.setter
646+
def rpo(self, value):
647+
"""
648+
Set the RPO (Recovery Point Objective) of this bucket.
649+
650+
See: https://cloud.google.com/storage/docs/managing-turbo-replication
651+
652+
:type value: str
653+
:param value: "ASYNC_TURBO" or "DEFAULT"
654+
"""
655+
self._patch_property("rpo", value)
656+
634657
@property
635658
def user_project(self):
636659
"""Project ID to be billed for API requests made via this bucket.

google/cloud/storage/constants.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,3 +117,15 @@
117117
118118
See: https://cloud.google.com/storage/docs/public-access-prevention
119119
"""
120+
121+
RPO_ASYNC_TURBO = "ASYNC_TURBO"
122+
"""Turbo Replication RPO
123+
124+
See: https://cloud.google.com/storage/docs/managing-turbo-replication
125+
"""
126+
127+
RPO_DEFAULT = "DEFAULT"
128+
"""Default RPO
129+
130+
See: https://cloud.google.com/storage/docs/managing-turbo-replication
131+
"""

samples/snippets/rpo_test.py

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
# Copyright 2021 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
import uuid
16+
17+
from google.cloud import storage
18+
import pytest
19+
20+
import storage_create_bucket_turbo_replication
21+
import storage_get_rpo
22+
import storage_set_rpo_async_turbo
23+
import storage_set_rpo_default
24+
25+
26+
@pytest.fixture
27+
def dual_region_bucket():
28+
"""Yields a dual region bucket that is deleted after the test completes."""
29+
bucket = None
30+
while bucket is None or bucket.exists():
31+
bucket_name = "bucket-lock-{}".format(uuid.uuid4())
32+
bucket = storage.Client().bucket(bucket_name)
33+
bucket.location = "NAM4"
34+
bucket.create()
35+
yield bucket
36+
bucket.delete(force=True)
37+
38+
39+
def test_get_rpo(dual_region_bucket, capsys):
40+
storage_get_rpo.get_rpo(dual_region_bucket.name)
41+
out, _ = capsys.readouterr()
42+
assert f"RPO for {dual_region_bucket.name} is DEFAULT." in out
43+
44+
45+
def test_set_rpo_async_turbo(dual_region_bucket, capsys):
46+
storage_set_rpo_async_turbo.set_rpo_async_turbo(dual_region_bucket.name)
47+
out, _ = capsys.readouterr()
48+
assert f"RPO is ASYNC_TURBO for {dual_region_bucket.name}." in out
49+
50+
51+
def test_set_rpo_default(dual_region_bucket, capsys):
52+
storage_set_rpo_default.set_rpo_default(dual_region_bucket.name)
53+
out, _ = capsys.readouterr()
54+
assert f"RPO is DEFAULT for {dual_region_bucket.name}." in out
55+
56+
57+
def test_create_bucket_turbo_replication(capsys):
58+
bucket_name = "test-rpo-{}".format(uuid.uuid4())
59+
storage_create_bucket_turbo_replication.create_bucket_turbo_replication(bucket_name)
60+
out, _ = capsys.readouterr()
61+
assert f"{bucket_name} created with RPO ASYNC_TURBO in NAM4." in out
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
#!/usr/bin/env python
2+
3+
# Copyright 2021 Google LLC
4+
#
5+
# Licensed under the Apache License, Version 2.0 (the 'License');
6+
# you may not use this file except in compliance with the License.
7+
# You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing, software
12+
# distributed under the License is distributed on an "AS IS" BASIS,
13+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
# See the License for the specific language governing permissions and
15+
# limitations under the License.
16+
17+
import sys
18+
19+
"""Sample that creates a new bucket with dual-region and turbo replication.
20+
This sample is used on this page:
21+
https://cloud.google.com/storage/docs/managing-turbo-replication
22+
For more information, see README.md.
23+
"""
24+
25+
# [START storage_create_bucket_turbo_replication]
26+
27+
from google.cloud import storage
28+
from google.cloud.storage.constants import RPO_ASYNC_TURBO
29+
30+
31+
def create_bucket_turbo_replication(bucket_name):
32+
"""Creates dual-region bucket with turbo replication enabled."""
33+
# The ID of your GCS bucket
34+
# bucket_name = "my-bucket"
35+
36+
storage_client = storage.Client()
37+
bucket = storage_client.bucket(bucket_name)
38+
bucket.location = "NAM4"
39+
bucket.rpo = RPO_ASYNC_TURBO
40+
bucket.create()
41+
42+
print(f"{bucket.name} created with RPO {bucket.rpo} in {bucket.location}.")
43+
44+
45+
# [END storage_create_bucket_turbo_replication]
46+
47+
if __name__ == "__main__":
48+
create_bucket_turbo_replication(bucket_name=sys.argv[1])

samples/snippets/storage_get_rpo.py

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
#!/usr/bin/env python
2+
3+
# Copyright 2021 Google LLC
4+
#
5+
# Licensed under the Apache License, Version 2.0 (the 'License');
6+
# you may not use this file except in compliance with the License.
7+
# You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing, software
12+
# distributed under the License is distributed on an "AS IS" BASIS,
13+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
# See the License for the specific language governing permissions and
15+
# limitations under the License.
16+
17+
import sys
18+
19+
"""Sample that gets RPO (Recovery Point Objective) of a bucket
20+
This sample is used on this page:
21+
https://cloud.google.com/storage/docs/managing-turbo-replication
22+
For more information, see README.md.
23+
"""
24+
25+
# [START storage_get_rpo]
26+
27+
from google.cloud import storage
28+
from google.cloud.storage.constants import RPO_DEFAULT
29+
30+
31+
def get_rpo(bucket_name):
32+
"""Gets the RPO of the bucket"""
33+
# The ID of your GCS bucket
34+
# bucket_name = "my-bucket"
35+
36+
storage_client = storage.Client()
37+
bucket = storage_client.bucket(bucket_name)
38+
39+
bucket.rpo = RPO_DEFAULT
40+
rpo = bucket.rpo
41+
42+
print(f"RPO for {bucket.name} is {rpo}.")
43+
44+
45+
# [END storage_get_rpo]
46+
47+
if __name__ == "__main__":
48+
get_rpo(bucket_name=sys.argv[1])
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
#!/usr/bin/env python
2+
3+
# Copyright 2021 Google LLC
4+
#
5+
# Licensed under the Apache License, Version 2.0 (the 'License');
6+
# you may not use this file except in compliance with the License.
7+
# You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing, software
12+
# distributed under the License is distributed on an "AS IS" BASIS,
13+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
# See the License for the specific language governing permissions and
15+
# limitations under the License.
16+
17+
import sys
18+
19+
"""Sample that sets RPO (Recovery Point Objective) to ASYNC_TURBO
20+
This sample is used on this page:
21+
https://cloud.google.com/storage/docs/managing-turbo-replication
22+
For more information, see README.md.
23+
"""
24+
25+
# [START storage_set_rpo_async_turbo]
26+
27+
from google.cloud import storage
28+
from google.cloud.storage.constants import RPO_ASYNC_TURBO
29+
30+
31+
def set_rpo_async_turbo(bucket_name):
32+
"""Sets the RPO to ASYNC_TURBO, enabling the turbo replication feature"""
33+
# The ID of your GCS bucket
34+
# bucket_name = "my-bucket"
35+
36+
storage_client = storage.Client()
37+
bucket = storage_client.bucket(bucket_name)
38+
39+
bucket.rpo = RPO_ASYNC_TURBO
40+
bucket.patch()
41+
42+
print(f"RPO is ASYNC_TURBO for {bucket.name}.")
43+
44+
45+
# [END storage_set_rpo_async_turbo]
46+
47+
if __name__ == "__main__":
48+
set_rpo_async_turbo(bucket_name=sys.argv[1])
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
#!/usr/bin/env python
2+
3+
# Copyright 2021 Google LLC
4+
#
5+
# Licensed under the Apache License, Version 2.0 (the 'License');
6+
# you may not use this file except in compliance with the License.
7+
# You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing, software
12+
# distributed under the License is distributed on an "AS IS" BASIS,
13+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
# See the License for the specific language governing permissions and
15+
# limitations under the License.
16+
17+
import sys
18+
19+
"""Sample that sets RPO (Recovery Point Objective) to default
20+
This sample is used on this page:
21+
https://cloud.google.com/storage/docs/managing-turbo-replication
22+
For more information, see README.md.
23+
"""
24+
25+
# [START storage_set_rpo_default]
26+
27+
from google.cloud import storage
28+
from google.cloud.storage.constants import RPO_DEFAULT
29+
30+
31+
def set_rpo_default(bucket_name):
32+
"""Sets the RPO to DEFAULT, disabling the turbo replication feature"""
33+
# The ID of your GCS bucket
34+
# bucket_name = "my-bucket"
35+
36+
storage_client = storage.Client()
37+
bucket = storage_client.bucket(bucket_name)
38+
39+
bucket.rpo = RPO_DEFAULT
40+
bucket.patch()
41+
42+
print(f"RPO is DEFAULT for {bucket.name}.")
43+
44+
45+
# [END storage_set_rpo_default]
46+
47+
if __name__ == "__main__":
48+
set_rpo_default(bucket_name=sys.argv[1])

tests/system/test_bucket.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -885,3 +885,22 @@ def test_new_bucket_created_w_enforced_pap(
885885
constants.PUBLIC_ACCESS_PREVENTION_INHERITED,
886886
]
887887
assert not bucket.iam_configuration.uniform_bucket_level_access_enabled
888+
889+
890+
def test_new_bucket_with_rpo(
891+
storage_client, buckets_to_delete, blobs_to_delete,
892+
):
893+
from google.cloud.storage import constants
894+
895+
bucket_name = _helpers.unique_name("new-w-turbo-replication")
896+
bucket = storage_client.create_bucket(bucket_name, location="NAM4")
897+
buckets_to_delete.append(bucket)
898+
899+
assert bucket.rpo == constants.RPO_DEFAULT
900+
901+
bucket.rpo = constants.RPO_ASYNC_TURBO
902+
bucket.patch()
903+
904+
bucket_from_server = storage_client.get_bucket(bucket_name)
905+
906+
assert bucket_from_server.rpo == constants.RPO_ASYNC_TURBO

tests/unit/test_bucket.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525
from google.cloud.storage.constants import PUBLIC_ACCESS_PREVENTION_ENFORCED
2626
from google.cloud.storage.constants import PUBLIC_ACCESS_PREVENTION_INHERITED
2727
from google.cloud.storage.constants import PUBLIC_ACCESS_PREVENTION_UNSPECIFIED
28+
from google.cloud.storage.constants import RPO_DEFAULT
29+
from google.cloud.storage.constants import RPO_ASYNC_TURBO
2830

2931

3032
def _create_signing_credentials():
@@ -2476,6 +2478,14 @@ def test_location_type_getter_set(self):
24762478
bucket = self._make_one(properties=properties)
24772479
self.assertEqual(bucket.location_type, REGION_LOCATION_TYPE)
24782480

2481+
def test_rpo_getter_and_setter(self):
2482+
bucket = self._make_one()
2483+
bucket.rpo = RPO_ASYNC_TURBO
2484+
self.assertEqual(bucket.rpo, RPO_ASYNC_TURBO)
2485+
bucket.rpo = RPO_DEFAULT
2486+
self.assertIn("rpo", bucket._changes)
2487+
self.assertEqual(bucket.rpo, RPO_DEFAULT)
2488+
24792489
def test_get_logging_w_prefix(self):
24802490
NAME = "name"
24812491
LOG_BUCKET = "logs"

0 commit comments

Comments
 (0)