Skip to content

Commit df10cf7

Browse files
committed
Merge pull request googleapis#208 from pcostell/devel
Allow GCE credentials even if GAE SDK is detected.
2 parents 4fdd7f9 + 245205a commit df10cf7

File tree

2 files changed

+114
-48
lines changed

2 files changed

+114
-48
lines changed

oauth2client/client.py

Lines changed: 38 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -975,38 +975,46 @@ def _detect_gce_environment(urlopen=None):
975975
return False
976976

977977

978-
def _get_environment(urlopen=None):
979-
"""Detect the environment the code is being run on.
980-
981-
Args:
982-
urlopen: Optional argument. Function used to open a connection to a URL.
978+
def _in_gae_environment():
979+
"""Detects if the code is running in the App Engine environment.
983980
984981
Returns:
985-
The value of SETTINGS.env_name after being set. If already
986-
set, simply returns the value.
982+
True if running in the GAE environment, False otherwise.
987983
"""
988984
if SETTINGS.env_name is not None:
989-
return SETTINGS.env_name
990-
991-
# None is an unset value, not the default.
992-
SETTINGS.env_name = DEFAULT_ENV_NAME
985+
return SETTINGS.env_name in ('GAE_PRODUCTION', 'GAE_LOCAL')
993986

994987
try:
995988
import google.appengine
996-
has_gae_sdk = True
997-
except ImportError:
998-
has_gae_sdk = False
999-
1000-
if has_gae_sdk:
1001989
server_software = os.environ.get('SERVER_SOFTWARE', '')
1002990
if server_software.startswith('Google App Engine/'):
1003991
SETTINGS.env_name = 'GAE_PRODUCTION'
992+
return True
1004993
elif server_software.startswith('Development/'):
1005994
SETTINGS.env_name = 'GAE_LOCAL'
1006-
elif NO_GCE_CHECK != 'True' and _detect_gce_environment(urlopen=urlopen):
1007-
SETTINGS.env_name = 'GCE_PRODUCTION'
995+
return True
996+
except ImportError:
997+
pass
998+
999+
return False
10081000

1009-
return SETTINGS.env_name
1001+
1002+
def _in_gce_environment(urlopen=None):
1003+
"""Detect if the code is running in the Compute Engine environment.
1004+
1005+
Args:
1006+
urlopen: Optional argument. Function used to open a connection to a URL.
1007+
1008+
Returns:
1009+
True if running in the GCE environment, False otherwise.
1010+
"""
1011+
if SETTINGS.env_name is not None:
1012+
return SETTINGS.env_name == 'GCE_PRODUCTION'
1013+
1014+
if NO_GCE_CHECK != 'True' and _detect_gce_environment(urlopen=urlopen):
1015+
SETTINGS.env_name = 'GCE_PRODUCTION'
1016+
return True
1017+
return False
10101018

10111019

10121020
class GoogleCredentials(OAuth2Credentials):
@@ -1085,56 +1093,45 @@ def serialization_data(self):
10851093
}
10861094

10871095
@staticmethod
1088-
def _implicit_credentials_from_gae(env_name=None):
1096+
def _implicit_credentials_from_gae():
10891097
"""Attempts to get implicit credentials in Google App Engine env.
10901098
10911099
If the current environment is not detected as App Engine, returns None,
10921100
indicating no Google App Engine credentials can be detected from the
10931101
current environment.
10941102
1095-
Args:
1096-
env_name: String, indicating current environment.
1097-
10981103
Returns:
10991104
None, if not in GAE, else an appengine.AppAssertionCredentials object.
11001105
"""
1101-
env_name = env_name or _get_environment()
1102-
if env_name not in ('GAE_PRODUCTION', 'GAE_LOCAL'):
1106+
if not _in_gae_environment():
11031107
return None
11041108

11051109
return _get_application_default_credential_GAE()
11061110

11071111
@staticmethod
1108-
def _implicit_credentials_from_gce(env_name=None):
1112+
def _implicit_credentials_from_gce():
11091113
"""Attempts to get implicit credentials in Google Compute Engine env.
11101114
11111115
If the current environment is not detected as Compute Engine, returns None,
11121116
indicating no Google Compute Engine credentials can be detected from the
11131117
current environment.
11141118
1115-
Args:
1116-
env_name: String, indicating current environment.
1117-
11181119
Returns:
11191120
None, if not in GCE, else a gce.AppAssertionCredentials object.
11201121
"""
1121-
env_name = env_name or _get_environment()
1122-
if env_name != 'GCE_PRODUCTION':
1122+
if not _in_gce_environment():
11231123
return None
11241124

11251125
return _get_application_default_credential_GCE()
11261126

11271127
@staticmethod
1128-
def _implicit_credentials_from_files(env_name=None):
1128+
def _implicit_credentials_from_files():
11291129
"""Attempts to get implicit credentials from local credential files.
11301130
11311131
First checks if the environment variable GOOGLE_APPLICATION_CREDENTIALS
11321132
is set with a filename and then falls back to a configuration file (the
11331133
"well known" file) associated with the 'gcloud' command line tool.
11341134
1135-
Args:
1136-
env_name: Unused argument.
1137-
11381135
Returns:
11391136
Credentials object associated with the GOOGLE_APPLICATION_CREDENTIALS
11401137
file or the "well known" file if either exist. If neither file is
@@ -1156,6 +1153,10 @@ def _implicit_credentials_from_files(env_name=None):
11561153
if not credentials_filename:
11571154
return
11581155

