Repository: libcloud
Updated Branches:
  refs/heads/trunk 17c2217b5 -> 022d01edd


Improvements to Google Auth for Storage and Compute and MIME bug fix

Signed-off-by: Eric Johnson <erjoh...@google.com>


Project: http://git-wip-us.apache.org/repos/asf/libcloud/repo
Commit: http://git-wip-us.apache.org/repos/asf/libcloud/commit/022d01ed
Tree: http://git-wip-us.apache.org/repos/asf/libcloud/tree/022d01ed
Diff: http://git-wip-us.apache.org/repos/asf/libcloud/diff/022d01ed

Branch: refs/heads/trunk
Commit: 022d01edd63bb018dd3054bea3daaff23570f37f
Parents: 17c2217
Author: Scott Crunkleton <crunkle...@google.com>
Authored: Mon Feb 1 16:03:44 2016 -0800
Committer: Eric Johnson <erjoh...@google.com>
Committed: Tue Feb 9 17:12:37 2016 +0000

----------------------------------------------------------------------
 CHANGES.rst                                  |   8 +
 libcloud/common/google.py                    | 183 +++++++++++-----------
 libcloud/compute/drivers/gce.py              |   3 +-
 libcloud/storage/drivers/google_storage.py   |  39 +++--
 libcloud/test/common/test_google.py          | 135 +++++++++-------
 libcloud/test/storage/test_google_storage.py | 130 ++++-----------
 6 files changed, 229 insertions(+), 269 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/libcloud/blob/022d01ed/CHANGES.rst
----------------------------------------------------------------------
diff --git a/CHANGES.rst b/CHANGES.rst
index 369d810..0e99f0f 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -29,6 +29,14 @@ Compute
   (GITHUB-691)
   [Jeff Dunham]
 
+Storage
+~~~~~~~
+
+- Improvements to Google Auth for Storage and Compute and MIME bug fix
+  (LIBCLOUD-800, GITHUB-689)
+  [Scott Crunkleton]
+
+
 Changes with Apache Libcloud 1.0.0-pre1
 ---------------------------------------
 

http://git-wip-us.apache.org/repos/asf/libcloud/blob/022d01ed/libcloud/common/google.py
----------------------------------------------------------------------
diff --git a/libcloud/common/google.py b/libcloud/common/google.py
index 07cf553..7378d8e 100644
--- a/libcloud/common/google.py
+++ b/libcloud/common/google.py
@@ -609,6 +609,94 @@ class GoogleAuthType(object):
         return user_id.endswith('.gserviceaccount.com')
 
 
+class GoogleOAuth2Credential(object):
+    default_credential_file = '~/.google_libcloud_auth'
+
+    def __init__(self, user_id, key, auth_type=None, credential_file=None,
+                 scopes=None, **kwargs):
+        self.auth_type = auth_type or GoogleAuthType.guess_type(user_id)
+        if self.auth_type not in GoogleAuthType.ALL_TYPES:
+            raise GoogleAuthError('Invalid auth type: %s' % self.auth_type)
+        if not GoogleAuthType.is_oauth2(self.auth_type):
+            raise GoogleAuthError(('Auth type %s cannot be used with OAuth2' %
+                                   self.auth_type))
+        self.user_id = user_id
+        self.key = key
+
+        default_credential_file = '.'.join([self.default_credential_file,
+                                            user_id])
+        self.credential_file = credential_file or default_credential_file
+        # Default scopes to read/write for compute, storage, and dns.
+        self.scopes = scopes or [
+            'https://www.googleapis.com/auth/compute',
+            'https://www.googleapis.com/auth/devstorage.full_control',
+            'https://www.googleapis.com/auth/ndev.clouddns.readwrite',
+        ]
+
+        self.token = self._get_token_from_file()
+
+        if self.auth_type == GoogleAuthType.GCE:
+            self.oauth2_conn = GoogleGCEServiceAcctAuthConnection(
+                self.user_id, self.scopes, **kwargs)
+        elif self.auth_type == GoogleAuthType.SA:
+            self.oauth2_conn = GoogleServiceAcctAuthConnection(
+                self.user_id, self.key, self.scopes, **kwargs)
+        elif self.auth_type == GoogleAuthType.IA:
+            self.oauth2_conn = GoogleInstalledAppAuthConnection(
+                self.user_id, self.key, self.scopes, **kwargs)
+        else:
+            raise GoogleAuthError('Invalid auth_type: %s' %
+                                  str(self.auth_type))
+
+        if self.token is None:
+            self.token = self.oauth2_conn.get_new_token()
+            self._write_token_to_file()
+
+    @property
+    def access_token(self):
+        if self.token_expire_utc_datetime < _utcnow():
+            self._refresh_token()
+        return self.token['access_token']
+
+    @property
+    def token_expire_utc_datetime(self):
+        return _from_utc_timestamp(self.token['expire_time'])
+
+    def _refresh_token(self):
+        self.token = self.oauth2_conn.refresh_token(self.token)
+        self._write_token_to_file()
+
+    def _get_token_from_file(self):
+        """
+        Read credential file and return token information.
+        Mocked in libcloud.test.common.google.GoogleTestCase.
+
+        :return:  Token information dictionary, or None
+        :rtype:   ``dict`` or ``None``
+        """
+        token = None
+        filename = os.path.realpath(os.path.expanduser(self.credential_file))
+
+        try:
+            with open(filename, 'r') as f:
+                data = f.read()
+            token = json.loads(data)
+        except IOError:
+            pass
+        return token
+
+    def _write_token_to_file(self):
+        """
+        Write token to credential file.
+        Mocked in libcloud.test.common.google.GoogleTestCase.
+        """
+        filename = os.path.realpath(os.path.expanduser(self.credential_file))
+        data = json.dumps(self.token)
+        with os.fdopen(os.open(filename, os.O_CREAT | os.O_WRONLY,
+                               int('600', 8)), 'w') as f:
+            f.write(data)
+
+
 class GoogleBaseConnection(ConnectionUserAndKey, PollingConnection):
     """Base connection class for interacting with Google APIs."""
     driver = GoogleBaseDriver
