Skip to content

Commit

Permalink
Merge pull request #576 from tseaver/508-unify_http_exceptions
Browse files Browse the repository at this point in the history
Fix #508: unify http exceptions
  • Loading branch information
tseaver committed Jan 28, 2015
2 parents 2c75763 + 6f2796c commit bf93d46
Show file tree
Hide file tree
Showing 14 changed files with 142 additions and 183 deletions.
182 changes: 71 additions & 111 deletions docs/_components/storage-getting-started.rst
Original file line number Diff line number Diff line change
@@ -1,15 +1,12 @@
Getting started with Cloud Storage
==================================

This tutorial focuses on using ``gcloud`` to access
Google Cloud Storage.
We'll go through the basic concepts,
how to operate on buckets and blobs,
and how to handle access control,
among other things.
This tutorial focuses on using ``gcloud`` to access Google Cloud Storage.
We'll go through the basic concepts, how to operate on buckets and blobs,
and how to handle access control, among other things.

We're going to assume that you've already downloaded
and installed the library.
We're going to assume that you've already downloaded and installed the
library.

Creating a project
------------------
Expand All @@ -19,19 +16,15 @@ Creating a project
Enabling the API
----------------

Now that you created a project,
you need to **turn on** the Google Cloud Storage API.
This is sort of like telling Google
which services you intend to use for this project.
Now that you created a project, you need to **turn on** the Google Cloud
Storage API. This is sort of like telling Google which services you intend
to use for this project.

* **Click on APIs & Auth**
on the left hand side,
and scroll down to where it says
"Google Cloud Storage JSON API".
* **Click on APIs & Auth** on the left hand side, and scroll down to where
it says "Google Cloud Storage JSON API".

* **Click the "Off" button**
on the right side
to turn it into an "On" button.
* **Click the "Off" button** on the right side to turn it into an "On"
button.

Enabling a service account
--------------------------
Expand All @@ -41,31 +34,25 @@ Enabling a service account
Creating a connection
---------------------

The first step in accessing Cloud Storage
is to create a connection to the service::
The first step in accessing Cloud Storage is to create a connection to the
service::

>>> from gcloud import storage
>>> connection = storage.get_connection(project_name)

