Skip to content

Commit 4727cb7

Browse files
committedDec 28, 2019
Improve Google Drive auth failure error reporting
1 parent dd68e9a commit 4727cb7

File tree

2 files changed

+80
-14
lines changed

2 files changed

+80
-14
lines changed
 

‎dvc/remote/gdrive.py

Lines changed: 36 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,18 @@
2121

2222

2323
class GDriveRetriableError(DvcException):
24-
def __init__(self, msg):
25-
super(GDriveRetriableError, self).__init__(msg)
24+
def __init__(self, msg, cause=None):
25+
super(GDriveRetriableError, self).__init__(msg, cause=cause)
26+
27+
28+
class GDriveAccessTokenRefreshError(DvcException):
29+
def __init__(self, msg, cause=None):
30+
super(GDriveAccessTokenRefreshError, self).__init__(msg, cause=cause)
31+
32+
33+
class GDriveMissedCredentialKeyError(DvcException):
34+
def __init__(self, msg, cause=None):
35+
super(GDriveMissedCredentialKeyError, self).__init__(msg, cause=cause)
2636

2737

2838
@decorator
@@ -38,7 +48,7 @@ def _wrap_pydrive_retriable(call):
3848
"HttpError {}".format(code) in str(exception)
3949
for code in retry_codes
4050
):
41-
raise GDriveRetriableError(msg="Google API request failed")
51+
raise GDriveRetriableError("Google API request failed")
4252
raise
4353
return result
4454

@@ -78,7 +88,7 @@ def init_drive(self):
7888
"https://man.dvc.org/remote/add."
7989
)
8090
self.gdrive_user_credentials_path = (
81-
tmp_fname(".dvc/tmp/")
91+
tmp_fname(os.path.join(self.repo.tmp_dir, ""))
8292
if os.getenv(RemoteGDrive.GDRIVE_USER_CREDENTIALS_DATA)
8393
else self.config.get(
8494
Config.SECTION_GDRIVE_USER_CREDENTIALS_FILE,
@@ -161,6 +171,8 @@ def cached_ids(self):
161171
@property
162172
@wrap_with(threading.RLock())
163173
def drive(self):
174+
from pydrive.auth import RefreshError
175+
164176
if not hasattr(self, "_gdrive"):
165177
from pydrive.auth import GoogleAuth
166178
from pydrive.drive import GoogleDrive
@@ -195,10 +207,27 @@ def drive(self):
195207

196208
# Pass non existent settings path to force DEFAULT_SETTINGS loading
197209
gauth = GoogleAuth(settings_file="")
198-
gauth.CommandLineAuth()
199210

200-
if os.getenv(RemoteGDrive.GDRIVE_USER_CREDENTIALS_DATA):
201-
os.remove(self.gdrive_user_credentials_path)
211+
try:
212+
gauth.CommandLineAuth()
213+
except RefreshError as e:
214+
raise GDriveAccessTokenRefreshError(
215+
"Google Drive's access token refreshment is failed", e
216+
)
217+
except KeyError as e:
218+
raise GDriveMissedCredentialKeyError(
219+
"Google Drive's user credentials file '{}' "
220+
"misses value for key '{}'".format(
221+
self.gdrive_user_credentials_path, str(e)
222+
),
223+
e,
224+
)
225+
# Handle pydrive.auth.AuthenticationError and others auth failures
226+
except Exception as e:
227+
raise DvcException("Google Drive authentication failed", e)
228+
finally:
229+
if os.getenv(RemoteGDrive.GDRIVE_USER_CREDENTIALS_DATA):
230+
os.remove(self.gdrive_user_credentials_path)
202231

203232
self._gdrive = GoogleDrive(gauth)
204233

‎tests/unit/remote/test_gdrive.py

Lines changed: 44 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,46 @@
1-
import mock
2-
from dvc.remote.gdrive import RemoteGDrive
1+
from unittest import TestCase
32

3+
import pytest
4+
import os
45

5-
@mock.patch("dvc.remote.gdrive.RemoteGDrive.init_drive")
6-
def test_init(repo):
7-
url = "gdrive://root/data"
8-
gdrive = RemoteGDrive(repo, {"url": url})
9-
assert str(gdrive.path_info) == url
6+
from dvc.remote.gdrive import (
7+
RemoteGDrive,
8+
GDriveAccessTokenRefreshError,
9+
GDriveMissedCredentialKeyError,
10+
)
11+
12+
USER_CREDS_TOKEN_REFRESH_ERROR = '{"access_token": "", "client_id": "", "client_secret": "", "refresh_token": "", "token_expiry": "", "token_uri": "https://oauth2.googleapis.com/token", "user_agent": null, "revoke_uri": "https://oauth2.googleapis.com/revoke", "id_token": null, "id_token_jwt": null, "token_response": {"access_token": "", "expires_in": 3600, "scope": "https://www.googleapis.com/auth/drive.appdata https://www.googleapis.com/auth/drive", "token_type": "Bearer"}, "scopes": ["https://www.googleapis.com/auth/drive", "https://www.googleapis.com/auth/drive.appdata"], "token_info_uri": "https://oauth2.googleapis.com/tokeninfo", "invalid": true, "_class": "OAuth2Credentials", "_module": "oauth2client.client"}' # noqa: E501
13+
14+
USER_CREDS_MISSED_KEY_ERROR = "{}"
15+
16+
17+
class Repo(object):
18+
tmp_dir = ""
19+
20+
21+
class TestRemoteGDrive(TestCase):
22+
CONFIG = {
23+
"url": "gdrive://root/data",
24+
"gdrive_client_id": "client",
25+
"gdrive_client_secret": "secret",
26+
}
27+
28+
def test_init(self):
29+
remote = RemoteGDrive(Repo(), self.CONFIG)
30+
assert str(remote.path_info) == self.CONFIG["url"]
31+
32+
def test_drive(self):
33+
remote = RemoteGDrive(Repo(), self.CONFIG)
34+
os.environ[
35+
RemoteGDrive.GDRIVE_USER_CREDENTIALS_DATA
36+
] = USER_CREDS_TOKEN_REFRESH_ERROR
37+
with pytest.raises(GDriveAccessTokenRefreshError):
38+
remote.drive
39+
40+
os.environ[RemoteGDrive.GDRIVE_USER_CREDENTIALS_DATA] = ""
41+
remote = RemoteGDrive(Repo(), self.CONFIG)
42+
os.environ[
43+
RemoteGDrive.GDRIVE_USER_CREDENTIALS_DATA
44+
] = USER_CREDS_MISSED_KEY_ERROR
45+
with pytest.raises(GDriveMissedCredentialKeyError):
46+
remote.drive

0 commit comments

Comments
 (0)