@@ -616,7 +704,6 @@ class GoogleBaseConnection(ConnectionUserAndKey, 
PollingConnection):
     host = 'www.googleapis.com'
     poll_interval = 2.0
     timeout = 180
-    credential_file = '~/.google_libcloud_auth'
 
     def __init__(self, user_id, key=None, auth_type=None,
                  credential_file=None, scopes=None, **kwargs):
@@ -648,35 +735,16 @@ class GoogleBaseConnection(ConnectionUserAndKey, 
PollingConnection):
                           read/write access to Compute, Storage, and DNS.
         :type     scopes: ``list``
         """
-        self.user_id = user_id
-        self.key = key
-        if auth_type and auth_type not in GoogleAuthType.ALL_TYPES:
-            raise GoogleAuthError('Invalid auth type: %s' % auth_type)
-        self.auth_type = auth_type or GoogleAuthType.guess_type(user_id)
-
-        # OAuth2 stuff and placeholders
-        self.scopes = scopes
-        self.oauth2_conn = None
-        self.oauth2_token = None
-        if credential_file:
-            self.credential_file = credential_file
-        elif self.auth_type == GoogleAuthType.SA:
-            self.credential_file += '.' + user_id
-
-        if GoogleAuthType.is_oauth2(self.auth_type):
-            self._init_oauth2(**kwargs)
-
         super(GoogleBaseConnection, self).__init__(user_id, key, **kwargs)
 
+        self.oauth2_credential = GoogleOAuth2Credential(
+            user_id, key, auth_type, credential_file, scopes, **kwargs)
+
         python_ver = '%s.%s.%s' % (sys.version_info[0], sys.version_info[1],
                                    sys.version_info[2])
         ver_platform = 'Python %s/%s' % (python_ver, sys.platform)
         self.user_agent_append(ver_platform)
 
-    @property
-    def token_expire_utc_datetime(self):
-        return _from_utc_timestamp(self.oauth2_token['expire_time'])
-
     def add_default_headers(self, headers):
         """
         @inherits: :class:`Connection.add_default_headers`
