Add a timeout for GCE detection.
This is a temporary workaround for #93, though may end up being the final fix. I did some test cleanup while I was here, switching to `mock`.
This commit is contained in:
@@ -26,6 +26,7 @@ import datetime
|
|||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
|
import socket
|
||||||
import sys
|
import sys
|
||||||
import time
|
import time
|
||||||
import six
|
import six
|
||||||
@@ -930,12 +931,22 @@ def _detect_gce_environment(urlopen=None):
|
|||||||
Compute Engine.
|
Compute Engine.
|
||||||
"""
|
"""
|
||||||
urlopen = urlopen or urllib.request.urlopen
|
urlopen = urlopen or urllib.request.urlopen
|
||||||
|
# Note: the explicit `timeout` below is a workaround. The underlying
|
||||||
|
# issue is that resolving an unknown host on some networks will take
|
||||||
|
# 20-30 seconds; making this timeout short fixes the issue, but
|
||||||
|
# could lead to false negatives in the event that we are on GCE, but
|
||||||
|
# the metadata resolution was particularly slow. The latter case is
|
||||||
|
# "unlikely".
|
||||||
try:
|
try:
|
||||||
response = urlopen('http://metadata.google.internal')
|
response = urlopen('http://metadata.google.internal/', timeout=1)
|
||||||
return any('Metadata-Flavor: Google' in header
|
return any('Metadata-Flavor: Google' in header
|
||||||
for header in response.info().headers)
|
for header in response.info().headers)
|
||||||
except urllib.error.URLError:
|
except socket.timeout:
|
||||||
|
logger.info('Timeout attempting to reach GCE metadata service.')
|
||||||
|
return False
|
||||||
|
except urllib.error.URLError as e:
|
||||||
|
if isinstance(getattr(e, 'reason', None), socket.timeout):
|
||||||
|
logger.info('Timeout attempting to reach GCE metadata service.')
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -25,13 +25,11 @@ __author__ = 'jcgregorio@google.com (Joe Gregorio)'
|
|||||||
import base64
|
import base64
|
||||||
import datetime
|
import datetime
|
||||||
import json
|
import json
|
||||||
try:
|
|
||||||
from mox3 import mox
|
|
||||||
except ImportError:
|
|
||||||
import mox
|
|
||||||
import os
|
import os
|
||||||
import time
|
import time
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
|
import mock
|
||||||
import six
|
import six
|
||||||
from six.moves import urllib
|
from six.moves import urllib
|
||||||
|
|
||||||
@@ -208,37 +206,19 @@ class GoogleCredentialsTests(unittest.TestCase):
|
|||||||
|
|
||||||
def test_get_environment_gce_production(self):
|
def test_get_environment_gce_production(self):
|
||||||
os.environ['SERVER_SOFTWARE'] = ''
|
os.environ['SERVER_SOFTWARE'] = ''
|
||||||
mockResponse = MockResponse(['Metadata-Flavor: Google\r\n'])
|
with mock.patch.object(urllib.request, 'urlopen') as urlopen:
|
||||||
|
urlopen.return_value = MockResponse(['Metadata-Flavor: Google\r\n'])
|
||||||
m = mox.Mox()
|
self.assertEqual('GCE_PRODUCTION', _get_environment())
|
||||||
|
urlopen.assert_called_once_with(
|
||||||
urllib2_urlopen = m.CreateMock(object)
|
'http://metadata.google.internal/', timeout=1)
|
||||||
urllib2_urlopen.__call__(('http://metadata.google.internal'
|
|
||||||
)).AndReturn(mockResponse)
|
|
||||||
|
|
||||||
m.ReplayAll()
|
|
||||||
|
|
||||||
self.assertEqual('GCE_PRODUCTION', _get_environment(urllib2_urlopen))
|
|
||||||
|
|
||||||
m.UnsetStubs()
|
|
||||||
m.VerifyAll()
|
|
||||||
|
|
||||||
def test_get_environment_unknown(self):
|
def test_get_environment_unknown(self):
|
||||||
os.environ['SERVER_SOFTWARE'] = ''
|
os.environ['SERVER_SOFTWARE'] = ''
|
||||||
mockResponse = MockResponse([])
|
with mock.patch.object(urllib.request, 'urlopen') as urlopen:
|
||||||
|
urlopen.return_value = MockResponse([])
|
||||||
m = mox.Mox()
|
self.assertEqual(DEFAULT_ENV_NAME, _get_environment())
|
||||||
|
urlopen.assert_called_once_with(
|
||||||
urllib2_urlopen = m.CreateMock(object)
|
'http://metadata.google.internal/', timeout=1)
|
||||||
urllib2_urlopen.__call__(('http://metadata.google.internal'
|
|
||||||
)).AndReturn(mockResponse)
|
|
||||||
|
|
||||||
m.ReplayAll()
|
|
||||||
|
|
||||||
self.assertEqual(DEFAULT_ENV_NAME, _get_environment(urllib2_urlopen))
|
|
||||||
|
|
||||||
m.UnsetStubs()
|
|
||||||
m.VerifyAll()
|
|
||||||
|
|
||||||
def test_get_environment_variable_file(self):
|
def test_get_environment_variable_file(self):
|
||||||
environment_variable_file = datafile(
|
environment_variable_file = datafile(
|
||||||
|
|||||||
3
tox.ini
3
tox.ini
@@ -4,6 +4,7 @@ envlist = py26,py27,py33,py34,pypy,cover
|
|||||||
[testenv]
|
[testenv]
|
||||||
basedeps = keyring
|
basedeps = keyring
|
||||||
mox3
|
mox3
|
||||||
|
mock
|
||||||
pycrypto==2.6
|
pycrypto==2.6
|
||||||
django>=1.5,<1.6
|
django>=1.5,<1.6
|
||||||
webtest
|
webtest
|
||||||
@@ -11,7 +12,7 @@ basedeps = keyring
|
|||||||
deps = {[testenv]basedeps}
|
deps = {[testenv]basedeps}
|
||||||
pyopenssl==0.14
|
pyopenssl==0.14
|
||||||
setenv = PYTHONPATH=../google_appengine
|
setenv = PYTHONPATH=../google_appengine
|
||||||
commands = nosetests --ignore-files=test_appengine\.py
|
commands = nosetests --ignore-files=test_appengine\.py {posargs}
|
||||||
|
|
||||||
# whitelist
|
# whitelist
|
||||||
branches:
|
branches:
|
||||||
|
|||||||
Reference in New Issue
Block a user