From 136300ed0d40a7f35ff3abc4de4c688cf598368a Mon Sep 17 00:00:00 2001 From: Sven Marnach Date: Thu, 11 Jul 2024 14:39:37 +0200 Subject: [PATCH] bug-1905455: Enable anonymous access to GCS buckets. --- tecken/ext/gcs/storage.py | 33 +++++++++++++++++++-------------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/tecken/ext/gcs/storage.py b/tecken/ext/gcs/storage.py index 00264c355..dc8c352c5 100644 --- a/tecken/ext/gcs/storage.py +++ b/tecken/ext/gcs/storage.py @@ -8,11 +8,12 @@ from urllib.parse import quote, urlparse from django.conf import settings - from google.api_core.client_options import ClientOptions -from google.api_core.exceptions import ClientError, NotFound +from google.api_core.exceptions import ClientError from google.cloud import storage +from requests.exceptions import RequestException +from tecken.librequests import session_with_retries from tecken.libstorage import ObjectMetadata, StorageBackend, StorageError @@ -52,13 +53,7 @@ def _get_client(self) -> storage.Client: def _get_bucket(self) -> storage.Bucket: """Return a thread-local low-level storage bucket client.""" - if not hasattr(self.clients, "bucket"): - client = self._get_client() - try: - self.clients.bucket = client.get_bucket(self.name, timeout=self.timeout) - except NotFound as exc: - raise StorageError(self) from exc - return self.clients.bucket + return self._get_client().bucket(self.name) def exists(self) -> bool: """Check that this storage exists. @@ -67,13 +62,23 @@ def exists(self) -> bool: :raises StorageError: an unexpected backend-specific error was raised """ + session = session_with_retries() + if self.endpoint_url.startswith("http://gcs-emulator"): + # NOTE(smarnach): The GCS emulator does not support HEAD requests. Moreover, the + # simpler public endpoint used below will throw 500s if the bucket doesn't exists, + # tripping up the retry behaviour. We can't use this endpoint for Google's API, + # since it requires authentication, and we want to be able to detect the existence + # of public buckets anonymously (bug 1905455). + method = "GET" + url = f"{self.endpoint_url}/storage/v1/b/{self.name}" + else: + method = "HEAD" + url = f"{self.endpoint_url}/{self.name}" try: - self._get_bucket() - except StorageError: - return False - except ClientError as exc: + response = session.request(method, url) + except RequestException as exc: raise StorageError(self) from exc - return True + return response.status_code == 200 def get_object_metadata(self, key: str) -> Optional[ObjectMetadata]: """Return object metadata for the object with the given key.