@@ -692,11 +760,8 @@ class GoogleBaseConnection(ConnectionUserAndKey, 
PollingConnection):
 
         @inherits: :class:`Connection.pre_connect_hook`
         """
-        if self.token_expire_utc_datetime < _utcnow():
-            self._refresh_oauth2_token()
-        headers['Authorization'] = 'Bearer %s' % (
-            self.oauth2_token['access_token'])
-
+        headers['Authorization'] = ('Bearer ' +
+                                    self.oauth2_credential.access_token)
         return params, headers
 
     def encode_data(self, data):
@@ -766,65 +831,3 @@ class GoogleBaseConnection(ConnectionUserAndKey, 
PollingConnection):
         else:
             request = self.request_path + action
         return request
-
-    def _refresh_oauth2_token(self):
-        self.oauth2_token = self.oauth2_conn.refresh_token(self.oauth2_token)
-        self._write_token_to_file()
-
-    def _init_oauth2(self, **kwargs):
-        # Default scopes to read/write for compute, storage, and dns.  Can
-        # override this when calling get_driver() or setting in secrets.py
-        if not self.scopes:
-            self.scopes = [
-                'https://www.googleapis.com/auth/compute',
-                'https://www.googleapis.com/auth/devstorage.full_control',
-                'https://www.googleapis.com/auth/ndev.clouddns.readwrite',
-            ]
-        self.oauth2_token = self._get_token_from_file()
-
-        if self.auth_type == GoogleAuthType.GCE:
-            self.oauth2_conn = GoogleGCEServiceAcctAuthConnection(
-                self.user_id, self.scopes, **kwargs)
-        elif self.auth_type == GoogleAuthType.SA:
-            self.oauth2_conn = GoogleServiceAcctAuthConnection(
-                self.user_id, self.key, self.scopes, **kwargs)
-        elif self.auth_type == GoogleAuthType.IA:
-            self.oauth2_conn = GoogleInstalledAppAuthConnection(
-                self.user_id, self.key, self.scopes, **kwargs)
-        else:
-            raise GoogleAuthError('Invalid auth_type: %s' %
-                                  str(self.auth_type))
-
-        if self.oauth2_token is None:
-            self.oauth2_token = self.oauth2_conn.get_new_token()
-            self._write_token_to_file()
-
-    def _get_token_from_file(self):
-        """
-        Read credential file and return token information.
-        Mocked in libcloud.test.common.google.GoogleTestCase.
-
-        :return:  Token information dictionary, or None
-        :rtype:   ``dict`` or ``None``
-        """
-        token = None
-        filename = os.path.realpath(os.path.expanduser(self.credential_file))
-
-        try:
-            with open(filename, 'r') as f:
-                data = f.read()
-            token = json.loads(data)
-        except IOError:
-            pass
-        return token
-
-    def _write_token_to_file(self):
-        """
-        Write token to credential file.
-        Mocked in libcloud.test.common.google.GoogleTestCase.
-        """
-        filename = os.path.realpath(os.path.expanduser(self.credential_file))
-        data = json.dumps(self.oauth2_token)
-        with os.fdopen(os.open(filename, os.O_CREAT | os.O_WRONLY,
-                               int('600', 8)), 'w') as f:
-            f.write(data)

http://git-wip-us.apache.org/repos/asf/libcloud/blob/022d01ed/libcloud/compute/drivers/gce.py
----------------------------------------------------------------------
diff --git a/libcloud/compute/drivers/gce.py b/libcloud/compute/drivers/gce.py
index 4a5181e..12e296b 100644
--- a/libcloud/compute/drivers/gce.py
+++ b/libcloud/compute/drivers/gce.py
@@ -22,6 +22,7 @@ import time
 import sys
 
 from libcloud.common.base import LazyObject
+from libcloud.common.google import GoogleOAuth2Credential
 from libcloud.common.google import GoogleResponse
 from libcloud.common.google import GoogleBaseConnection
 from libcloud.common.google import GoogleBaseError
@@ -1070,7 +1071,7 @@ class GCENodeDriver(NodeDriver):
         self.project = project
         self.scopes = scopes
         self.credential_file = credential_file or \
-            GCEConnection.credential_file + '.' + self.project
+            GoogleOAuth2Credential.default_credential_file + '.' + self.project
 
         super(GCENodeDriver, self).__init__(user_id, key, **kwargs)
 

http://git-wip-us.apache.org/repos/asf/libcloud/blob/022d01ed/libcloud/storage/drivers/google_storage.py
----------------------------------------------------------------------
diff --git a/libcloud/storage/drivers/google_storage.py 
b/libcloud/storage/drivers/google_storage.py
index ca80c97..580a29c 100644
--- a/libcloud/storage/drivers/google_storage.py
+++ b/libcloud/storage/drivers/google_storage.py
@@ -17,8 +17,9 @@ import copy
 
 import email.utils
 
+from libcloud.common.base import ConnectionUserAndKey
 from libcloud.common.google import GoogleAuthType
-from libcloud.common.google import GoogleBaseConnection
+from libcloud.common.google import GoogleOAuth2Credential
 from libcloud.storage.drivers.s3 import BaseS3Connection
 from libcloud.storage.drivers.s3 import BaseS3StorageDriver
 from libcloud.storage.drivers.s3 import S3RawResponse
@@ -31,12 +32,12 @@ API_VERSION = '2006-03-01'
 NAMESPACE = 'http://doc.s3.amazonaws.com/%s' % (API_VERSION)
 
 
-class GoogleStorageConnection(GoogleBaseConnection):
+class GoogleStorageConnection(ConnectionUserAndKey):
     """
     Represents a single connection to the Google storage API endpoint.
 
     This can either authenticate via the Google OAuth2 methods or via