1156+
# If we can read the credentials from a file, we don't need to know what
1157+
# environment we are in.
1158+
SETTINGS.env_name = DEFAULT_ENV_NAME
1159+
11591160
try:
11601161
return _get_application_default_credential_from_file(credentials_filename)
11611162
except (ApplicationDefaultCredentialsError, ValueError) as error:
@@ -1176,18 +1177,16 @@ def _get_implicit_credentials(cls):
11761177
ApplicationDefaultCredentialsError: raised when the credentials fail
11771178
to be retrieved.
11781179
"""
1179-
env_name = _get_environment()
11801180

1181-
# Environ checks (in order). Assumes each checker takes `env_name`
1182-
# as a kwarg.
1181+
# Environ checks (in order).
11831182
environ_checkers = [
11841183
cls._implicit_credentials_from_gae,
11851184
cls._implicit_credentials_from_files,
11861185
cls._implicit_credentials_from_gce,
11871186
]
11881187

11891188
for checker in environ_checkers:
1190-
credentials = checker(env_name=env_name)
1189+
credentials = checker()
11911190
if credentials is not None:
11921191
return credentials
11931192

tests/test_oauth2client.py

Lines changed: 76 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import datetime
2828
import json
2929
import os
30+
import socket
3031
import sys
3132
import time
3233
import unittest
@@ -65,9 +66,10 @@
6566
from oauth2client.client import VerifyJwtTokenError
6667
from oauth2client.client import _extract_id_token
6768
from oauth2client.client import _get_application_default_credential_from_file
68-
from oauth2client.client import _get_environment
6969
from oauth2client.client import _get_environment_variable_file
7070
from oauth2client.client import _get_well_known_file
71+
from oauth2client.client import _in_gae_environment
72+
from oauth2client.client import _in_gce_environment
7173
from oauth2client.client import _raise_exception_for_missing_fields
7274
from oauth2client.client import _raise_exception_for_reading_json
7375
from oauth2client.client import _update_query_params
@@ -218,32 +220,97 @@ def test_create_scoped(self):
218220
self.assertEqual(credentials,
219221
credentials.create_scoped(['dummy_scope']))
220222

221-
def test_get_environment_gae_production(self):
223+
def test_environment_check_gae_production(self):
222224
with mock_module_import('google.appengine'):
223225
os.environ['SERVER_SOFTWARE'] = 'Google App Engine/XYZ'
224-
self.assertEqual('GAE_PRODUCTION', _get_environment())
226+
self.assertTrue(_in_gae_environment())
227+
self.assertFalse(_in_gce_environment())
225228

226-
def test_get_environment_gae_local(self):
229+
def test_environment_check_gae_local(self):
227230
with mock_module_import('google.appengine'):
228231
os.environ['SERVER_SOFTWARE'] = 'Development/XYZ'
229-
self.assertEqual('GAE_LOCAL', _get_environment())
232+
self.assertTrue(_in_gae_environment())
233+
self.assertFalse(_in_gce_environment())
230234

231-
def test_get_environment_gce_production(self):
235+
def test_environment_check_fastpath(self):
236+
os.environ['SERVER_SOFTWARE'] = 'Development/XYZ'
237+
with mock_module_import('google.appengine'):
238+
with mock.patch.object(urllib.request, 'urlopen',
239+
return_value=MockResponse({}),
240+
autospec=True) as urlopen:
241+
self.assertTrue(_in_gae_environment())
242+
self.assertFalse(_in_gce_environment())
243+
# We already know are in GAE, so we shouldn't actually do the urlopen.
244+
self.assertFalse(urlopen.called)
245+
246+
def test_environment_caching(self):
247+
os.environ['SERVER_SOFTWARE'] = 'Development/XYZ'
248+
with mock_module_import('google.appengine'):
249+
self.assertTrue(_in_gae_environment())
250+
os.environ['SERVER_SOFTWARE'] = ''
251+
# Even though we no longer pass the environment check, it is cached.
252+
self.assertTrue(_in_gae_environment())
253+
254+
def test_environment_check_gae_module_on_gce(self):
255+
with mock_module_import('google.appengine'):
256+
os.environ['SERVER_SOFTWARE'] = ''
257+
response = MockResponse({'Metadata-Flavor': 'Google'})
258+
with mock.patch.object(urllib.request, 'urlopen',
259+
return_value=response,
260+
autospec=True) as urlopen:
261+
self.assertFalse(_in_gae_environment())
262+
self.assertTrue(_in_gce_environment())
263+
urlopen.assert_called_once_with(
264+
'http://169.254.169.254/', timeout=1)
265+
266+
def test_environment_check_gae_module_unknown(self):
267+
with mock_module_import('google.appengine'):
268+
os.environ['SERVER_SOFTWARE'] = ''
269+
with mock.patch.object(urllib.request, 'urlopen',
270+
return_value=MockResponse({}),
271+
autospec=True) as urlopen:
272+
self.assertFalse(_in_gae_environment())
273+
self.assertFalse(_in_gce_environment())
274+
urlopen.assert_called_once_with(
275+
'http://169.254.169.254/', timeout=1)
276+
277+
def test_environment_check_gce_production(self):
232278
os.environ['SERVER_SOFTWARE'] = ''
233279
response = MockResponse({'Metadata-Flavor': 'Google'})
234280
with mock.patch.object(urllib.request, 'urlopen',
235281
return_value=response,
236282
autospec=True) as urlopen:
237-
self.assertEqual('GCE_PRODUCTION', _get_environment())
283+
self.assertFalse(_in_gae_environment())
284+
self.assertTrue(_in_gce_environment())
285+
urlopen.assert_called_once_with(
286+
'http://169.254.169.254/', timeout=1)
287+
288+
def test_environment_check_gce_timeout(self):
289+
os.environ['SERVER_SOFTWARE'] = ''
290+
response = MockResponse({'Metadata-Flavor': 'Google'})
291+
with mock.patch.object(urllib.request, 'urlopen',
292+
return_value=response,
293+
autospec=True) as urlopen:
294+
urlopen.side_effect = socket.timeout()
295+
self.assertFalse(_in_gce_environment())
296+
urlopen.assert_called_once_with(
297+
'http://169.254.169.254/', timeout=1)
298+
299+
with mock.patch.object(urllib.request, 'urlopen',
300+
return_value=response,
301+
autospec=True) as urlopen:
302+
urlopen.side_effect = urllib.error.URLError(socket.timeout())
303+
self.assertFalse(_in_gce_environment())
238304
urlopen.assert_called_once_with(
239305
'http://169.254.169.254/', timeout=1)
240306

241-
def test_get_environment_unknown(self):
307+
def test_environment_check_unknown(self):
242308
os.environ['SERVER_SOFTWARE'] = ''
243309
with mock.patch.object(urllib.request, 'urlopen',
244310
return_value=MockResponse({}),
245311
autospec=True) as urlopen:
246-
self.assertEqual(DEFAULT_ENV_NAME, _get_environment())
312+
self.assertFalse(_in_gce_environment())
313+
self.assertFalse(_in_gae_environment())
247314
urlopen.assert_called_once_with(
248315
'http://169.254.169.254/', timeout=1)
249316

0 commit comments

Comments
 (0)