diff --git a/docs/_components/storage-getting-started.rst b/docs/_components/storage-getting-started.rst index 0a16b3ddb2d1..48acc673b4bb 100644 --- a/docs/_components/storage-getting-started.rst +++ b/docs/_components/storage-getting-started.rst @@ -171,17 +171,17 @@ bucket itself as an iterator:: Deleting a bucket ----------------- -You can delete a bucket using the :func:`delete_bucket -` method:: +You can delete a bucket using the +:meth:`delete ` method:: - >>> connection.delete_bucket('my-bucket') + >>> bucket.delete() Remember, the bucket you're deleting needs to be empty, otherwise you'll -get an error. +get an error (409 conflict). If you have a full bucket, you can delete it this way:: - >>> bucket = connection.delete_bucket('my-bucket', force=True) + >>> bucket.delete(force=True) Listing available buckets ------------------------- diff --git a/gcloud/storage/bucket.py b/gcloud/storage/bucket.py index be9be8098cad..2564a6f5e303 100644 --- a/gcloud/storage/bucket.py +++ b/gcloud/storage/bucket.py @@ -322,7 +322,7 @@ def delete(self, force=False): # Ignore 404 errors on delete. self.delete_blobs(blobs, on_error=lambda blob: None) - self.connection.delete_bucket(self.name) + self.connection.api_request(method='DELETE', path=self.path) def delete_blob(self, blob): """Deletes a blob from the current bucket. diff --git a/gcloud/storage/connection.py b/gcloud/storage/connection.py index 632ba568a0e6..fa25eb371b78 100644 --- a/gcloud/storage/connection.py +++ b/gcloud/storage/connection.py @@ -31,10 +31,6 @@ class Connection(base_connection.Connection): :class:`gcloud.storage.bucket.Bucket` and :class:`gcloud.storage.blob.Blob`). - Methods for getting, creating and deleting individual buckets as well - as listing buckets associated with a project are defined here. This - corresponds to the "storage.buckets" resource in the API. - See :class:`gcloud.connection.Connection` for a full list of parameters. This subclass differs only in needing a project name (which you specify when creating a project in the Cloud @@ -46,12 +42,6 @@ class Connection(base_connection.Connection): >>> from gcloud import storage >>> connection = storage.get_connection(project) >>> bucket = connection.create_bucket('my-bucket-name') - - You can then delete this bucket:: - - >>> bucket.delete() - >>> # or - >>> connection.delete_bucket(bucket.name) """ API_BASE_URL = base_connection.API_BASE_URL @@ -280,38 +270,3 @@ def create_bucket(self, bucket_name): response = self.api_request(method='POST', path='/b', data={'name': bucket_name}) return Bucket(properties=response, connection=self) - - def delete_bucket(self, bucket_name): - """Delete a bucket. - - You can use this method to delete a bucket by name. - - >>> from gcloud import storage - >>> connection = storage.get_connection(project) - >>> connection.delete_bucket('my-bucket') - - If the bucket doesn't exist, this will raise a - :class:`gcloud.exceptions.NotFound`:: - - >>> from gcloud.exceptions import NotFound - >>> try: - >>> connection.delete_bucket('my-bucket') - >>> except NotFound: - >>> print 'That bucket does not exist!' - - If the bucket still has objects in it, this will raise a - :class:`gcloud.exceptions.Conflict`:: - - >>> from gcloud.exceptions import Conflict - >>> try: - >>> connection.delete_bucket('my-bucket') - >>> except Conflict: - >>> print 'That bucket is not empty!' - - This implements "storage.buckets.delete". - - :type bucket_name: string - :param bucket_name: The bucket name to delete. - """ - bucket_path = Bucket.path_helper(bucket_name) - self.api_request(method='DELETE', path=bucket_path) diff --git a/gcloud/storage/test_batch.py b/gcloud/storage/test_batch.py index eca9cf457a0f..df9e7e50a5a6 100644 --- a/gcloud/storage/test_batch.py +++ b/gcloud/storage/test_batch.py @@ -386,9 +386,6 @@ def api_request(self, method, path, query_params=None, def create_bucket(self, name): # pragma: NO COVER pass - def delete_bucket(self, name): # pragma: NO COVER - pass - class _Response(dict): diff --git a/gcloud/storage/test_bucket.py b/gcloud/storage/test_bucket.py index 4bef81ad5f4d..08811e200616 100644 --- a/gcloud/storage/test_bucket.py +++ b/gcloud/storage/test_bucket.py @@ -331,16 +331,18 @@ def test_delete_default_miss(self): connection = _Connection() bucket = self._makeOne(connection, NAME) self.assertRaises(NotFound, bucket.delete) - self.assertEqual(connection._deleted, [NAME]) + expected_cw = [{'method': 'DELETE', 'path': bucket.path}] + self.assertEqual(connection._deleted_buckets, expected_cw) def test_delete_explicit_hit(self): NAME = 'name' GET_BLOBS_RESP = {'items': []} connection = _Connection(GET_BLOBS_RESP) - connection._delete_ok = True + connection._delete_bucket = True bucket = self._makeOne(connection, NAME) self.assertEqual(bucket.delete(force=True), None) - self.assertEqual(connection._deleted, [NAME]) + expected_cw = [{'method': 'DELETE', 'path': bucket.path}] + self.assertEqual(connection._deleted_buckets, expected_cw) def test_delete_explicit_force_delete_blobs(self): NAME = 'name' @@ -355,10 +357,11 @@ def test_delete_explicit_force_delete_blobs(self): DELETE_BLOB1_RESP = DELETE_BLOB2_RESP = {} connection = _Connection(GET_BLOBS_RESP, DELETE_BLOB1_RESP, DELETE_BLOB2_RESP) - connection._delete_ok = True + connection._delete_bucket = True bucket = self._makeOne(connection, NAME) self.assertEqual(bucket.delete(force=True), None) - self.assertEqual(connection._deleted, [NAME]) + expected_cw = [{'method': 'DELETE', 'path': bucket.path}] + self.assertEqual(connection._deleted_buckets, expected_cw) def test_delete_explicit_force_miss_blobs(self): NAME = 'name' @@ -366,10 +369,11 @@ def test_delete_explicit_force_miss_blobs(self): GET_BLOBS_RESP = {'items': [{'name': BLOB_NAME}]} # Note the connection does not have a response for the blob. connection = _Connection(GET_BLOBS_RESP) - connection._delete_ok = True + connection._delete_bucket = True bucket = self._makeOne(connection, NAME) self.assertEqual(bucket.delete(force=True), None) - self.assertEqual(connection._deleted, [NAME]) + expected_cw = [{'method': 'DELETE', 'path': bucket.path}] + self.assertEqual(connection._deleted_buckets, expected_cw) def test_delete_explicit_too_many(self): NAME = 'name' @@ -382,12 +386,13 @@ def test_delete_explicit_too_many(self): ], } connection = _Connection(GET_BLOBS_RESP) - connection._delete_ok = True + connection._delete_bucket = True bucket = self._makeOne(connection, NAME) # Make the Bucket refuse to delete with 2 objects. bucket._MAX_OBJECTS_FOR_BUCKET_DELETE = 1 self.assertRaises(ValueError, bucket.delete, force=True) + self.assertEqual(connection._deleted_buckets, []) def test_delete_blob_miss(self): from gcloud.exceptions import NotFound @@ -1079,17 +1084,33 @@ def get_items_from_response(self, response): class _Connection(object): - _delete_ok = False + _delete_bucket = False def __init__(self, *responses): self._responses = responses self._requested = [] - self._deleted = [] + self._deleted_buckets = [] + + @staticmethod + def _is_bucket_path(path): + if not path.startswith('/b/'): # pragma: NO COVER + return False + # Now just ensure the path only has /b/ and one more segment. + return path.count('/') == 2 def api_request(self, **kw): from gcloud.exceptions import NotFound self._requested.append(kw) + method = kw.get('method') + path = kw.get('path', '') + if method == 'DELETE' and self._is_bucket_path(path): + self._deleted_buckets.append(kw) + if self._delete_bucket: + return + else: + raise NotFound('miss') + try: response, self._responses = self._responses[0], self._responses[1:] except: @@ -1097,13 +1118,6 @@ def api_request(self, **kw): else: return response - def delete_bucket(self, bucket): - from gcloud.exceptions import NotFound - self._deleted.append(bucket) - if not self._delete_ok: - raise NotFound('miss') - return True - class _Bucket(object): path = '/b/name' diff --git a/gcloud/storage/test_connection.py b/gcloud/storage/test_connection.py index e3567bbcc186..5ef730f39d42 100644 --- a/gcloud/storage/test_connection.py +++ b/gcloud/storage/test_connection.py @@ -303,29 +303,6 @@ def test_create_bucket_ok(self): self.assertEqual(http._called_with['method'], 'POST') self.assertEqual(http._called_with['uri'], URI) - def test_delete_bucket_defaults_miss(self): - _deleted_blobs = [] - - PROJECT = 'project' - BLOB_NAME = 'blob-name' - conn = self._makeOne(PROJECT) - URI = '/'.join([ - conn.API_BASE_URL, - 'storage', - conn.API_VERSION, - 'b', - '%s?project=%s' % (BLOB_NAME, PROJECT), - ]) - http = conn._http = Http( - {'status': '200', 'content-type': 'application/json'}, - '{}', - ) - - self.assertEqual(conn.delete_bucket(BLOB_NAME), None) - self.assertEqual(_deleted_blobs, []) - self.assertEqual(http._called_with['method'], 'DELETE') - self.assertEqual(http._called_with['uri'], URI) - class Http(object):