-    the S3 interoperability method.
+    the S3 HMAC interoperability method.
     """
 
     host = 'storage.googleapis.com'
@@ -46,27 +47,23 @@ class GoogleStorageConnection(GoogleBaseConnection):
 
     def __init__(self, user_id, key, secure, auth_type=None,
                  credential_file=None, **kwargs):
-        super(GoogleStorageConnection, self).__init__(
-            user_id, key, secure=secure, auth_type=auth_type,
-            credential_file=credential_file, **kwargs)
+        self.auth_type = auth_type or GoogleAuthType.guess_type(user_id)
+        if GoogleAuthType.is_oauth2(self.auth_type):
+            self.oauth2_credential = GoogleOAuth2Credential(
+                user_id, key, self.auth_type, credential_file, **kwargs)
+        else:
+            self.oauth2_credential = None
+        super(GoogleStorageConnection, self).__init__(user_id, key, secure,
+                                                      **kwargs)
 
     def add_default_headers(self, headers):
-        if self.auth_type == GoogleAuthType.GCS_S3:
-            date = email.utils.formatdate(usegmt=True)
-            headers['Date'] = date
-        else:
-            headers = super(GoogleStorageConnection,
-                            self).add_default_headers(headers)
+        date = email.utils.formatdate(usegmt=True)
+        headers['Date'] = date
         project = self.get_project()
         if project:
             headers[self.PROJECT_ID_HEADER] = project
         return headers
 
-    def encode_data(self, data):
-        if self.auth_type == GoogleAuthType.GCS_S3:
-            return data
-        return super(GoogleStorageConnection, self).encode_data(data)
-
     def get_project(self):
         return getattr(self.driver, 'project')
 
@@ -76,8 +73,8 @@ class GoogleStorageConnection(GoogleBaseConnection):
             headers['Authorization'] = '%s %s:%s' % (SIGNATURE_IDENTIFIER,
                                                      self.user_id, signature)
         else:
-            params, headers = super(GoogleStorageConnection,
-                                    self).pre_connect_hook(params, headers)
+            headers['Authorization'] = ('Bearer ' +
+                                        self.oauth2_credential.access_token)
         return params, headers
 
     def _get_s3_auth_signature(self, params, headers):
@@ -126,8 +123,8 @@ class GoogleStorageDriver(BaseS3StorageDriver):
 
         driver = GoogleStorageDriver(key=foo , secret=bar, ...)
 
-    Can also authenticate via Google Cloud Storage's S3 interoperability API.
-    S3 user keys are 20 alphanumeric characters, starting with GOOG.
+    Can also authenticate via Google Cloud Storage's S3 HMAC interoperability
+    API. S3 user keys are 20 alphanumeric characters, starting with GOOG.
 
     Example::
 

http://git-wip-us.apache.org/repos/asf/libcloud/blob/022d01ed/libcloud/test/common/test_google.py
----------------------------------------------------------------------
diff --git a/libcloud/test/common/test_google.py 
b/libcloud/test/common/test_google.py
index d463147..2266ddd 100644
--- a/libcloud/test/common/test_google.py
+++ b/libcloud/test/common/test_google.py
@@ -32,6 +32,7 @@ from libcloud.common.google import (GoogleAuthError,
                                     GoogleInstalledAppAuthConnection,
                                     GoogleServiceAcctAuthConnection,
                                     GoogleGCEServiceAcctAuthConnection,
+                                    GoogleOAuth2Credential,
                                     GoogleBaseConnection,
                                     _utcnow,
                                     _utc_timestamp)
@@ -47,8 +48,8 @@ except ImportError:
 
 
 SCRIPT_PATH = os.path.dirname(os.path.realpath(__file__))
-PEM_KEY = os.path.join(SCRIPT_PATH, "fixtures", "google", "pkey.pem")
-JSON_KEY = os.path.join(SCRIPT_PATH, "fixtures", "google", "pkey.json")
+PEM_KEY = os.path.join(SCRIPT_PATH, 'fixtures', 'google', 'pkey.pem')
+JSON_KEY = os.path.join(SCRIPT_PATH, 'fixtures', 'google', 'pkey.json')
 with open(JSON_KEY, 'r') as f:
     KEY_STR = json.loads(f.read())['private_key']
 
@@ -120,12 +121,12 @@ class GoogleTestCase(LibcloudTestCase):
         'libcloud.common.google.GoogleAuthType._is_gce', return_value=False)
 
     _read_token_file_patcher = mock.patch(
-        'libcloud.common.google.GoogleBaseConnection._get_token_from_file',
+        'libcloud.common.google.GoogleOAuth2Credential._get_token_from_file',
         return_value=STUB_TOKEN_FROM_FILE
     )
 
     _write_token_file_patcher = mock.patch(
-        'libcloud.common.google.GoogleBaseConnection._write_token_to_file')
+        'libcloud.common.google.GoogleOAuth2Credential._write_token_to_file')
 
     _ia_get_code_patcher = mock.patch(
         'libcloud.common.google.GoogleInstalledAppAuthConnection.get_code',
@@ -222,67 +223,103 @@ class 
GoogleInstalledAppAuthConnectionTest(GoogleTestCase):
 class GoogleAuthTypeTest(GoogleTestCase):
 
     def test_guess(self):
-        self.assertEqual(
-            GoogleAuthType.guess_type(GCE_PARAMS[0]),
-            GoogleAuthType.SA)
-        self.assertEqual(
-            GoogleAuthType.guess_type(GCE_PARAMS_IA[0]),
-            GoogleAuthType.IA)
+        self.assertEqual(GoogleAuthType.guess_type(GCE_PARAMS[0]),
+                         GoogleAuthType.SA)
+        self.assertEqual(GoogleAuthType.guess_type(GCE_PARAMS_IA[0]),
+                         GoogleAuthType.IA)
         with mock.patch.object(GoogleAuthType, '_is_gce', return_value=True):
-            self.assertEqual(
-                GoogleAuthType.guess_type(GCE_PARAMS_GCE[0]),
-                GoogleAuthType.GCE)
+            self.assertEqual(GoogleAuthType.guess_type(GCE_PARAMS_GCE[0]),
+                             GoogleAuthType.GCE)
         self.assertEqual(
             GoogleAuthType.guess_type(GCS_S3_PARAMS[0]),
             GoogleAuthType.GCS_S3)
 
 
-class GoogleBaseConnectionTest(GoogleTestCase):
-    """
-    Tests for GoogleBaseConnection
-    """
+class GoogleOAuth2CredentialTest(GoogleTestCase):
 
-    def setUp(self):
-        GoogleBaseAuthConnection.conn_classes = (GoogleAuthMockHttp,
-                                                 GoogleAuthMockHttp)
-        self.mock_scopes = ['https://www.googleapis.com/auth/foo']
-        kwargs = {'scopes': self.mock_scopes,
-                  'auth_type': GoogleAuthType.IA}
-        self.conn = GoogleBaseConnection(*GCE_PARAMS, **kwargs)
+    def test_init_oauth2(self):
+        kwargs = {'auth_type': GoogleAuthType.IA}
+        cred = GoogleOAuth2Credential(*GCE_PARAMS, **kwargs)
+
+        # If there is a viable token file, this gets used first
+        self.assertEqual(cred.token, STUB_TOKEN_FROM_FILE)
 
-    def test_auth_type(self):
-        self.assertRaises(GoogleAuthError, GoogleBaseConnection, *GCE_PARAMS,
+        # No token file, get a new token. Check that it gets written to file.
+        with mock.patch.object(GoogleOAuth2Credential, '_get_token_from_file',
+                               return_value=None):
+            cred = GoogleOAuth2Credential(*GCE_PARAMS, **kwargs)
+            expected = STUB_IA_TOKEN
+            expected['expire_time'] = cred.token['expire_time']
+            self.assertEqual(cred.token, expected)
+            cred._write_token_to_file.assert_called_once_with()
+
+    def test_refresh(self):
+        args = list(GCE_PARAMS) + [GoogleAuthType.GCE]
+        cred = GoogleOAuth2Credential(*args)
+        cred._refresh_token = mock.Mock()
+
+        # Test getting an unexpired access token.
+        tomorrow = datetime.datetime.now() + datetime.timedelta(days=1)
+        cred.token = {'access_token': 'Access Token!',
+                      'expire_time': _utc_timestamp(tomorrow)}
+        cred.access_token
+        self.assertFalse(cred._refresh_token.called)
+
+        # Test getting an expired access token.
+        yesterday = datetime.datetime.now() - datetime.timedelta(days=1)
+        cred.token = {'access_token': 'Access Token!',
+                      'expire_time': _utc_timestamp(yesterday)}
+        cred.access_token
+        self.assertTrue(cred._refresh_token.called)
+
+    def test_auth_connection(self):
+        # Test a bogus auth type
+        self.assertRaises(GoogleAuthError, GoogleOAuth2Credential, *GCE_PARAMS,
                           **{'auth_type': 'XX'})
+        # Try to create an OAuth2 credential when dealing with a GCS S3
+        # interoperability auth type.
+        self.assertRaises(GoogleAuthError, GoogleOAuth2Credential, *GCE_PARAMS,
+                          **{'auth_type': GoogleAuthType.GCS_S3})
 
-        kwargs = {'scopes': self.mock_scopes}
+        kwargs = {}
 
         if SHA256:
             kwargs['auth_type'] = GoogleAuthType.SA
-            conn1 = GoogleBaseConnection(*GCE_PARAMS_PEM_KEY, **kwargs)
-            self.assertTrue(isinstance(conn1.oauth2_conn,
+            cred1 = GoogleOAuth2Credential(*GCE_PARAMS_PEM_KEY, **kwargs)
+            self.assertTrue(isinstance(cred1.oauth2_conn,
                                        GoogleServiceAcctAuthConnection))
 
-            conn1 = GoogleBaseConnection(*GCE_PARAMS_JSON_KEY, **kwargs)
-            self.assertTrue(isinstance(conn1.oauth2_conn,
+            cred1 = GoogleOAuth2Credential(*GCE_PARAMS_JSON_KEY, **kwargs)
+            self.assertTrue(isinstance(cred1.oauth2_conn,
                                        GoogleServiceAcctAuthConnection))
 
-            conn1 = GoogleBaseConnection(*GCE_PARAMS_KEY, **kwargs)
-            self.assertTrue(isinstance(conn1.oauth2_conn,
+            cred1 = GoogleOAuth2Credential(*GCE_PARAMS_KEY, **kwargs)
+            self.assertTrue(isinstance(cred1.oauth2_conn,
                                        GoogleServiceAcctAuthConnection))
 
         kwargs['auth_type'] = GoogleAuthType.IA
-        conn2 = GoogleBaseConnection(*GCE_PARAMS_IA, **kwargs)
-        self.assertTrue(isinstance(conn2.oauth2_conn,
+        cred2 = GoogleOAuth2Credential(*GCE_PARAMS_IA, **kwargs)
+        self.assertTrue(isinstance(cred2.oauth2_conn,
                                    GoogleInstalledAppAuthConnection))
 
         kwargs['auth_type'] = GoogleAuthType.GCE
-        conn3 = GoogleBaseConnection(*GCE_PARAMS_GCE, **kwargs)
-        self.assertTrue(isinstance(conn3.oauth2_conn,
+        cred3 = GoogleOAuth2Credential(*GCE_PARAMS_GCE, **kwargs)
+        self.assertTrue(isinstance(cred3.oauth2_conn,
                                    GoogleGCEServiceAcctAuthConnection))
 
-        kwargs['auth_type'] = GoogleAuthType.GCS_S3
-        conn4 = GoogleBaseConnection(*GCS_S3_PARAMS, **kwargs)
-        self.assertIsNone(conn4.oauth2_conn)
+
+class GoogleBaseConnectionTest(GoogleTestCase):
+    """
+    Tests for GoogleBaseConnection
+    """
+
+    def setUp(self):
+        GoogleBaseAuthConnection.conn_classes = (GoogleAuthMockHttp,
+                                                 GoogleAuthMockHttp)
+        self.mock_scopes = ['https://www.googleapis.com/auth/foo']
+        kwargs = {'scopes': self.mock_scopes,
+                  'auth_type': GoogleAuthType.IA}
+        self.conn = GoogleBaseConnection(*GCE_PARAMS, **kwargs)
 
     def test_add_default_headers(self):
         old_headers = {}
@@ -347,24 +384,6 @@ class GoogleBaseConnectionTest(GoogleTestCase):
         self.assertEqual(request1, expected_request)
         self.assertEqual(request2, expected_request)
 
-    def test_init_oauth2(self):
-        mock_scopes = ['https://www.googleapis.com/auth/foo']
-        kwargs = {'scopes': mock_scopes,
-                  'auth_type': GoogleAuthType.IA}
-        conn = GoogleBaseConnection(*GCE_PARAMS, **kwargs)
-
-        # If there is a viable token file, this gets used first
-        self.assertEqual(conn.oauth2_token, STUB_TOKEN_FROM_FILE)
-
-        # No token file, get a new token. Check that it gets written to file.
-        with mock.patch.object(GoogleBaseConnection,
-                               '_get_token_from_file', return_value=None):
-            conn = GoogleBaseConnection(*GCE_PARAMS, **kwargs)
-            expected = STUB_IA_TOKEN
-            expected['expire_time'] = conn.oauth2_token['expire_time']
-            self.assertEqual(conn.oauth2_token, expected)
-            conn._write_token_to_file.assert_called_once_with()
-
 
 class GoogleAuthMockHttp(MockHttp):
     """

