Skip to content

Commit

Permalink
fix: add preconditions and retry config support to ACL patch operatio…
Browse files Browse the repository at this point in the history
…nss (#586)

* add preconditions and retry config support to ACL patch operations

* update existing unit tests

* add unit tests

* add preconditions and retry config to bucket make public/private

* add preconditions and retry config to blob make public/private

* update docstrings

* add system tests acl with metegeneration match

* revise to use permitted group email
  • Loading branch information
cojenco committed Sep 15, 2021
1 parent 3d43049 commit 4333caf
Show file tree
Hide file tree
Showing 8 changed files with 639 additions and 44 deletions.
175 changes: 165 additions & 10 deletions google/cloud/storage/acl.py
Expand Up @@ -84,8 +84,10 @@
when sending metadata for ACLs to the API.
"""

from google.cloud.storage._helpers import _add_generation_match_parameters
from google.cloud.storage.constants import _DEFAULT_TIMEOUT
from google.cloud.storage.retry import DEFAULT_RETRY
from google.cloud.storage.retry import DEFAULT_RETRY_IF_METAGENERATION_SPECIFIED


class _ACLEntity(object):
Expand Down Expand Up @@ -465,7 +467,18 @@ def reload(self, client=None, timeout=_DEFAULT_TIMEOUT, retry=DEFAULT_RETRY):
for entry in found.get("items", ()):
self.add_entity(self.entity_from_dict(entry))

def _save(self, acl, predefined, client, timeout=_DEFAULT_TIMEOUT):
def _save(
self,
acl,
predefined,
client,
if_generation_match=None,
if_generation_not_match=None,
if_metageneration_match=None,
if_metageneration_not_match=None,
timeout=_DEFAULT_TIMEOUT,
retry=DEFAULT_RETRY_IF_METAGENERATION_SPECIFIED,
):
"""Helper for :meth:`save` and :meth:`save_predefined`.
:type acl: :class:`google.cloud.storage.acl.ACL`, or a compatible list.
Expand All @@ -481,12 +494,28 @@ def _save(self, acl, predefined, client, timeout=_DEFAULT_TIMEOUT):
:param client: (Optional) The client to use. If not passed, falls back
to the ``client`` stored on the ACL's parent.
:type if_generation_match: long
:param if_generation_match:
(Optional) See :ref:`using-if-generation-match`
:type if_generation_not_match: long
:param if_generation_not_match:
(Optional) See :ref:`using-if-generation-not-match`
:type if_metageneration_match: long
:param if_metageneration_match:
(Optional) See :ref:`using-if-metageneration-match`
:type if_metageneration_not_match: long
:param if_metageneration_not_match:
(Optional) See :ref:`using-if-metageneration-not-match`
:type timeout: float or tuple
:param timeout:
(Optional) The amount of time, in seconds, to wait
for the server response. See: :ref:`configuring_timeouts`
:type retry: :class:`~google.api_core.retry.Retry`
:type retry: google.api_core.retry.Retry or google.cloud.storage.retry.ConditionalRetryPolicy
:param retry:
(Optional) How to retry the RPC. See: :ref:`configuring_retries`
"""
Expand All @@ -500,14 +529,22 @@ def _save(self, acl, predefined, client, timeout=_DEFAULT_TIMEOUT):
if self.user_project is not None:
query_params["userProject"] = self.user_project

_add_generation_match_parameters(
query_params,
if_generation_match=if_generation_match,
if_generation_not_match=if_generation_not_match,
if_metageneration_match=if_metageneration_match,
if_metageneration_not_match=if_metageneration_not_match,
)

path = self.save_path

result = client._patch_resource(
path,
{self._URL_PATH_ELEM: list(acl)},
query_params=query_params,
timeout=timeout,
retry=None,
retry=retry,
)

self.entities.clear()
Expand All @@ -517,7 +554,17 @@ def _save(self, acl, predefined, client, timeout=_DEFAULT_TIMEOUT):

self.loaded = True

def save(self, acl=None, client=None, timeout=_DEFAULT_TIMEOUT):
def save(
self,
acl=None,
client=None,
if_generation_match=None,
if_generation_not_match=None,
if_metageneration_match=None,
if_metageneration_not_match=None,
timeout=_DEFAULT_TIMEOUT,
retry=DEFAULT_RETRY_IF_METAGENERATION_SPECIFIED,
):
"""Save this ACL for the current bucket.
If :attr:`user_project` is set, bills the API request to that project.
Expand All @@ -531,10 +578,30 @@ def save(self, acl=None, client=None, timeout=_DEFAULT_TIMEOUT):
:param client: (Optional) The client to use. If not passed, falls back
to the ``client`` stored on the ACL's parent.
:type if_generation_match: long
:param if_generation_match:
(Optional) See :ref:`using-if-generation-match`
:type if_generation_not_match: long
:param if_generation_not_match:
(Optional) See :ref:`using-if-generation-not-match`
:type if_metageneration_match: long
:param if_metageneration_match:
(Optional) See :ref:`using-if-metageneration-match`
:type if_metageneration_not_match: long
:param if_metageneration_not_match:
(Optional) See :ref:`using-if-metageneration-not-match`
:type timeout: float or tuple
:param timeout:
(Optional) The amount of time, in seconds, to wait
for the server response. See: :ref:`configuring_timeouts`
:type retry: google.api_core.retry.Retry or google.cloud.storage.retry.ConditionalRetryPolicy
:param retry:
(Optional) How to retry the RPC. See: :ref:`configuring_retries`
"""
if acl is None:
acl = self
Expand All @@ -543,9 +610,29 @@ def save(self, acl=None, client=None, timeout=_DEFAULT_TIMEOUT):
save_to_backend = True

if save_to_backend:
self._save(acl, None, client, timeout=timeout)

def save_predefined(self, predefined, client=None, timeout=_DEFAULT_TIMEOUT):
self._save(
acl,
None,
client,
if_generation_match=if_generation_match,
if_generation_not_match=if_generation_not_match,
if_metageneration_match=if_metageneration_match,
if_metageneration_not_match=if_metageneration_not_match,
timeout=timeout,
retry=retry,
)

def save_predefined(
self,
predefined,
client=None,
if_generation_match=None,
if_generation_not_match=None,
if_metageneration_match=None,
if_metageneration_not_match=None,
timeout=_DEFAULT_TIMEOUT,
retry=DEFAULT_RETRY_IF_METAGENERATION_SPECIFIED,
):
"""Save this ACL for the current bucket using a predefined ACL.
If :attr:`user_project` is set, bills the API request to that project.
Expand All @@ -562,15 +649,54 @@ def save_predefined(self, predefined, client=None, timeout=_DEFAULT_TIMEOUT):
:param client: (Optional) The client to use. If not passed, falls back
to the ``client`` stored on the ACL's parent.
:type if_generation_match: long
:param if_generation_match:
(Optional) See :ref:`using-if-generation-match`
:type if_generation_not_match: long
:param if_generation_not_match:
(Optional) See :ref:`using-if-generation-not-match`
:type if_metageneration_match: long
:param if_metageneration_match:
(Optional) See :ref:`using-if-metageneration-match`
:type if_metageneration_not_match: long
:param if_metageneration_not_match:
(Optional) See :ref:`using-if-metageneration-not-match`
:type timeout: float or tuple
:param timeout:
(Optional) The amount of time, in seconds, to wait
for the server response. See: :ref:`configuring_timeouts`
:type retry: google.api_core.retry.Retry or google.cloud.storage.retry.ConditionalRetryPolicy
:param retry:
(Optional) How to retry the RPC. See: :ref:`configuring_retries`
"""
predefined = self.validate_predefined(predefined)
self._save(None, predefined, client, timeout=timeout)
self._save(
None,
predefined,
client,
if_generation_match=if_generation_match,
if_generation_not_match=if_generation_not_match,
if_metageneration_match=if_metageneration_match,
if_metageneration_not_match=if_metageneration_not_match,
timeout=timeout,
retry=retry,
)

def clear(self, client=None, timeout=_DEFAULT_TIMEOUT):
def clear(
self,
client=None,
if_generation_match=None,
if_generation_not_match=None,
if_metageneration_match=None,
if_metageneration_not_match=None,
timeout=_DEFAULT_TIMEOUT,
retry=DEFAULT_RETRY_IF_METAGENERATION_SPECIFIED,
):
"""Remove all ACL entries.
If :attr:`user_project` is set, bills the API request to that project.
Expand All @@ -585,12 +711,41 @@ def clear(self, client=None, timeout=_DEFAULT_TIMEOUT):
:param client: (Optional) The client to use. If not passed, falls back
to the ``client`` stored on the ACL's parent.
:type if_generation_match: long
:param if_generation_match:
(Optional) See :ref:`using-if-generation-match`
:type if_generation_not_match: long
:param if_generation_not_match:
(Optional) See :ref:`using-if-generation-not-match`
:type if_metageneration_match: long
:param if_metageneration_match:
(Optional) See :ref:`using-if-metageneration-match`
:type if_metageneration_not_match: long
:param if_metageneration_not_match:
(Optional) See :ref:`using-if-metageneration-not-match`
:type timeout: float or tuple
:param timeout:
(Optional) The amount of time, in seconds, to wait
for the server response. See: :ref:`configuring_timeouts`
:type retry: google.api_core.retry.Retry or google.cloud.storage.retry.ConditionalRetryPolicy
:param retry:
(Optional) How to retry the RPC. See: :ref:`configuring_retries`
"""
self.save([], client=client, timeout=timeout)
self.save(
[],
client=client,
if_generation_match=if_generation_match,
if_generation_not_match=if_generation_not_match,
if_metageneration_match=if_metageneration_match,
if_metageneration_not_match=if_metageneration_not_match,
timeout=timeout,
retry=retry,
)


class BucketACL(ACL):
Expand Down
82 changes: 78 additions & 4 deletions google/cloud/storage/blob.py
Expand Up @@ -83,6 +83,7 @@
from google.cloud.storage.retry import DEFAULT_RETRY
from google.cloud.storage.retry import DEFAULT_RETRY_IF_ETAG_IN_JSON
from google.cloud.storage.retry import DEFAULT_RETRY_IF_GENERATION_SPECIFIED
from google.cloud.storage.retry import DEFAULT_RETRY_IF_METAGENERATION_SPECIFIED
from google.cloud.storage.fileio import BlobReader
from google.cloud.storage.fileio import BlobWriter

Expand Down Expand Up @@ -3226,7 +3227,16 @@ def test_iam_permissions(

return resp.get("permissions", [])

def make_public(self, client=None, timeout=_DEFAULT_TIMEOUT):
def make_public(
self,
client=None,
timeout=_DEFAULT_TIMEOUT,
if_generation_match=None,
if_generation_not_match=None,
if_metageneration_match=None,
if_metageneration_not_match=None,
retry=DEFAULT_RETRY_IF_METAGENERATION_SPECIFIED,
):
"""Update blob's ACL, granting read access to anonymous users.
:type client: :class:`~google.cloud.storage.client.Client` or
Expand All @@ -3239,11 +3249,47 @@ def make_public(self, client=None, timeout=_DEFAULT_TIMEOUT):
(Optional) The amount of time, in seconds, to wait
for the server response. See: :ref:`configuring_timeouts`
:type if_generation_match: long
:param if_generation_match:
(Optional) See :ref:`using-if-generation-match`
:type if_generation_not_match: long
:param if_generation_not_match:
(Optional) See :ref:`using-if-generation-not-match`
:type if_metageneration_match: long
:param if_metageneration_match:
(Optional) See :ref:`using-if-metageneration-match`
:type if_metageneration_not_match: long
:param if_metageneration_not_match:
(Optional) See :ref:`using-if-metageneration-not-match`
:type retry: google.api_core.retry.Retry or google.cloud.storage.retry.ConditionalRetryPolicy
:param retry:
(Optional) How to retry the RPC. See: :ref:`configuring_retries`
"""
self.acl.all().grant_read()
self.acl.save(client=client, timeout=timeout)
self.acl.save(
client=client,
timeout=timeout,
if_generation_match=if_generation_match,
if_generation_not_match=if_generation_not_match,
if_metageneration_match=if_metageneration_match,
if_metageneration_not_match=if_metageneration_not_match,
retry=retry,
)

def make_private(self, client=None, timeout=_DEFAULT_TIMEOUT):
def make_private(
self,
client=None,
timeout=_DEFAULT_TIMEOUT,
if_generation_match=None,
if_generation_not_match=None,
if_metageneration_match=None,
if_metageneration_not_match=None,
retry=DEFAULT_RETRY_IF_METAGENERATION_SPECIFIED,
):
"""Update blob's ACL, revoking read access for anonymous users.
:type client: :class:`~google.cloud.storage.client.Client` or
Expand All @@ -3255,9 +3301,37 @@ def make_private(self, client=None, timeout=_DEFAULT_TIMEOUT):
:param timeout:
(Optional) The amount of time, in seconds, to wait
for the server response. See: :ref:`configuring_timeouts`
:type if_generation_match: long
:param if_generation_match:
(Optional) See :ref:`using-if-generation-match`
:type if_generation_not_match: long
:param if_generation_not_match:
(Optional) See :ref:`using-if-generation-not-match`
:type if_metageneration_match: long
:param if_metageneration_match:
(Optional) See :ref:`using-if-metageneration-match`
:type if_metageneration_not_match: long
:param if_metageneration_not_match:
(Optional) See :ref:`using-if-metageneration-not-match`
:type retry: google.api_core.retry.Retry or google.cloud.storage.retry.ConditionalRetryPolicy
:param retry:
(Optional) How to retry the RPC. See: :ref:`configuring_retries`
"""
self.acl.all().revoke_read()
self.acl.save(client=client, timeout=timeout)
self.acl.save(
client=client,
timeout=timeout,
if_generation_match=if_generation_match,
if_generation_not_match=if_generation_not_match,
if_metageneration_match=if_metageneration_match,
if_metageneration_not_match=if_metageneration_not_match,
retry=retry,
)

def compose(
self,
Expand Down

0 comments on commit 4333caf

Please sign in to comment.