We're going to use this
:class:`connection <gcloud.storage.connection.Connection>` object
for the rest of this guide.
We're going to use this :class:`connection
<gcloud.storage.connection.Connection>` object for the rest of this guide.

Creating a bucket
-----------------

Once you've established a connection to Cloud Storage,
the first thing you typically want to do is
create a new bucket.
A bucket is a container used to store
objects in Cloud Storage
(if you're familiar with S3,
buckets on Cloud Storage mean the same thing).
Think of each bucket as a single "disk drive",
where you can store lots of files on each.
How you organize your data is up to you,
but it's typical to group common data
in a single bucket.
Once you've established a connection to Cloud Storage, the first thing you
typically want to do is create a new bucket. A bucket is a container used
to store objects in Cloud Storage (if you're familiar with S3, buckets on
Cloud Storage mean the same thing). Think of each bucket as a single "disk
drive", where you can store lots of files on each. How you organize your
data is up to you, but it's typical to group common data in a single
bucket.

Let's create a bucket:

Expand All @@ -74,59 +61,41 @@ Let's create a bucket:
File "<stdin>", line 1, in <module>
File "gcloud/storage/connection.py", line 340, in create_bucket
data={'name': bucket.name})
File "gcloud/storage/connection.py", line 224, in api_request
raise exceptions.ConnectionError(response, content)
gcloud.storage.exceptions.ConnectionError: {'status': '409', 'alternate-protocol': '443:quic', 'content-length': '271', 'x-xss-protection': '1; mode=block', 'x-content-type-options': 'nosniff', 'expires': 'Sat, 15 Mar 2014 19:19:47 GMT', 'server': 'GSE', '-content-encoding': 'gzip', 'cache-control': 'private, max-age=0', 'date': 'Sat, 15 Mar 2014 19:19:47 GMT', 'x-frame-options': 'SAMEORIGIN', 'content-type': 'application/json; charset=UTF-8'}{
"error": {
"errors": [
{
"domain": "global",
"reason": "conflict",
"message": "Sorry, that name is not available. Please try a different one."
}
],
"code": 409,
"message": "Sorry, that name is not available. Please try a different one."
}
}
File "gcloud/storage/connection.py", line 324, in api_request
raise make_exception(response, content)
...

**Whoops!**
It might be important to mention
that bucket names are like domain names:
it's one big namespace that we all share,
so you have to pick a bucket name that isn't already taken.

It's up to you to decide what a good name is,
let's assume that you found a unique name
and are ready to move on with your newly created bucket.
It might be important to mention that bucket names are like domain names:
it's one big namespace that we all share, so you have to pick a bucket name
that isn't already taken.

It's up to you to decide what a good name is, let's assume that you found a
unique name and are ready to move on with your newly created bucket.

Storing data
------------

OK, so you have a bucket. Now what?
Cloud Storage is just an arbitrary data container,
so you can put whatever format of data you want.
The naming of your files is also arbitrary,
however the Cloud Storage online file browser
tries to make it feel a bit like a file system
by recognizing forward-slashes (``/``)
so if you want to group data into "directories",
OK, so you have a bucket. Now what? Cloud Storage is just an arbitrary
data container, so you can put whatever format of data you want. The
naming of your files is also arbitrary, however the Cloud Storage online
file browser tries to make it feel a bit like a file system by recognizing
forward-slashes (``/``) so if you want to group data into "directories",
you can do that.

The fundamental container for a file in Cloud Storage
is called an Object, however ``gcloud`` uses the term ``Blob``
to avoid confusion with the Python built-in ``object``.
The fundamental container for a file in Cloud Storage is called an Object,
however ``gcloud`` uses the term ``Blob`` to avoid confusion with the
Python built-in ``object``.

If you want to set some data,
you just create a ``Blob`` inside your bucket
If you want to set some data, you just create a ``Blob`` inside your bucket
and store your data inside the blob::

>>> blob = bucket.new_blob('greeting.txt')
>>> blob.upload_from_string('Hello world!')

:func:`new_blob <gcloud.storage.bucket.Bucket.new_blob>`
creates a :class:`Blob <gcloud.storage.blob.Blob>` object locally
and
:func:`new_blob <gcloud.storage.bucket.Bucket.new_blob>` creates a
:class:`Blob <gcloud.storage.blob.Blob>` object locally and
:func:`upload_from_string <gcloud.storage.blob.Blob.upload_from_string>`
allows you to put a string into the blob.

Expand Down Expand Up @@ -160,21 +129,19 @@ and check if they are the same in a terminal::

$ diff kitten.jpg kitten2.jpg

Notice that we're using
:func:`get_blob <gcloud.storage.bucket.Bucket.get_blob>`
to retrieve a blob we know exists remotely.
If the blob doesn't exist, it will return ``None``.
Notice that we're using :func:`get_blob
<gcloud.storage.bucket.Bucket.get_blob>` to retrieve a blob we know exists
remotely. If the blob doesn't exist, it will return ``None``.

.. note:: ``get_blob`` is **not** retrieving the entire object's data.

If you want to "get-or-create" the blob
(that is, overwrite it if it already exists),
you can use :func:`new_blob <gcloud.storage.bucket.Bucket.new_blob>`.
However, keep in mind, the blob is not created
until you store some data inside of it.
If you want to "get-or-create" the blob (that is, overwrite it if it
already exists), you can use :func:`new_blob
<gcloud.storage.bucket.Bucket.new_blob>`. However, keep in mind, the blob
is not created until you store some data inside of it.

If you want to check whether a blob exists,
you can use the ``in`` operator in Python::
If you want to check whether a blob exists, you can use the ``in`` operator
in Python::

>>> print 'kitten.jpg' in bucket
True
Expand All @@ -184,35 +151,33 @@ you can use the ``in`` operator in Python::
Accessing a bucket
------------------

If you already have a bucket,
use :func:`get_bucket <gcloud.storage.connection.Connection.get_bucket>`
to retrieve the bucket object::
If you already have a bucket, use :func:`get_bucket
<gcloud.storage.connection.Connection.get_bucket>` to retrieve the bucket
object::

>>> bucket = connection.get_bucket('my-bucket')

If you want to get all the blobs in the bucket,
you can use
If you want to get all the blobs in the bucket, you can use
:func:`get_all_blobs <gcloud.storage.bucket.Bucket.get_all_blobs>`::

>>> blobs = bucket.get_all_blobs()

However, if you're looking to iterate through the blobs,
you can use the bucket itself as an iterator::
However, if you're looking to iterate through the blobs, you can use the
bucket itself as an iterator::

>>> for blob in bucket:
... print blob

Deleting a bucket
-----------------

You can delete a bucket using the
:func:`delete_bucket <gcloud.storage.connection.Connection.delete_bucket>`
method::
You can delete a bucket using the :func:`delete_bucket
<gcloud.storage.connection.Connection.delete_bucket>` method::

>>> connection.delete_bucket('my-bucket')

Remember, the bucket you're deleting needs to be empty,
otherwise you'll get an error.
Remember, the bucket you're deleting needs to be empty, otherwise you'll
get an error.

If you have a full bucket, you can delete it this way::

Expand All @@ -221,9 +186,9 @@ If you have a full bucket, you can delete it this way::
Listing available buckets
-------------------------

The :class:`Connection <gcloud.storage.connection.Connection>`
object itself is iterable,
so you can loop over it, or call ``list`` on it to get a list object::
The :class:`Connection <gcloud.storage.connection.Connection>` object
itself is iterable, so you can loop over it, or call ``list`` on it to get
a list object::

>>> for bucket in connection:
... print bucket.name
Expand All @@ -232,19 +197,14 @@ so you can loop over it, or call ``list`` on it to get a list object::
Managing access control
-----------------------

Cloud storage provides fine-grained access control
for both buckets and blobs.
`gcloud` tries to simplify access control
by working with entities and "grants".
On any ACL,
you get a reference to an entity,
and then either grant or revoke a specific access level.
Additionally, we provide two default entities:
all users, and all authenticated users.
Cloud storage provides fine-grained access control for both buckets and
blobs. `gcloud` tries to simplify access control by working with entities
and "grants". On any ACL, you get a reference to an entity, and then
either grant or revoke a specific access level. Additionally, we provide
two default entities: all users, and all authenticated users.

For example, if you want to grant read access to all users on your bucket::

>>> bucket.get_acl().all().grant_read()

For more detail on access control,
see :mod:`gcloud.storage.acl`.
For more detail on access control, see :mod:`gcloud.storage.acl`.
8 changes: 8 additions & 0 deletions docs/gcloud-api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,11 @@ Credentials
:members:
:undoc-members:
:show-inheritance:

Exceptions
~~~~~~~~~~

.. automodule:: gcloud.exceptions
:members:
:undoc-members:
:show-inheritance:
8 changes: 0 additions & 8 deletions docs/storage-api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,3 @@ Iterators
:members:
:undoc-members:
:show-inheritance:

Exceptions
~~~~~~~~~~

.. automodule:: gcloud.storage.exceptions
:members:
:undoc-members:
:show-inheritance:
9 changes: 3 additions & 6 deletions gcloud/datastore/connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,8 @@

"""Connections to gcloud datastore API servers."""

import six

from gcloud import connection
from gcloud.exceptions import make_exception
from gcloud.datastore import _datastore_v1_pb2 as datastore_pb
from gcloud.datastore import helpers

Expand Down Expand Up @@ -54,7 +53,7 @@ def _request(self, dataset_id, method, data):
:rtype: string
:returns: The string response content from the API call.
:raises: :class:`six.moves.http_client.HTTPException` if the response
:raises: :class:`gcloud.exceptions.GCloudError` if the response
code is not 200 OK.
"""
headers = {
Expand All @@ -68,9 +67,7 @@ def _request(self, dataset_id, method, data):

status = headers['status']
if status != '200':
message = ('Request failed with status code %s. '
'Error was: %s' % (status, content))
raise six.moves.http_client.HTTPException(message)
raise make_exception(headers, content)

return content

Expand Down
14 changes: 7 additions & 7 deletions gcloud/datastore/test_connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,17 +100,16 @@ def test__request_w_200(self):
self.assertEqual(http._called_with['body'], DATA)

def test__request_not_200(self):
import six
from gcloud.exceptions import BadRequest

DATASET_ID = 'DATASET'
METHOD = 'METHOD'
DATA = 'DATA'
conn = self._makeOne()
conn._http = Http({'status': '400'}, 'Bad Request')
with self.assertRaises(six.moves.http_client.HTTPException) as e:
conn._http = Http({'status': '400'}, '{"message": "Bad Request"}')
with self.assertRaises(BadRequest) as e:
conn._request(DATASET_ID, METHOD, DATA)
expected_message = ('Request failed with status code 400. '
'Error was: Bad Request')
expected_message = ('400 Bad Request')
self.assertEqual(str(e.exception), expected_message)

def test__rpc(self):
Expand Down Expand Up @@ -845,12 +844,13 @@ class Http(object):
_called_with = None

def __init__(self, headers, content):
self._headers = headers
from httplib2 import Response
self._response = Response(headers)
self._content = content

def request(self, **kw):
self._called_with = kw
return self._headers, self._content
return self._response, self._content


class HttpMultiple(object):
Expand Down
Loading

0 comments on commit bf93d46

Please sign in to comment.