http://git-wip-us.apache.org/repos/asf/libcloud/blob/022d01ed/libcloud/test/storage/test_google_storage.py
----------------------------------------------------------------------
diff --git a/libcloud/test/storage/test_google_storage.py 
b/libcloud/test/storage/test_google_storage.py
index 001a9f3..2668c50 100644
--- a/libcloud/test/storage/test_google_storage.py
+++ b/libcloud/test/storage/test_google_storage.py
@@ -60,41 +60,12 @@ class GoogleStorageMockHttp(S3MockHttp):
 class GoogleStorageConnectionTest(GoogleTestCase):
 
     @mock.patch('email.utils.formatdate')
-    @mock.patch('libcloud.common.google.'
-                'GoogleBaseConnection.add_default_headers')
-    def test_add_default_headers(self, mock_base_method, mock_formatdate):
+    def test_add_default_headers(self, mock_formatdate):
         mock_formatdate.return_value = TODAY
         starting_headers = {'starting': 'headers'}
-        changed_headers = {'changed': 'headers'}
         project = 'foo-project'
 
-        # Should use base add_default_headers
-        mock_base_method.return_value = dict(changed_headers)
-        conn = CONN_CLS('foo_user', 'bar_key', secure=True,
-                        auth_type=GoogleAuthType.GCE)
-        conn.get_project = lambda: None
-        self.assertEqual(
-            conn.add_default_headers(dict(starting_headers)),
-            dict(changed_headers)
-        )
-        mock_base_method.assert_called_once_with(dict(starting_headers))
-        mock_base_method.reset_mock()
-
-        # Base add_default_headers with project
-        mock_base_method.return_value = dict(changed_headers)
-        conn = CONN_CLS('foo_user', 'bar_key', secure=True,
-                        auth_type=GoogleAuthType.GCE)
-        conn.get_project = lambda: project
-        headers = dict(changed_headers)
-        headers[CONN_CLS.PROJECT_ID_HEADER] = project
-        self.assertEqual(
-            conn.add_default_headers(dict(starting_headers)),
-            headers
-        )
-        mock_base_method.assert_called_once_with(dict(starting_headers))
-        mock_base_method.reset_mock()
-
-        # Should use S3 add_default_headers
+        # Modify headers when there is no project.
         conn = CONN_CLS('foo_user', 'bar_key', secure=True,
                         auth_type=GoogleAuthType.GCS_S3)
         conn.get_project = lambda: None
@@ -102,9 +73,8 @@ class GoogleStorageConnectionTest(GoogleTestCase):
         headers['Date'] = TODAY
         self.assertEqual(conn.add_default_headers(dict(starting_headers)),
                          headers)
-        mock_base_method.assert_not_called()
 
-        # S3 add_default_headers with project
+        # Modify headers when there is a project.
         conn = CONN_CLS('foo_user', 'bar_key', secure=True,
                         auth_type=GoogleAuthType.GCS_S3)
         conn.get_project = lambda: project
@@ -113,44 +83,25 @@ class GoogleStorageConnectionTest(GoogleTestCase):
         headers[CONN_CLS.PROJECT_ID_HEADER] = project
         self.assertEqual(conn.add_default_headers(dict(starting_headers)),
                          headers)
-        mock_base_method.assert_not_called()
-
-    @mock.patch('libcloud.common.google.GoogleBaseConnection.encode_data')
-    def test_encode_data(self, mock_base_method):
-        old_data = 'old data!'
-        new_data = 'new data!'
-
-        # Should use Base encode_data
-        mock_base_method.return_value = new_data
-        conn = CONN_CLS('foo_user', 'bar_key', secure=True,
-                        auth_type=GoogleAuthType.GCE)
-        self.assertEqual(conn.encode_data(old_data), new_data)
-        mock_base_method.assert_called_once_with(old_data)
-        mock_base_method.reset_mock()
-
-        # Should use S3 encode_data (which does nothing)
-        conn = CONN_CLS('foo_user', 'bar_key', secure=True,
-                        auth_type=GoogleAuthType.GCS_S3)
-        self.assertEqual(conn.encode_data(old_data), old_data)
-        mock_base_method.assert_not_called()
 
     @mock.patch('libcloud.storage.drivers.s3.'
                 'BaseS3Connection.get_auth_signature')
     def test_get_s3_auth_signature(self, mock_s3_auth_sig_method):
         # Check that the S3 HMAC signature method is used.
-        # Check that headers are copied and modified before calling the method.
+        # Check that headers are copied and modified properly before calling
+        # the signature method.
         mock_s3_auth_sig_method.return_value = 'mock signature!'
         starting_params = {}
         starting_headers = {
             'Date': TODAY,
-            'x-goog-foo': 'MAINTAIN UPPERCASE!',
-            'x-Goog-bar': 'Header should be lowered',
+            'x-goog-foo': 'X-GOOG: MAINTAIN UPPERCASE!',
+            'x-Goog-bar': 'Header key should be lowered',
             'Other': 'LOWER THIS!'
         }
         modified_headers = {
             'date': TODAY,
-            'x-goog-foo': 'MAINTAIN UPPERCASE!',
-            'x-goog-bar': 'Header should be lowered',
+            'x-goog-foo': 'X-GOOG: MAINTAIN UPPERCASE!',
+            'x-goog-bar': 'Header key should be lowered',
             'other': 'lower this!'
         }
 
@@ -171,38 +122,30 @@ class GoogleStorageConnectionTest(GoogleTestCase):
             vendor_prefix='x-goog'
         )
 
-    @mock.patch('libcloud.common.google.GoogleBaseConnection.pre_connect_hook')
-    def test_pre_connect_hook_oauth2(self, mock_base_hook):
-        # Should use BaseGoogleConnection pre_connect_hook
-        # Check that the base hook is called.
+    @mock.patch('libcloud.common.google.GoogleOAuth2Credential')
+    def test_pre_connect_hook_oauth2(self, mock_oauth2_credential_init):
+        # Check that we get the Authorization header from the OAuth2 token,
+        # not from the HMAC signature method.
+        # Check that the headers and pa
+        mock_oauth2_credential_init.return_value = mock.Mock()
         starting_params = {'starting': 'params'}
-        changed_params = {'changed': 'params'}
         starting_headers = {'starting': 'headers'}
-        changed_headers = {'changed': 'headers'}
 
-        mock_base_hook.return_value = (dict(changed_params),
-                                       dict(changed_headers))
         conn = CONN_CLS('foo_user', 'bar_key', secure=True,
                         auth_type=GoogleAuthType.GCE)
-        result = conn.pre_connect_hook(
-            dict(starting_params),
-            dict(starting_headers)
-        )
-        self.assertEqual(
-            result,
-            (dict(changed_params), dict(changed_headers))
-        )
-        mock_base_hook.assert_called_once_with(
-            dict(starting_params),
-            dict(starting_headers)
-        )
-        mock_base_hook.reset_mock()
+        conn._get_s3_auth_signature = mock.Mock()
+        conn.oauth2_credential = mock.Mock()
+        conn.oauth2_credential.access_token = 'Access_Token!'
+        expected_headers = dict(starting_headers)
+        expected_headers['Authorization'] = 'Bearer Access_Token!'
+        result = conn.pre_connect_hook(dict(starting_params),
+                                       dict(starting_headers))
+        self.assertEqual(result, (starting_params, expected_headers))
 
-    @mock.patch('libcloud.common.google.GoogleBaseConnection.pre_connect_hook')
-    def test_pre_connect_hook_hmac(self, mock_base_hook):
+    def test_pre_connect_hook_hmac(self):
         # Check that we call for a HMAC signature, passing params and headers
         # Check that we properly apply the HMAC signature.
-        # Check that we don't use the BaseGoogleConnection pre_connect_hook.
+        # Check that we don't use OAuth2 credentials.
         starting_params = {'starting': 'params'}
         starting_headers = {'starting': 'headers'}
 
@@ -222,23 +165,12 @@ class GoogleStorageConnectionTest(GoogleTestCase):
             '%s %s:%s' % (google_storage.SIGNATURE_IDENTIFIER, 'foo_user',
                           'fake signature!')
         )
-        result = conn.pre_connect_hook(
-            dict(starting_params),
-            dict(starting_headers)
-        )
-        self.assertEqual(
-            result,
-            (dict(starting_params), expected_headers)
-        )
-        mock_base_hook.assert_not_called()
-        self.assertEqual(
-            fake_hmac_method.params_passed,
-            starting_params
-        )
-        self.assertEqual(
-            fake_hmac_method.headers_passed,
-            starting_headers
-        )
+        result = conn.pre_connect_hook(dict(starting_params),
+                                       dict(starting_headers))
+        self.assertEqual(result, (dict(starting_params), expected_headers))
+        self.assertEqual(fake_hmac_method.params_passed, starting_params)
+        self.assertEqual(fake_hmac_method.headers_passed, starting_headers)
+        self.assertIsNone(conn.oauth2_credential)
 
 
 class GoogleStorageTests(S3Tests, GoogleTestCase):

Reply via email to